Merge branch 'http-sync'

Conflicts:
	fileserver/Makefile.am
	server/Makefile.am
This commit is contained in:
Jiaqiang Xu 2014-11-06 12:10:06 +08:00
commit 299746d77c
46 changed files with 5739 additions and 1889 deletions

View File

@ -17,14 +17,14 @@ else
endif
if COMPILE_SERVER
MAKE_SERVER = server tools fileserver $(MAKE_CONTROLLER) $(MAKE_FUSE)
MAKE_SERVER = server tools $(MAKE_CONTROLLER) $(MAKE_FUSE)
endif
SUBDIRS = include lib common app python tests doc \
$(MAKE_CLIENT) $(MAKE_SERVER)
DIST_SUBDIRS = include lib common app python tests \
daemon server tools fileserver controller \
daemon server tools controller \
doc fuse
INTLTOOL = \

View File

@ -152,7 +152,7 @@ seafile_set_upload_rate_limit (int limit, GError **error)
if (limit < 0)
limit = 0;
seaf->transfer_mgr->upload_limit = limit;
seaf->sync_mgr->upload_limit = limit;
return seafile_session_config_set_int (seaf, KEY_UPLOAD_LIMIT, limit);
}
@ -163,7 +163,7 @@ seafile_set_download_rate_limit (int limit, GError **error)
if (limit < 0)
limit = 0;
seaf->transfer_mgr->download_limit = limit;
seaf->sync_mgr->download_limit = limit;
return seafile_session_config_set_int (seaf, KEY_DOWNLOAD_LIMIT, limit);
}
@ -396,10 +396,8 @@ seafile_get_clone_tasks (GError **error)
"state", clone_task_state_to_str(task->state),
"error_str", clone_task_error_to_str(task->error),
"repo_id", task->repo_id,
"peer_id", task->peer_id,
"repo_name", task->repo_name,
"worktree", task->worktree,
"tx_id", task->tx_id,
NULL);
ret = g_list_prepend (ret, t);
}
@ -448,14 +446,10 @@ convert_task (TransferTask *task)
if (task->protocol_version < 7)
get_task_size (task, &rsize, &dsize);
g_object_set (t, "tx_id", task->tx_id,
g_object_set (t,
"repo_id", task->repo_id,
"dest_id", task->dest_id,
"from_branch", task->from_branch,
"to_branch", task->to_branch,
"state", task_state_to_str(task->state),
"rt_state", task_rt_state_to_str(task->runtime_state),
"rsize", rsize, "dsize", dsize,
"error_str", task_error_str(task->error),
NULL);
@ -485,50 +479,66 @@ convert_task (TransferTask *task)
return t;
}
static SeafileTask *
convert_http_task (HttpTxTask *task)
{
SeafileTask *t = seafile_task_new();
g_object_set (t,
"repo_id", task->repo_id,
"state", http_task_state_to_str(task->state),
"rt_state", http_task_rt_state_to_str(task->runtime_state),
"error_str", http_task_error_str(task->error),
NULL);
if (task->type == HTTP_TASK_TYPE_DOWNLOAD) {
g_object_set (t, "ttype", "download", NULL);
if (task->runtime_state == HTTP_TASK_RT_STATE_BLOCK) {
g_object_set (t, "block_total", task->n_files,
"block_done", task->done_files,
NULL);
g_object_set (t, "rate", http_tx_task_get_rate(task), NULL);
}
} else {
g_object_set (t, "ttype", "upload", NULL);
if (task->runtime_state == HTTP_TASK_RT_STATE_BLOCK) {
g_object_set (t, "block_total", task->n_blocks,
"block_done", task->done_blocks,
NULL);
g_object_set (t, "rate", http_tx_task_get_rate(task), NULL);
}
}
return t;
}
GObject *
seafile_find_transfer_task (const char *repo_id, GError *error)
{
TransferTask *task;
HttpTxTask *http_task;
task = seaf_transfer_manager_find_transfer_by_repo (
seaf->transfer_mgr, repo_id);
if (!task)
return NULL;
task = seaf_transfer_manager_find_transfer_by_repo (seaf->transfer_mgr, repo_id);
if (task)
return (GObject *)convert_task (task);
return (GObject *)convert_task (task);
http_task = http_tx_manager_find_task (seaf->http_tx_mgr, repo_id);
if (task)
return (GObject *)convert_http_task (http_task);
return NULL;
}
int
seafile_get_upload_rate(GError **error)
{
int rate = 0;
GList *ptr, *tasks = seaf_transfer_manager_get_upload_tasks(seaf->transfer_mgr);
for (ptr = tasks; ptr; ptr = ptr->next) {
TransferTask *task = (TransferTask *)ptr->data;
rate += transfer_task_get_rate (task);
}
g_list_free(tasks);
return rate;
return seaf->sync_mgr->last_sent_bytes;
}
int
seafile_get_download_rate(GError **error)
{
int rate = 0;
GList *ptr, *tasks = seaf_transfer_manager_get_download_tasks(seaf->transfer_mgr);
for (ptr = tasks; ptr; ptr = ptr->next) {
TransferTask *task = (TransferTask *)ptr->data;
rate += transfer_task_get_rate (task);
}
g_list_free(tasks);
return rate;
return seaf->sync_mgr->last_recv_bytes;
}
@ -574,9 +584,7 @@ seafile_get_repo_sync_task (const char *repo_id, GError **error)
const char *sync_state;
char allzeros[41] = {0};
if (!ccnet_peer_is_ready(seaf->ccnetrpc_client, repo->relay_id)) {
sync_state = "relay not connected";
} else if (!info->in_sync && memcmp(allzeros, info->head_commit, 41) == 0) {
if (!info->in_sync && memcmp(allzeros, info->head_commit, 41) == 0) {
sync_state = "waiting for sync";
} else {
sync_state = sync_state_to_str(task->state);
@ -588,8 +596,6 @@ seafile_get_repo_sync_task (const char *repo_id, GError **error)
"force_upload", task->is_manual_sync,
"state", sync_state,
"error", sync_error_to_str(task->error),
"tx_id", task->tx_id,
"dest_id", task->dest_id,
"repo_id", info->repo_id,
NULL);
@ -617,9 +623,7 @@ seafile_get_sync_task_list (GError **error)
"force_upload", task->is_manual_sync,
"state", sync_state_to_str(task->state),
"error", sync_error_to_str(task->error),
"dest_id", task->dest_id,
"repo_id", info->repo_id,
"tx_id", task->tx_id,
NULL);
task_list = g_list_prepend (task_list, s_task);
}

View File

@ -310,12 +310,10 @@ if test x${compile_server} = xyes; then
AC_SUBST(LIBARCHIVE_LIBS)
fi
# We use http to communicate with Riak, check for libcurl.
if test "${compile_riak}" = "yes"; then
if test "${compile_client}" = "yes"; then
PKG_CHECK_MODULES(CURL, [libcurl >= $CURL_REQUIRED])
AC_SUBST(CURL_CFLAGS)
AC_SUBST(CURL_LIBS)
AC_DEFINE([RIAK_BACKEND], [1], ["define if support Riak backend"])
fi
AM_CONDITIONAL([HAVE_KEYSTORAGE_GK], [test "${compile_gnome_keyring}" = "yes"])
@ -345,7 +343,6 @@ AC_CONFIG_FILES(
python/seafile/Makefile
python/seaserv/Makefile
controller/Makefile
fileserver/Makefile
tools/Makefile
tests/Makefile
tests/common-conf.sh

View File

@ -10,6 +10,7 @@ AM_CFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \
@SEARPC_CFLAGS@ \
@GLIB2_CFLAGS@ \
@MSVC_CFLAGS@ \
@CURL_CFLAGS@
-Wall
bin_PROGRAMS =
@ -57,6 +58,7 @@ noinst_HEADERS = \
block-tx-client.h \
seafile-config.h \
client-migrate.h \
http-tx-mgr.h \
$(proc_headers)
if LINUX
@ -72,6 +74,7 @@ wt_monitor_src = wt-monitor.c wt-monitor-macos.c wt-monitor-structs.c
endif
common_src = \
http-tx-mgr.c \
transfer-mgr.c \
../common/unpack-trees.c ../common/seaf-tree-walk.c \
merge.c merge-recursive.c vc-utils.c \
@ -124,7 +127,7 @@ seaf_daemon_LDADD = $(top_builddir)/lib/libseafile_common.la \
@GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_RT@ @LIB_UUID@ -lsqlite3 @LIBEVENT_LIBS@ \
$(top_builddir)/common/cdc/libcdc.la \
$(top_builddir)/common/index/libindex.la ${LIB_WS32} \
@SEARPC_LIBS@ @CCNET_LIBS@ @GNOME_KEYRING_LIBS@ @JANSSON_LIBS@ @LIB_MAC@ @ZLIB_LIBS@
@SEARPC_LIBS@ @CCNET_LIBS@ @GNOME_KEYRING_LIBS@ @JANSSON_LIBS@ @LIB_MAC@ @ZLIB_LIBS@ @CURL_LIBS@
seaf_daemon_LDFLAGS = @STATIC_COMPILE@ @CONSOLE@

View File

@ -574,7 +574,7 @@ send_encrypted_block (BlockTxClient *client,
/* Update global transferred bytes. */
g_atomic_int_add (&(info->task->tx_bytes), n);
g_atomic_int_add (&(seaf->transfer_mgr->sent_bytes), n);
g_atomic_int_add (&(seaf->sync_mgr->sent_bytes), n);
/* If uploaded bytes exceeds the limit, wait until the counter
* is reset. We check the counter every 100 milliseconds, so we
@ -582,9 +582,9 @@ send_encrypted_block (BlockTxClient *client,
* the counter is reset.
*/
while (1) {
gint sent = g_atomic_int_get(&(seaf->transfer_mgr->sent_bytes));
if (seaf->transfer_mgr->upload_limit > 0 &&
sent > seaf->transfer_mgr->upload_limit)
gint sent = g_atomic_int_get(&(seaf->sync_mgr->sent_bytes));
if (seaf->sync_mgr->upload_limit > 0 &&
sent > seaf->sync_mgr->upload_limit)
/* 100 milliseconds */
g_usleep (100000);
else
@ -648,12 +648,12 @@ save_block_content_cb (char *content, int clen, int end, void *cbarg)
/* Update global transferred bytes. */
g_atomic_int_add (&(task->tx_bytes), clen);
g_atomic_int_add (&(seaf->transfer_mgr->recv_bytes), clen);
g_atomic_int_add (&(seaf->sync_mgr->recv_bytes), clen);
while (1) {
gint recv_bytes = g_atomic_int_get (&(seaf->transfer_mgr->recv_bytes));
if (seaf->transfer_mgr->download_limit > 0 &&
recv_bytes > seaf->transfer_mgr->download_limit) {
gint recv_bytes = g_atomic_int_get (&(seaf->sync_mgr->recv_bytes));
if (seaf->sync_mgr->download_limit > 0 &&
recv_bytes > seaf->sync_mgr->download_limit) {
g_usleep (100000);
} else {
break;

View File

@ -12,6 +12,7 @@
#include "unpack-trees.h"
#include "vc-utils.h"
#include "utils.h"
#include "seafile-config.h"
#include "processors/checkff-proc.h"
@ -24,6 +25,11 @@ on_repo_fetched (SeafileSession *seaf,
TransferTask *tx_task,
SeafCloneManager *mgr);
static void
on_repo_http_fetched (SeafileSession *seaf,
HttpTxTask *tx_task,
SeafCloneManager *mgr);
static void
on_checkout_done (CheckoutTask *task, SeafRepo *repo, void *data);
@ -48,6 +54,7 @@ add_transfer_task (CloneTask *task, GError **error);
static const char *state_str[] = {
"init",
"connect",
"connect",
"connect", /* Use "connect" for CHECK_PROTOCOL */
"index",
"fetch",
@ -114,6 +121,9 @@ mark_clone_done_v2 (SeafRepo *repo, CloneTask *task)
seaf_repo_set_readonly (repo);
}
if (task->server_url)
repo->server_url = g_strdup(task->server_url);
if (repo->auto_sync && !task->is_readonly) {
if (seaf_wt_monitor_watch_repo (seaf->wt_monitor,
repo->id, repo->worktree) < 0) {
@ -157,6 +167,13 @@ start_clone_v2 (CloneTask *task)
seaf_repo_manager_set_repo_email (seaf->repo_mgr, repo, task->email);
seaf_repo_manager_set_repo_relay_info (seaf->repo_mgr, repo->id,
task->peer_addr, task->peer_port);
seaf_repo_manager_set_repo_relay_id (seaf->repo_mgr, repo, task->peer_id);
if (task->server_url) {
seaf_repo_manager_set_repo_property (seaf->repo_mgr,
repo->id,
REPO_PROP_SERVER_URL,
task->server_url);
}
mark_clone_done_v2 (repo, task);
return;
@ -215,6 +232,90 @@ start_check_protocol_proc (const char *peer_id, CloneTask *task)
return 0;
}
static void
start_connect_task_relay (CloneTask *task, GError **error)
{
CcnetPeer *peer = ccnet_get_peer (seaf->ccnetrpc_client, task->peer_id);
if (!peer) {
/* clone from a new relay */
GString *buf = NULL;
seaf_message ("add relay before clone, %s:%s\n",
task->peer_addr, task->peer_port);
buf = g_string_new(NULL);
g_string_append_printf (buf, "add-relay --id %s --addr %s:%s",
task->peer_id, task->peer_addr, task->peer_port);
ccnet_send_command (seaf->session, buf->str, NULL, NULL);
transition_state (task, CLONE_STATE_CONNECT);
g_string_free (buf, TRUE);
} else {
/* The peer is added to ccnet already and will be connected,
* only need to transition the state
*/
transition_state (task, CLONE_STATE_CONNECT);
}
if (peer)
g_object_unref (peer);
}
static void
connect_non_http_server (CloneTask *task)
{
if (!ccnet_peer_is_ready (seaf->ccnetrpc_client, task->peer_id))
start_connect_task_relay (task, NULL);
else
start_check_protocol_proc (task->peer_id, task);
}
static void
check_head_commit_done (HttpHeadCommit *result, void *user_data)
{
CloneTask *task = user_data;
if (result->check_success && !result->is_corrupt && !result->is_deleted) {
memcpy (task->server_head_id, result->head_commit, 40);
start_clone_v2 (task);
} else {
task->http_sync = FALSE;
connect_non_http_server (task);
}
}
static void
http_check_head_commit (CloneTask *task)
{
http_tx_manager_check_head_commit (seaf->http_tx_mgr,
task->repo_id,
task->repo_version,
task->server_url,
task->token,
check_head_commit_done,
task);
}
static void
check_http_protocol_done (HttpProtocolVersion *result, void *user_data)
{
CloneTask *task = user_data;
if (result->check_success && !result->not_supported) {
task->http_protocol_version = result->version;
task->http_sync = TRUE;
http_check_head_commit (task);
} else
connect_non_http_server (task);
}
static void
check_http_protocol (CloneTask *task)
{
http_tx_manager_check_protocol_version (seaf->http_tx_mgr,
task->server_url,
check_http_protocol_done,
task);
transition_state (task, CLONE_STATE_CHECK_HTTP);
}
static CloneTask *
clone_task_new (const char *repo_id,
const char *peer_id,
@ -255,6 +356,7 @@ clone_task_free (CloneTask *task)
g_free (task->peer_port);
g_free (task->email);
g_free (task->random_key);
g_free (task->server_url);
g_free (task);
}
@ -373,6 +475,9 @@ load_more_info_cb (sqlite3_stmt *stmt, void *data)
json_t *integer = json_object_get (object, "is_readonly");
task->is_readonly = json_integer_value (integer);
json_t *string = json_object_get (object, "server_url");
if (string)
task->server_url = g_strdup (json_string_value (string));
json_decref (object);
return FALSE;
@ -432,10 +537,10 @@ restart_task (sqlite3_stmt *stmt, void *data)
if (repo != NULL && repo->head != NULL) {
transition_state (task, CLONE_STATE_DONE);
return TRUE;
} else if (!ccnet_peer_is_ready (seaf->ccnetrpc_client, task->peer_id))
start_connect_task_relay (task, NULL);
} else if (seaf->enable_http_sync && task->repo_version > 0 && task->server_url)
check_http_protocol (task);
else
start_check_protocol_proc (task->peer_id, task);
connect_non_http_server (task);
g_hash_table_insert (mgr->tasks, g_strdup(task->repo_id), task);
@ -470,6 +575,11 @@ seaf_clone_manager_init (SeafCloneManager *mgr)
if (sqlite_query_exec (mgr->db, sql) < 0)
return -1;
sql = "CREATE TABLE IF NOT EXISTS CloneServerURL "
"(repo_id TEXT PRIMARY KEY, server_url TEXT);";
if (sqlite_query_exec (mgr->db, sql) < 0)
return -1;
return 0;
}
@ -514,6 +624,8 @@ seaf_clone_manager_start (SeafCloneManager *mgr)
g_signal_connect (seaf, "repo-fetched",
(GCallback)on_repo_fetched, mgr);
g_signal_connect (seaf, "repo-http-fetched",
(GCallback)on_repo_http_fetched, mgr);
return 0;
}
@ -564,13 +676,15 @@ save_task_to_db (SeafCloneManager *mgr, CloneTask *task)
}
sqlite3_free (sql);
if (task->is_readonly) {
if (task->is_readonly || task->server_url) {
/* need to store more info */
json_t *object = NULL;
gchar *info = NULL;
object = json_object ();
json_object_set_new (object, "is_readonly", json_integer (task->is_readonly));
if (task->server_url)
json_object_set_new (object, "server_url", json_string(task->server_url));
info = json_dumps (object, 0);
json_decref (object);
@ -655,19 +769,36 @@ transition_to_error (CloneTask *task, int error)
static int
add_transfer_task (CloneTask *task, GError **error)
{
task->tx_id = seaf_transfer_manager_add_download (seaf->transfer_mgr,
task->repo_id,
task->repo_version,
task->peer_id,
"fetch_head",
"master",
task->token,
task->server_side_merge,
task->passwd,
task->worktree,
error);
if (!task->tx_id)
return -1;
if (!task->http_sync) {
task->tx_id = seaf_transfer_manager_add_download (seaf->transfer_mgr,
task->repo_id,
task->repo_version,
task->peer_id,
"fetch_head",
"master",
task->token,
task->server_side_merge,
task->passwd,
task->worktree,
error);
if (!task->tx_id)
return -1;
} else {
int ret = http_tx_manager_add_download (seaf->http_tx_mgr,
task->repo_id,
task->repo_version,
task->server_url,
task->token,
task->server_head_id,
TRUE,
task->passwd,
task->worktree,
task->http_protocol_version,
error);
if (ret < 0)
return -1;
task->tx_id = g_strdup(task->repo_id);
}
return 0;
}
@ -765,32 +896,6 @@ start_index_or_transfer (SeafCloneManager *mgr, CloneTask *task, GError **error)
return ret;
}
static void
start_connect_task_relay (CloneTask *task, GError **error)
{
CcnetPeer *peer = ccnet_get_peer (seaf->ccnetrpc_client, task->peer_id);
if (!peer) {
/* clone from a new relay */
GString *buf = NULL;
seaf_message ("add relay before clone, %s:%s\n",
task->peer_addr, task->peer_port);
buf = g_string_new(NULL);
g_string_append_printf (buf, "add-relay --id %s --addr %s:%s",
task->peer_id, task->peer_addr, task->peer_port);
ccnet_send_command (seaf->session, buf->str, NULL, NULL);
transition_state (task, CLONE_STATE_CONNECT);
g_string_free (buf, TRUE);
} else {
/* The peer is added to ccnet already and will be connected,
* only need to transition the state
*/
transition_state (task, CLONE_STATE_CONNECT);
}
if (peer)
g_object_unref (peer);
}
static gboolean
is_duplicate_task (SeafCloneManager *mgr, const char *repo_id)
{
@ -1047,6 +1152,18 @@ seaf_clone_manager_check_worktree_path (SeafCloneManager *mgr, const char *path,
return TRUE;
}
static char *
canonical_server_url (const char *url_in)
{
char *url = g_strdup(url_in);
int len = strlen(url);
if (url[len - 1] == '/')
url[len - 1] = 0;
return url;
}
static char *
add_task_common (SeafCloneManager *mgr,
const char *repo_id,
@ -1089,7 +1206,10 @@ add_task_common (SeafCloneManager *mgr,
}
json_t *integer = json_object_get (object, "is_readonly");
task->is_readonly = json_integer_value (integer);
task->is_readonly = json_integer_value (integer);
json_t *string = json_object_get (object, "server_url");
if (string)
task->server_url = canonical_server_url (json_string_value (string));
json_decref (object);
}
@ -1099,13 +1219,10 @@ add_task_common (SeafCloneManager *mgr,
return NULL;
}
if (!ccnet_peer_is_ready(seaf->ccnetrpc_client, task->peer_id)) {
/* the relay is not connected yet.
* We need relay connected even before checkout.
*/
start_connect_task_relay (task, error);
} else
start_check_protocol_proc (task->peer_id, task);
if (seaf->enable_http_sync && task->repo_version > 0 && task->server_url)
check_http_protocol (task);
else
connect_non_http_server (task);
/* The old task for this repo will be freed. */
g_hash_table_insert (mgr->tasks, g_strdup(task->repo_id), task);
@ -1373,9 +1490,14 @@ seaf_clone_manager_cancel_task (SeafCloneManager *mgr,
transition_state (task, CLONE_STATE_CANCELED);
break;
case CLONE_STATE_FETCH:
seaf_transfer_manager_cancel_task (seaf->transfer_mgr,
task->tx_id,
TASK_TYPE_DOWNLOAD);
if (!task->http_sync)
seaf_transfer_manager_cancel_task (seaf->transfer_mgr,
task->tx_id,
TASK_TYPE_DOWNLOAD);
else
http_tx_manager_cancel_task (seaf->http_tx_mgr,
task->repo_id,
HTTP_TASK_TYPE_DOWNLOAD);
transition_state (task, CLONE_STATE_CANCEL_PENDING);
break;
case CLONE_STATE_INDEX:
@ -2043,6 +2165,13 @@ on_repo_fetched (SeafileSession *seaf,
seaf_repo_manager_set_repo_email (seaf->repo_mgr, repo, task->email);
seaf_repo_manager_set_repo_relay_info (seaf->repo_mgr, repo->id,
task->peer_addr, task->peer_port);
seaf_repo_manager_set_repo_relay_id (seaf->repo_mgr, repo, task->peer_id);
if (task->server_url) {
seaf_repo_manager_set_repo_property (seaf->repo_mgr,
repo->id,
REPO_PROP_SERVER_URL,
task->server_url);
}
if (!task->server_side_merge)
start_checkout (repo, task);
@ -2050,6 +2179,53 @@ on_repo_fetched (SeafileSession *seaf,
mark_clone_done_v2 (repo, task);
}
static void
on_repo_http_fetched (SeafileSession *seaf,
HttpTxTask *tx_task,
SeafCloneManager *mgr)
{
CloneTask *task;
/* Only handle clone task. */
if (!tx_task->is_clone)
return;
task = g_hash_table_lookup (mgr->tasks, tx_task->repo_id);
g_return_if_fail (task != NULL);
if (tx_task->state == HTTP_TASK_STATE_CANCELED) {
/* g_assert (task->state == CLONE_STATE_CANCEL_PENDING); */
transition_state (task, CLONE_STATE_CANCELED);
return;
} else if (tx_task->state == HTTP_TASK_STATE_ERROR) {
transition_to_error (task, CLONE_ERROR_FETCH);
return;
}
SeafRepo *repo = seaf_repo_manager_get_repo (seaf->repo_mgr,
tx_task->repo_id);
if (repo == NULL) {
seaf_warning ("[Clone mgr] cannot find repo %s after fetched.\n",
tx_task->repo_id);
transition_to_error (task, CLONE_ERROR_INTERNAL);
return;
}
seaf_repo_manager_set_repo_token (seaf->repo_mgr, repo, task->token);
seaf_repo_manager_set_repo_email (seaf->repo_mgr, repo, task->email);
seaf_repo_manager_set_repo_relay_info (seaf->repo_mgr, repo->id,
task->peer_addr, task->peer_port);
seaf_repo_manager_set_repo_relay_id (seaf->repo_mgr, repo, task->peer_id);
if (task->server_url) {
seaf_repo_manager_set_repo_property (seaf->repo_mgr,
repo->id,
REPO_PROP_SERVER_URL,
task->server_url);
}
mark_clone_done_v2 (repo, task);
}
static void
on_checkout_done (CheckoutTask *ctask, SeafRepo *repo, void *data)
{

View File

@ -13,6 +13,7 @@ typedef struct _SeafCloneManager SeafCloneManager;
enum {
CLONE_STATE_INIT,
CLONE_STATE_CHECK_HTTP,
CLONE_STATE_CONNECT,
CLONE_STATE_CHECK_PROTOCOL,
CLONE_STATE_INDEX,
@ -58,6 +59,12 @@ struct _CloneTask {
char root_id[41];
gboolean is_readonly;
/* Http sync fields */
char *server_url;
int http_protocol_version;
gboolean http_sync;
char server_head_id[41];
gboolean server_side_merge;
};

2775
daemon/http-tx-mgr.c Normal file

File diff suppressed because it is too large Load Diff

189
daemon/http-tx-mgr.h Normal file
View File

@ -0,0 +1,189 @@
#ifndef HTTP_TX_MGR_H
#define HTTP_TX_MGR_H
enum {
HTTP_TASK_TYPE_DOWNLOAD = 0,
HTTP_TASK_TYPE_UPLOAD,
};
/**
* The state that can be set by user.
*
* A task in NORMAL state can be canceled;
* A task in RT_STATE_FINISHED can be removed.
*/
enum HttpTaskState {
HTTP_TASK_STATE_NORMAL = 0,
HTTP_TASK_STATE_CANCELED,
HTTP_TASK_STATE_FINISHED,
HTTP_TASK_STATE_ERROR,
N_HTTP_TASK_STATE,
};
enum HttpTaskRuntimeState {
HTTP_TASK_RT_STATE_INIT = 0,
HTTP_TASK_RT_STATE_CHECK,
HTTP_TASK_RT_STATE_COMMIT,
HTTP_TASK_RT_STATE_FS,
HTTP_TASK_RT_STATE_BLOCK, /* Only used in upload. */
HTTP_TASK_RT_STATE_UPDATE_BRANCH, /* Only used in upload. */
HTTP_TASK_RT_STATE_FINISHED,
N_HTTP_TASK_RT_STATE,
};
enum HttpTaskError {
HTTP_TASK_OK = 0,
HTTP_TASK_ERR_FORBIDDEN,
HTTP_TASK_ERR_NET,
HTTP_TASK_ERR_SERVER,
HTTP_TASK_ERR_BAD_REQUEST,
HTTP_TASK_ERR_BAD_LOCAL_DATA,
HTTP_TASK_ERR_NOT_ENOUGH_MEMORY,
HTTP_TASK_ERR_WRITE_LOCAL_DATA,
HTTP_TASK_ERR_NO_QUOTA,
HTTP_TASK_ERR_UNKNOWN,
N_HTTP_TASK_ERROR,
};
struct _SeafileSession;
struct _HttpTxPriv;
struct _HttpTxManager {
struct _SeafileSession *seaf;
struct _HttpTxPriv *priv;
};
typedef struct _HttpTxManager HttpTxManager;
struct _HttpTxTask {
HttpTxManager *manager;
char repo_id[37];
int repo_version;
char *token;
int protocol_version;
int type;
char *host;
gboolean is_clone;
char head[41];
char *passwd;
char *worktree;
int state;
int runtime_state;
int error;
/* For upload progress */
int n_blocks;
int done_blocks;
/* For download progress */
int n_files;
int done_files;
gint tx_bytes; /* bytes transferred in this second. */
gint last_tx_bytes; /* bytes transferred in the last second. */
};
typedef struct _HttpTxTask HttpTxTask;
HttpTxManager *
http_tx_manager_new (struct _SeafileSession *seaf);
int
http_tx_manager_start (HttpTxManager *mgr);
int
http_tx_manager_add_download (HttpTxManager *manager,
const char *repo_id,
int repo_version,
const char *host,
const char *token,
const char *server_head_id,
gboolean is_clone,
const char *passwd,
const char *worktree,
int protocol_version,
GError **error);
int
http_tx_manager_add_upload (HttpTxManager *manager,
const char *repo_id,
int repo_version,
const char *host,
const char *token,
int protocol_version,
GError **error);
struct _HttpProtocolVersion {
gboolean check_success; /* TRUE if we get response from the server. */
gboolean not_supported;
int version;
};
typedef struct _HttpProtocolVersion HttpProtocolVersion;
typedef void (*HttpProtocolVersionCallback) (HttpProtocolVersion *result,
void *user_data);
/* Asynchronous interface for getting protocol version from a server.
* Also used to determine if the server support http sync.
*/
int
http_tx_manager_check_protocol_version (HttpTxManager *manager,
const char *host,
HttpProtocolVersionCallback callback,
void *user_data);
struct _HttpHeadCommit {
gboolean check_success;
gboolean is_corrupt;
gboolean is_deleted;
char head_commit[41];
};
typedef struct _HttpHeadCommit HttpHeadCommit;
typedef void (*HttpHeadCommitCallback) (HttpHeadCommit *result,
void *user_data);
/* Asynchronous interface for getting head commit info from a server. */
int
http_tx_manager_check_head_commit (HttpTxManager *manager,
const char *repo_id,
int repo_version,
const char *host,
const char *token,
HttpHeadCommitCallback callback,
void *user_data);
int
http_tx_task_download_file_blocks (HttpTxTask *task, const char *file_id);
GList*
http_tx_manager_get_upload_tasks (HttpTxManager *manager);
GList*
http_tx_manager_get_download_tasks (HttpTxManager *manager);
HttpTxTask *
http_tx_manager_find_task (HttpTxManager *manager, const char *repo_id);
void
http_tx_manager_cancel_task (HttpTxManager *manager,
const char *repo_id,
int task_type);
int
http_tx_task_get_rate (HttpTxTask *task);
const char *
http_task_state_to_str (int state);
const char *
http_task_rt_state_to_str (int rt_state);
const char *
http_task_error_str (int task_errno);
#endif

View File

@ -1743,6 +1743,8 @@ checkout_file (const char *repo_id,
SeafileCrypt *crypt,
struct cache_entry *ce,
TransferTask *task,
HttpTxTask *http_task,
gboolean is_http,
const char *conflict_head_id,
GHashTable *conflict_hash,
GHashTable *no_conflict_hash)
@ -1813,20 +1815,33 @@ checkout_file (const char *repo_id,
}
/* Download the blocks of this file. */
int rc = seaf_transfer_manager_download_file_blocks (seaf->transfer_mgr,
int rc;
if (!is_http) {
rc = seaf_transfer_manager_download_file_blocks (seaf->transfer_mgr,
task, file_id);
switch (rc) {
case BLOCK_CLIENT_SUCCESS:
break;
case BLOCK_CLIENT_UNKNOWN:
case BLOCK_CLIENT_FAILED:
case BLOCK_CLIENT_NET_ERROR:
case BLOCK_CLIENT_SERVER_ERROR:
g_free (path);
return FETCH_CHECKOUT_TRANSFER_ERROR;
case BLOCK_CLIENT_CANCELED:
g_free (path);
return FETCH_CHECKOUT_CANCELED;
switch (rc) {
case BLOCK_CLIENT_SUCCESS:
break;
case BLOCK_CLIENT_UNKNOWN:
case BLOCK_CLIENT_FAILED:
case BLOCK_CLIENT_NET_ERROR:
case BLOCK_CLIENT_SERVER_ERROR:
g_free (path);
return FETCH_CHECKOUT_TRANSFER_ERROR;
case BLOCK_CLIENT_CANCELED:
g_free (path);
return FETCH_CHECKOUT_CANCELED;
}
} else {
rc = http_tx_task_download_file_blocks (http_task, file_id);
if (http_task->state == HTTP_TASK_STATE_CANCELED) {
g_free (path);
return FETCH_CHECKOUT_CANCELED;
}
if (rc < 0) {
g_free (path);
return FETCH_CHECKOUT_TRANSFER_ERROR;
}
}
/* The worktree file may have been changed when we're downloading the blocks. */
@ -1978,8 +1993,16 @@ cleanup_file_blocks (const char *repo_id, int version, const char *file_id)
int
seaf_repo_fetch_and_checkout (TransferTask *task,
HttpTxTask *http_task,
gboolean is_http,
const char *remote_head_id)
{
char *repo_id;
int repo_version;
gboolean is_clone;
char *worktree;
char *passwd;
SeafRepo *repo = NULL;
SeafBranch *master = NULL;
SeafCommit *remote_head = NULL, *master_head = NULL;
@ -1991,64 +2014,75 @@ seaf_repo_fetch_and_checkout (TransferTask *task,
GHashTable *conflict_hash = NULL, *no_conflict_hash = NULL;
GList *ignore_list = NULL;
if (is_http) {
repo_id = http_task->repo_id;
repo_version = http_task->repo_version;
is_clone = http_task->is_clone;
worktree = http_task->worktree;
passwd = http_task->passwd;
} else {
repo_id = task->repo_id;
repo_version = task->repo_version;
is_clone = task->is_clone;
worktree = task->worktree;
passwd = task->passwd;
}
memset (&istate, 0, sizeof(istate));
snprintf (index_path, SEAF_PATH_MAX, "%s/%s",
seaf->repo_mgr->index_dir, task->repo_id);
if (read_index_from (&istate, index_path, task->repo_version) < 0) {
seaf->repo_mgr->index_dir, repo_id);
if (read_index_from (&istate, index_path, repo_version) < 0) {
g_warning ("Failed to load index.\n");
return FETCH_CHECKOUT_FAILED;
}
if (!task->is_clone) {
repo = seaf_repo_manager_get_repo (seaf->repo_mgr, task->repo_id);
if (!is_clone) {
repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
if (!repo) {
seaf_warning ("Failed to get repo %.8s.\n", task->repo_id);
seaf_warning ("Failed to get repo %.8s.\n", repo_id);
goto out;
}
master = seaf_branch_manager_get_branch (seaf->branch_mgr,
task->repo_id, "master");
repo_id, "master");
if (!master) {
seaf_warning ("Failed to get master branch for repo %.8s.\n",
task->repo_id);
repo_id);
ret = FETCH_CHECKOUT_FAILED;
goto out;
}
master_head = seaf_commit_manager_get_commit (seaf->commit_mgr,
task->repo_id,
task->repo_version,
repo_id,
repo_version,
master->commit_id);
if (!master_head) {
seaf_warning ("Failed to get master head %s of repo %.8s.\n",
task->repo_id, master->commit_id);
repo_id, master->commit_id);
ret = FETCH_CHECKOUT_FAILED;
goto out;
}
}
char *worktree;
if (!task->is_clone)
if (!is_clone)
worktree = repo->worktree;
else
worktree = task->worktree;
remote_head = seaf_commit_manager_get_commit (seaf->commit_mgr,
task->repo_id,
task->repo_version,
repo_id,
repo_version,
remote_head_id);
if (!remote_head) {
seaf_warning ("Failed to get remote head %s of repo %.8s.\n",
task->repo_id, remote_head_id);
repo_id, remote_head_id);
ret = FETCH_CHECKOUT_FAILED;
goto out;
}
if (diff_commit_roots (task->repo_id, task->repo_version,
if (diff_commit_roots (repo_id, repo_version,
master_head ? master_head->root_id : EMPTY_SHA1,
remote_head->root_id,
&results, FALSE) < 0) {
seaf_warning ("Failed to diff for repo %.8s.\n", task->repo_id);
seaf_warning ("Failed to diff for repo %.8s.\n", repo_id);
ret = FETCH_CHECKOUT_FAILED;
goto out;
}
@ -2069,14 +2103,14 @@ seaf_repo_fetch_and_checkout (TransferTask *task,
#endif
if (remote_head->encrypted) {
if (!task->is_clone) {
if (!is_clone) {
crypt = seafile_crypt_new (repo->enc_version,
repo->enc_key,
repo->enc_iv);
} else {
unsigned char enc_key[32], enc_iv[16];
seafile_decrypt_repo_enc_key (remote_head->enc_version,
task->passwd,
passwd,
remote_head->random_key,
enc_key, enc_iv);
crypt = seafile_crypt_new (remote_head->enc_version,
@ -2095,8 +2129,12 @@ seaf_repo_fetch_and_checkout (TransferTask *task,
for (ptr = results; ptr; ptr = ptr->next) {
de = ptr->data;
if (de->status == DIFF_STATUS_ADDED || de->status == DIFF_STATUS_MODIFIED)
++(task->n_to_download);
if (de->status == DIFF_STATUS_ADDED || de->status == DIFF_STATUS_MODIFIED) {
if (!is_http)
++(task->n_to_download);
else
++(http_task->n_files);
}
}
/* Delete/rename files before deleting dirs,
@ -2188,8 +2226,8 @@ seaf_repo_fetch_and_checkout (TransferTask *task,
add_ce = TRUE;
}
int rc = checkout_file (task->repo_id,
task->repo_version,
int rc = checkout_file (repo_id,
repo_version,
worktree,
de->name,
file_id,
@ -2198,6 +2236,8 @@ seaf_repo_fetch_and_checkout (TransferTask *task,
crypt,
ce,
task,
http_task,
is_http,
remote_head_id,
conflict_hash,
no_conflict_hash);
@ -2218,9 +2258,12 @@ seaf_repo_fetch_and_checkout (TransferTask *task,
goto out;
}
cleanup_file_blocks (task->repo_id, task->repo_version, file_id);
cleanup_file_blocks (repo_id, repo_version, file_id);
++(task->n_downloaded);
if (!is_http)
++(task->n_downloaded);
else
++(http_task->done_files);
if (add_ce) {
if (!(ce->ce_flags & CE_REMOVE))
@ -3080,7 +3123,10 @@ load_repo (SeafRepoManager *manager, const char *repo_id)
repo->email = load_repo_property (manager, repo->id, REPO_PROP_EMAIL);
repo->token = load_repo_property (manager, repo->id, REPO_PROP_TOKEN);
/* May be NULL if this property is not set in db. */
repo->server_url = load_repo_property (manager, repo->id, REPO_PROP_SERVER_URL);
if (repo->head != NULL && seaf_repo_check_worktree (repo) < 0) {
if (seafile_session_config_get_allow_invalid_worktree(seaf)) {
seaf_warning ("Worktree for repo \"%s\" is invalid, but still keep it.\n",
@ -3279,6 +3325,17 @@ seaf_repo_manager_set_repo_relay_id (SeafRepoManager *mgr,
return 0;
}
static char *
canonical_server_url (const char *url_in)
{
char *url = g_strdup(url_in);
int len = strlen(url);
if (url[len - 1] == '/')
url[len - 1] = 0;
return url;
}
int
seaf_repo_manager_set_repo_property (SeafRepoManager *manager,
@ -3322,6 +3379,13 @@ seaf_repo_manager_set_repo_property (SeafRepoManager *manager,
if (strcmp(key, REPO_RELAY_ID) == 0)
return seaf_repo_manager_set_repo_relay_id (manager, repo, value);
if (strcmp (key, REPO_PROP_SERVER_URL) == 0) {
char *url = canonical_server_url (value);
repo->server_url = url;
save_repo_property (manager, repo_id, key, url);
return 0;
}
save_repo_property (manager, repo_id, key, value);
return 0;
}

View File

@ -28,6 +28,7 @@
#define REPO_ENCRYPTED 0x1
#define REPO_PROP_DOWNLOAD_HEAD "download-head"
#define REPO_PROP_IS_READONLY "is-readonly"
#define REPO_PROP_SERVER_URL "server-url"
struct _SeafRepoManager;
typedef struct _SeafRepo SeafRepo;
@ -79,6 +80,9 @@ struct _SeafRepo {
int version;
gboolean create_partial_commit;
/* Used for http sync. */
char *server_url;
};
@ -410,9 +414,12 @@ enum {
};
struct _TransferTask;
struct _HttpTxTask;
int
seaf_repo_fetch_and_checkout (struct _TransferTask *task,
struct _HttpTxTask *http_task,
gboolean is_http,
const char *remote_head_id);
#endif

View File

@ -202,7 +202,7 @@ start_rpc_service (CcnetClient *client)
seafile_download,
"seafile_download",
searpc_signature_string__string_int_string_string_string_string_string_string_string_string_string_string_int_string());
searpc_server_register_function ("seafile-rpcserver",
seafile_cancel_clone_task,
"seafile_cancel_clone_task",

View File

@ -96,6 +96,20 @@ seafile_session_config_set_string (SeafileSession *session,
session->sync_extra_temp_file = FALSE;
}
if (g_strcmp0(key, KEY_ENABLE_HTTP_SYNC) == 0) {
if (g_strcmp0(value, "true") == 0)
session->enable_http_sync = TRUE;
else
session->enable_http_sync = FALSE;
}
if (g_strcmp0(key, KEY_DISABLE_VERIFY_CERTIFICATE) == 0) {
if (g_strcmp0(value, "true") == 0)
session->disable_verify_certificate = TRUE;
else
session->disable_verify_certificate = FALSE;
}
return 0;
}

View File

@ -17,6 +17,8 @@
#define KEY_ALLOW_INVALID_WORKTREE "allow_invalid_worktree"
#define KEY_ALLOW_REPO_NOT_FOUND_ON_SERVER "allow_repo_not_found_on_server"
#define KEY_SYNC_EXTRA_TEMP_FILE "sync_extra_temp_file"
#define KEY_ENABLE_HTTP_SYNC "enable_http_sync"
#define KEY_DISABLE_VERIFY_CERTIFICATE "disable_verify_certificate"
/*
* Returns: config value in string. The string should be freed by caller.

View File

@ -35,6 +35,8 @@ enum {
REPO_COMMITTED,
REPO_FETCHED,
REPO_UPLOADED,
REPO_HTTP_FETCHED,
REPO_HTTP_UPLOADED,
REPO_WORKTREE_CHECKED,
LAST_SIGNAL
};
@ -71,7 +73,21 @@ seafile_session_class_init (SeafileSessionClass *klass)
NULL, NULL, /* no accumulator */
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
signals[REPO_HTTP_FETCHED] =
g_signal_new ("repo-http-fetched", SEAFILE_TYPE_SESSION,
G_SIGNAL_RUN_LAST,
0, /* no class singal handler */
NULL, NULL, /* no accumulator */
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
signals[REPO_HTTP_UPLOADED] =
g_signal_new ("repo-http-uploaded", SEAFILE_TYPE_SESSION,
G_SIGNAL_RUN_LAST,
0, /* no class singal handler */
NULL, NULL, /* no accumulator */
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
}
@ -150,13 +166,15 @@ seafile_session_new(const char *seafile_dir,
session->clone_mgr = seaf_clone_manager_new (session);
if (!session->clone_mgr)
goto onerror;
#ifndef SEAF_TOOL
session->sync_mgr = seaf_sync_manager_new (session);
if (!session->sync_mgr)
goto onerror;
session->wt_monitor = seaf_wt_monitor_new (session);
if (!session->wt_monitor)
goto onerror;
session->http_tx_mgr = http_tx_manager_new (session);
if (!session->http_tx_mgr)
goto onerror;
session->job_mgr = ccnet_job_manager_new (MAX_THREADS);
session->ev_mgr = cevent_manager_new ();
@ -166,7 +184,6 @@ seafile_session_new(const char *seafile_dir,
session->mq_mgr = seaf_mq_manager_new (session);
if (!session->mq_mgr)
goto onerror;
#endif
return session;
@ -192,6 +209,12 @@ seafile_session_prepare (SeafileSession *session)
session->sync_extra_temp_file = seafile_session_config_get_bool
(session, KEY_SYNC_EXTRA_TEMP_FILE);
session->enable_http_sync = seafile_session_config_get_bool
(session, KEY_ENABLE_HTTP_SYNC);
session->disable_verify_certificate = seafile_session_config_get_bool
(session, KEY_DISABLE_VERIFY_CERTIFICATE);
/* Start mq manager earlier, so that we can send notifications
* when start repo manager. */
seaf_mq_manager_init (session->mq_mgr);
@ -313,6 +336,11 @@ cleanup_job_done (void *vdata)
return;
}
if (http_tx_manager_start (session->http_tx_mgr) < 0) {
g_error ("Failed to start http transfer manager.\n");
return;
}
if (seaf_sync_manager_start (session->sync_mgr) < 0) {
g_error ("Failed to start sync manager.\n");
return;

View File

@ -20,6 +20,9 @@
#include "sync-mgr.h"
#include "wt-monitor.h"
#include "mq-mgr.h"
#include "http-tx-mgr.h"
#include <searpc-client.h>
struct _CcnetClient;
@ -64,10 +67,14 @@ struct _SeafileSession {
CEventManager *ev_mgr;
CcnetJobManager *job_mgr;
HttpTxManager *http_tx_mgr;
/* Set after all components are up and running. */
gboolean started;
gboolean sync_extra_temp_file;
gboolean enable_http_sync;
gboolean disable_verify_certificate;
};
struct _SeafileSessionClass

View File

@ -24,10 +24,31 @@
#define DEFAULT_SYNC_INTERVAL 30 /* 30s */
#define CHECK_SYNC_INTERVAL 1000 /* 1s */
#define UPDATE_TX_STATE_INTERVAL 1000 /* 1s */
#define MAX_RUNNING_SYNC_TASKS 5
enum {
SERVER_SIDE_MERGE_UNKNOWN = 0,
SERVER_SIDE_MERGE_SUPPORTED,
SERVER_SIDE_MERGE_UNSUPPORTED,
};
struct _ServerState {
int server_side_merge;
gboolean checking;
};
typedef struct _ServerState ServerState;
struct _HttpServerState {
gboolean http_not_supported;
int http_version;
gboolean checking;
};
typedef struct _HttpServerState HttpServerState;
struct _SeafSyncManagerPriv {
struct CcnetTimer *check_sync_timer;
struct CcnetTimer *update_tx_state_timer;
int pulse_count;
/* When FALSE, auto sync is globally disabled */
@ -40,12 +61,20 @@ start_sync (SeafSyncManager *manager, SeafRepo *repo,
gboolean is_initial_commit);
static int auto_sync_pulse (void *vmanager);
static void on_repo_fetched (SeafileSession *seaf,
TransferTask *tx_task,
SeafSyncManager *manager);
static void on_repo_uploaded (SeafileSession *seaf,
TransferTask *tx_task,
SeafSyncManager *manager);
static void on_repo_http_fetched (SeafileSession *seaf,
HttpTxTask *tx_task,
SeafSyncManager *manager);
static void on_repo_http_uploaded (SeafileSession *seaf,
HttpTxTask *tx_task,
SeafSyncManager *manager);
static inline void
transition_sync_state (SyncTask *task, int new_state);
@ -74,6 +103,22 @@ seaf_sync_manager_new (SeafileSession *seaf)
mgr->server_states = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_free);
mgr->http_server_states = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_free);
gboolean exists;
int download_limit = seafile_session_config_get_int (seaf,
KEY_DOWNLOAD_LIMIT,
&exists);
if (exists)
mgr->download_limit = download_limit;
int upload_limit = seafile_session_config_get_int (seaf,
KEY_UPLOAD_LIMIT,
&exists);
if (exists)
mgr->upload_limit = upload_limit;
return mgr;
}
@ -171,13 +216,136 @@ add_repo_relays ()
g_list_free (repo_list);
}
static void
format_transfer_task_detail (TransferTask *task, GString *buf)
{
if (task->state != TASK_STATE_NORMAL ||
task->runtime_state == TASK_RT_STATE_INIT ||
task->runtime_state == TASK_RT_STATE_FINISHED ||
task->runtime_state == TASK_RT_STATE_NETDOWN)
return;
SeafRepo *repo = seaf_repo_manager_get_repo (seaf->repo_mgr,
task->repo_id);
char *repo_name;
char *type;
if (repo) {
repo_name = repo->name;
type = (task->type == TASK_TYPE_UPLOAD) ? "upload" : "download";
} else if (task->is_clone) {
CloneTask *ctask;
ctask = seaf_clone_manager_get_task (seaf->clone_mgr, task->repo_id);
repo_name = ctask->repo_name;
type = "download";
} else {
return;
}
int rate = transfer_task_get_rate(task);
g_string_append_printf (buf, "%s\t%d %s\n", type, rate, repo_name);
}
static void
format_http_task_detail (HttpTxTask *task, GString *buf)
{
if (task->state != HTTP_TASK_STATE_NORMAL ||
task->runtime_state == HTTP_TASK_RT_STATE_INIT ||
task->runtime_state == HTTP_TASK_RT_STATE_FINISHED)
return;
SeafRepo *repo = seaf_repo_manager_get_repo (seaf->repo_mgr,
task->repo_id);
char *repo_name;
char *type;
if (repo) {
repo_name = repo->name;
type = (task->type == HTTP_TASK_TYPE_UPLOAD) ? "upload" : "download";
} else if (task->is_clone) {
CloneTask *ctask;
ctask = seaf_clone_manager_get_task (seaf->clone_mgr, task->repo_id);
repo_name = ctask->repo_name;
type = "download";
} else {
return;
}
int rate = http_tx_task_get_rate(task);
g_string_append_printf (buf, "%s\t%d %s\n", type, rate, repo_name);
}
/*
* Publish a notification message to report :
*
* [uploading/downloading]\t[transfer-rate] [repo-name]\n
*/
static int
update_tx_state (void *vmanager)
{
SeafSyncManager *mgr = vmanager;
GString *buf = g_string_new (NULL);
GList *tasks, *ptr;
TransferTask *task;
HttpTxTask *http_task;
mgr->last_sent_bytes = g_atomic_int_get (&mgr->sent_bytes);
g_atomic_int_set (&mgr->sent_bytes, 0);
mgr->last_recv_bytes = g_atomic_int_get (&mgr->recv_bytes);
g_atomic_int_set (&mgr->recv_bytes, 0);
tasks = seaf_transfer_manager_get_upload_tasks (seaf->transfer_mgr);
for (ptr = tasks; ptr; ptr = ptr->next) {
task = ptr->data;
format_transfer_task_detail (task, buf);
}
g_list_free (tasks);
tasks = seaf_transfer_manager_get_download_tasks (seaf->transfer_mgr);
for (ptr = tasks; ptr; ptr = ptr->next) {
task = ptr->data;
format_transfer_task_detail (task, buf);
}
g_list_free (tasks);
tasks = http_tx_manager_get_upload_tasks (seaf->http_tx_mgr);
for (ptr = tasks; ptr; ptr = ptr->next) {
http_task = ptr->data;
format_http_task_detail (http_task, buf);
}
g_list_free (tasks);
tasks = http_tx_manager_get_download_tasks (seaf->http_tx_mgr);
for (ptr = tasks; ptr; ptr = ptr->next) {
http_task = ptr->data;
format_http_task_detail (http_task, buf);
}
g_list_free (tasks);
if (buf->len != 0)
seaf_mq_manager_publish_notification (seaf->mq_mgr, "transfer",
buf->str);
g_string_free (buf, TRUE);
return TRUE;
}
int
seaf_sync_manager_start (SeafSyncManager *mgr)
{
add_repo_relays ();
mgr->priv->check_sync_timer = ccnet_timer_new (
auto_sync_pulse, mgr, CHECK_SYNC_INTERVAL);
mgr->priv->update_tx_state_timer = ccnet_timer_new (
update_tx_state, mgr, UPDATE_TX_STATE_INTERVAL);
ccnet_proc_factory_register_processor (mgr->seaf->session->proc_factory,
"seafile-sync-repo",
SEAFILE_TYPE_SYNC_REPO_PROC);
@ -191,6 +359,10 @@ seaf_sync_manager_start (SeafSyncManager *mgr)
(GCallback)on_repo_fetched, mgr);
g_signal_connect (seaf, "repo-uploaded",
(GCallback)on_repo_uploaded, mgr);
g_signal_connect (seaf, "repo-http-fetched",
(GCallback)on_repo_http_fetched, mgr);
g_signal_connect (seaf, "repo-http-uploaded",
(GCallback)on_repo_http_uploaded, mgr);
return 0;
}
@ -269,15 +441,25 @@ seaf_sync_manager_cancel_sync_task (SeafSyncManager *mgr,
switch (task->state) {
case SYNC_STATE_FETCH:
seaf_transfer_manager_cancel_task (seaf->transfer_mgr,
task->tx_id,
TASK_TYPE_DOWNLOAD);
if (!task->http_sync)
seaf_transfer_manager_cancel_task (seaf->transfer_mgr,
task->tx_id,
TASK_TYPE_DOWNLOAD);
else
http_tx_manager_cancel_task (seaf->http_tx_mgr,
repo_id,
HTTP_TASK_TYPE_DOWNLOAD);
transition_sync_state (task, SYNC_STATE_CANCEL_PENDING);
break;
case SYNC_STATE_UPLOAD:
seaf_transfer_manager_cancel_task (seaf->transfer_mgr,
task->tx_id,
TASK_TYPE_UPLOAD);
if (!task->http_sync)
seaf_transfer_manager_cancel_task (seaf->transfer_mgr,
task->tx_id,
TASK_TYPE_UPLOAD);
else
http_tx_manager_cancel_task (seaf->http_tx_mgr,
repo_id,
HTTP_TASK_TYPE_UPLOAD);
transition_sync_state (task, SYNC_STATE_CANCEL_PENDING);
break;
case SYNC_STATE_COMMIT:
@ -424,6 +606,7 @@ static const char *sync_error_str[] = {
"Conflict in merge.",
"Files changed in local folder, skip merge.",
"Server version is too old.",
"Failed to get sync info from server.",
"Unknown error.",
};
@ -472,52 +655,89 @@ static void
start_upload_if_necessary (SyncTask *task)
{
GError *error = NULL;
SeafRepo *repo = task->repo;
const char *repo_id = task->repo->id;
char *tx_id = seaf_transfer_manager_add_upload (seaf->transfer_mgr,
repo_id,
task->repo->version,
task->dest_id,
"local",
"master",
task->token,
task->server_side_merge,
&error);
if (error != NULL) {
seaf_warning ("Failed to start upload: %s\n", error->message);
seaf_sync_manager_set_task_error (task, SYNC_ERROR_START_UPLOAD);
return;
if (!task->http_sync) {
char *tx_id = seaf_transfer_manager_add_upload (seaf->transfer_mgr,
repo_id,
task->repo->version,
task->dest_id,
"local",
"master",
task->token,
task->server_side_merge,
&error);
if (error != NULL) {
seaf_warning ("Failed to start upload: %s\n", error->message);
seaf_sync_manager_set_task_error (task, SYNC_ERROR_START_UPLOAD);
return;
}
task->tx_id = tx_id;
} else {
if (http_tx_manager_add_upload (seaf->http_tx_mgr,
repo->id,
repo->version,
repo->server_url,
repo->token,
task->http_version,
&error) < 0) {
seaf_warning ("Failed to start http upload: %s\n", error->message);
seaf_sync_manager_set_task_error (task, SYNC_ERROR_START_UPLOAD);
return;
}
task->tx_id = g_strdup(repo->id);
}
task->tx_id = tx_id;
transition_sync_state (task, SYNC_STATE_UPLOAD);
}
static void
start_fetch_if_necessary (SyncTask *task)
start_fetch_if_necessary (SyncTask *task, const char *remote_head)
{
GError *error = NULL;
char *tx_id;
SeafRepo *repo = task->repo;
const char *repo_id = task->repo->id;
tx_id = seaf_transfer_manager_add_download (seaf->transfer_mgr,
repo_id,
task->repo->version,
task->dest_id,
"fetch_head",
"master",
task->token,
task->server_side_merge,
NULL,
NULL,
&error);
if (!task->http_sync) {
tx_id = seaf_transfer_manager_add_download (seaf->transfer_mgr,
repo_id,
task->repo->version,
task->dest_id,
"fetch_head",
"master",
task->token,
task->server_side_merge,
NULL,
NULL,
&error);
if (error != NULL) {
seaf_warning ("[sync-mgr] Failed to start download: %s\n",
error->message);
seaf_sync_manager_set_task_error (task, SYNC_ERROR_START_FETCH);
return;
if (error != NULL) {
seaf_warning ("[sync-mgr] Failed to start download: %s\n",
error->message);
seaf_sync_manager_set_task_error (task, SYNC_ERROR_START_FETCH);
return;
}
task->tx_id = tx_id;
} else {
if (http_tx_manager_add_download (seaf->http_tx_mgr,
repo->id,
repo->version,
repo->server_url,
repo->token,
remote_head,
FALSE,
NULL, NULL,
task->http_version,
&error) < 0) {
seaf_warning ("Failed to start http download: %s.\n", error->message);
seaf_sync_manager_set_task_error (task, SYNC_ERROR_START_FETCH);
return;
}
task->tx_id = g_strdup(repo->id);
}
task->tx_id = tx_id;
transition_sync_state (task, SYNC_STATE_FETCH);
}
@ -778,7 +998,7 @@ getca_done_cb (CcnetProcessor *processor, gboolean success, void *data)
"master");
if (!master || strcmp (info->head_commit, master->commit_id) != 0) {
start_fetch_if_necessary (task);
start_fetch_if_necessary (task, NULL);
} else if (strcmp (repo->head->commit_id, master->commit_id) != 0) {
/* Try to merge even if we don't need to fetch. */
merge_branches_if_necessary (task);
@ -1004,7 +1224,7 @@ update_sync_status (SyncTask *task)
goto out;
if (!master || strcmp (info->head_commit, master->commit_id) != 0) {
start_fetch_if_necessary (task);
start_fetch_if_necessary (task, NULL);
} else if (strcmp (local->commit_id, master->commit_id) != 0) {
/* Try to merge even if we don't need to fetch. */
merge_branches_if_necessary (task);
@ -1076,7 +1296,7 @@ update_sync_status_v2 (SyncTask *task)
} else
transition_sync_state (task, SYNC_STATE_DONE);
} else
start_fetch_if_necessary (task);
start_fetch_if_necessary (task, task->info->head_commit);
}
seaf_branch_unref (local);
@ -1156,6 +1376,40 @@ start_sync_repo_proc (SeafSyncManager *manager, SyncTask *task)
return 0;
}
static void
check_head_commit_done (HttpHeadCommit *result, void *user_data)
{
SyncTask *task = user_data;
SyncInfo *info = task->info;
if (!result->check_success) {
seaf_sync_manager_set_task_error (task, SYNC_ERROR_GET_SYNC_INFO);
return;
}
info->deleted_on_relay = result->is_deleted;
info->repo_corrupted = result->is_corrupt;
memcpy (info->head_commit, result->head_commit, 40);
update_sync_status_v2 (task);
}
static int
check_head_commit_http (SyncTask *task)
{
SeafRepo *repo = task->repo;
int ret = http_tx_manager_check_head_commit (seaf->http_tx_mgr,
repo->id, repo->version,
repo->server_url,
repo->token,
check_head_commit_done,
task);
if (ret == 0)
transition_sync_state (task, SYNC_STATE_INIT);
return ret;
}
struct CommitResult {
SyncTask *task;
gboolean changed;
@ -1232,9 +1486,12 @@ commit_job_done (void *vres)
} else {
if (res->changed)
start_upload_if_necessary (res->task);
else if (task->is_manual_sync || task->is_initial_commit)
start_sync_repo_proc (task->mgr, task);
else
else if (task->is_manual_sync || task->is_initial_commit) {
if (task->http_sync)
check_head_commit_http (task);
else
start_sync_repo_proc (task->mgr, task);
} else
transition_sync_state (task, SYNC_STATE_DONE);
}
@ -1389,6 +1646,17 @@ create_sync_task_v2 (SeafSyncManager *manager, SeafRepo *repo,
task->info->in_sync = TRUE;
task->repo = repo;
if (repo->server_url) {
HttpServerState *state = g_hash_table_lookup (manager->http_server_states,
repo->server_url);
if (state) {
if (!state->http_not_supported) {
task->http_sync = TRUE;
task->http_version = state->http_version;
}
}
}
return task;
}
@ -1476,7 +1744,7 @@ sync_repo_v2 (SeafSyncManager *manager, SeafRepo *repo, gboolean is_manual_sync)
if (last_download && strcmp (last_download, EMPTY_SHA1) != 0) {
if (is_manual_sync || can_schedule_repo (manager, repo)) {
task = create_sync_task_v2 (manager, repo, is_manual_sync, FALSE);
start_fetch_if_necessary (task);
start_fetch_if_necessary (task, last_download);
}
goto out;
}
@ -1497,7 +1765,10 @@ sync_repo_v2 (SeafSyncManager *manager, SeafRepo *repo, gboolean is_manual_sync)
if (is_manual_sync || can_schedule_repo (manager, repo)) {
task = create_sync_task_v2 (manager, repo, is_manual_sync, FALSE);
start_sync_repo_proc (manager, task);
if (task->http_sync)
check_head_commit_http (task);
else
start_sync_repo_proc (manager, task);
}
out:
@ -1596,6 +1867,66 @@ check_relay_status (SeafSyncManager *mgr, SeafRepo *repo)
}
}
static void
check_http_protocol_done (HttpProtocolVersion *result, void *user_data)
{
HttpServerState *state = user_data;
state->checking = FALSE;
if (result->check_success) {
state->http_not_supported = result->not_supported;
if (!result->not_supported)
state->http_version = result->version;
} else
state->http_not_supported = TRUE;
}
#define CHECK_HTTP_INTERVAL 10
/*
* Returns TRUE if we can use http-sync; otherwise FALSE.
* If FALSE is returned, the caller should also check @is_checking value.
* If @is_checking is set to TRUE, we're still determining whether the
* server supports http-sync.
*/
static gboolean
check_http_protocol (SeafSyncManager *mgr, SeafRepo *repo, gboolean *is_checking)
{
*is_checking = FALSE;
/* If a repo was cloned before 4.0, server-url is not set. */
if (!repo->server_url)
return FALSE;
HttpServerState *state = g_hash_table_lookup (mgr->http_server_states,
repo->server_url);
if (!state) {
state = g_new0 (HttpServerState, 1);
g_hash_table_insert (mgr->http_server_states,
g_strdup(repo->server_url), state);
}
if (state->checking) {
*is_checking = TRUE;
return FALSE;
}
if (state->http_not_supported)
return FALSE;
if (state->http_version > 0)
return TRUE;
http_tx_manager_check_protocol_version (seaf->http_tx_mgr,
repo->server_url,
check_http_protocol_done,
state);
state->checking = TRUE;
*is_checking = TRUE;
return FALSE;
}
/*
* If the user upgarde from 3.0.x, there may be more than one commit to upload
* on the local branch. The new syncing protocol can't handle more than one
@ -1717,17 +2048,28 @@ auto_sync_pulse (void *vmanager)
if (!manager->priv->auto_sync_enabled || !repo->auto_sync)
continue;
SyncInfo *info = get_sync_info (manager, repo->id);
if (info->in_sync)
continue;
/* Try to use http sync first if enabled. */
gboolean is_checking_http = FALSE;
if (seaf->enable_http_sync && repo->version > 0) {
if (check_http_protocol (manager, repo, &is_checking_http)) {
sync_repo_v2 (manager, repo, FALSE);
continue;
} else if (is_checking_http)
continue;
/* Otherwise we've determined the server doesn't support http-sync. */
}
/* If relay is not ready or protocol version is not determined,
* need to wait.
*/
if (!check_relay_status (manager, repo))
continue;
SyncInfo *info = get_sync_info (manager, repo->id);
if (info->in_sync)
continue;
ServerState *state = g_hash_table_lookup (manager->server_states,
repo->relay_id);
@ -1821,7 +2163,10 @@ on_repo_uploaded (SeafileSession *seaf,
transition_sync_state (task, SYNC_STATE_DONE);
else {
task->uploaded = TRUE;
start_sync_repo_proc (manager, task);
if (!task->http_sync)
start_sync_repo_proc (manager, task);
else
check_head_commit_http (task);
}
} else if (tx_task->state == TASK_STATE_CANCELED) {
transition_sync_state (task, SYNC_STATE_CANCELED);
@ -1844,6 +2189,87 @@ on_repo_uploaded (SeafileSession *seaf,
}
}
static void
on_repo_http_fetched (SeafileSession *seaf,
HttpTxTask *tx_task,
SeafSyncManager *manager)
{
SyncInfo *info = get_sync_info (manager, tx_task->repo_id);
SyncTask *task = info->current_task;
/* Clone tasks are handled by clone manager. */
if (tx_task->is_clone)
return;
if (task->repo->delete_pending) {
transition_sync_state (task, SYNC_STATE_CANCELED);
seaf_repo_manager_del_repo (seaf->repo_mgr, task->repo);
return;
}
if (tx_task->state == HTTP_TASK_STATE_FINISHED) {
memcpy (info->head_commit, tx_task->head, 41);
transition_sync_state (task, SYNC_STATE_DONE);
} else if (tx_task->state == HTTP_TASK_STATE_CANCELED) {
transition_sync_state (task, SYNC_STATE_CANCELED);
} else if (tx_task->state == HTTP_TASK_STATE_ERROR) {
if (tx_task->error == HTTP_TASK_ERR_FORBIDDEN) {
seaf_sync_manager_set_task_error (task, SYNC_ERROR_ACCESS_DENIED);
if (!task->repo->access_denied_notified) {
send_sync_error_notification (task->repo, "sync.access_denied");
task->repo->access_denied_notified = 1;
}
} else
seaf_sync_manager_set_task_error (task, SYNC_ERROR_FETCH);
}
}
static void
on_repo_http_uploaded (SeafileSession *seaf,
HttpTxTask *tx_task,
SeafSyncManager *manager)
{
SyncInfo *info = get_sync_info (manager, tx_task->repo_id);
SyncTask *task = info->current_task;
g_return_if_fail (task != NULL && info->in_sync);
if (task->repo->delete_pending) {
transition_sync_state (task, SYNC_STATE_CANCELED);
seaf_repo_manager_del_repo (seaf->repo_mgr, task->repo);
return;
}
if (tx_task->state == HTTP_TASK_STATE_FINISHED) {
memcpy (info->head_commit, tx_task->head, 41);
/* Save current head commit id for GC. */
seaf_repo_manager_set_repo_property (seaf->repo_mgr,
task->repo->id,
REPO_LOCAL_HEAD,
task->repo->head->commit_id);
task->uploaded = TRUE;
check_head_commit_http (task);
} else if (tx_task->state == HTTP_TASK_STATE_CANCELED) {
transition_sync_state (task, SYNC_STATE_CANCELED);
} else if (tx_task->state == HTTP_TASK_STATE_ERROR) {
if (tx_task->error == HTTP_TASK_ERR_FORBIDDEN) {
seaf_sync_manager_set_task_error (task, SYNC_ERROR_ACCESS_DENIED);
if (!task->repo->access_denied_notified) {
send_sync_error_notification (task->repo, "sync.access_denied");
task->repo->access_denied_notified = 1;
}
} else if (tx_task->error == HTTP_TASK_ERR_NO_QUOTA) {
seaf_sync_manager_set_task_error (task, SYNC_ERROR_QUOTA_FULL);
/* Only notify "quota full" once. */
if (!task->repo->quota_full_notified) {
send_sync_error_notification (task->repo, "sync.quota_full");
task->repo->quota_full_notified = 1;
}
} else
seaf_sync_manager_set_task_error (task, SYNC_ERROR_UPLOAD);
}
}
const char *
sync_error_to_str (int error)

View File

@ -61,6 +61,7 @@ enum {
SYNC_ERROR_MERGE,
SYNC_ERROR_WORKTREE_DIRTY,
SYNC_ERROR_DEPRECATED_SERVER,
SYNC_ERROR_GET_SYNC_INFO, /* for http sync */
SYNC_ERROR_UNKNOWN,
SYNC_ERROR_NUM,
};
@ -80,22 +81,12 @@ struct _SyncTask {
gboolean server_side_merge;
gboolean uploaded;
gboolean http_sync;
int http_version;
SeafRepo *repo; /* for convenience, only valid when in_sync. */
};
enum {
SERVER_SIDE_MERGE_UNKNOWN = 0,
SERVER_SIDE_MERGE_SUPPORTED,
SERVER_SIDE_MERGE_UNSUPPORTED,
};
struct _ServerState {
int server_side_merge;
gboolean checking;
};
typedef struct _ServerState ServerState;
struct _SeafileSession;
struct _SeafSyncManager {
@ -107,6 +98,19 @@ struct _SeafSyncManager {
int sync_interval;
GHashTable *server_states;
GHashTable *http_server_states;
/* Sent/recv bytes from all transfer tasks in this second.
* Since we have http and non-http tasks, sync manager is
* the only reasonable place to put these variables.
*/
gint sent_bytes;
gint recv_bytes;
gint last_sent_bytes;
gint last_recv_bytes;
/* Upload/download rate limits. */
gint upload_limit;
gint download_limit;
SeafSyncManagerPriv *priv;
};

View File

@ -438,19 +438,6 @@ seaf_transfer_manager_new (struct _SeafileSession *seaf)
return NULL;
}
gboolean exists;
int download_limit = seafile_session_config_get_int (seaf,
KEY_DOWNLOAD_LIMIT,
&exists);
if (exists)
mgr->download_limit = download_limit;
int upload_limit = seafile_session_config_get_int (seaf,
KEY_UPLOAD_LIMIT,
&exists);
if (exists)
mgr->upload_limit = upload_limit;
return mgr;
}
@ -1390,7 +1377,7 @@ download_and_checkout_files_thread (void *vdata)
piperead (task->tx_info->done_pipe[0], &rsp, sizeof(rsp));
if (rsp == BLOCK_CLIENT_READY) {
data->status = seaf_repo_fetch_and_checkout (task, task->head);
data->status = seaf_repo_fetch_and_checkout (task, NULL, FALSE, task->head);
block_tx_client_run_command (task->tx_info, BLOCK_CLIENT_CMD_END);
@ -1805,12 +1792,6 @@ update_local_repo (TransferTask *task)
branch = seaf_branch_new ("master", task->repo_id, task->head);
seaf_branch_manager_add_branch (seaf->branch_mgr, branch);
seaf_branch_unref (branch);
/* Set relay to where this repo from. */
if (is_peer_relay (task->dest_id)) {
seaf_repo_manager_set_repo_relay_id (seaf->repo_mgr, repo,
task->dest_id);
}
} else {
if (!repo) {
transition_state_to_error (task, TASK_ERR_UNKNOWN);
@ -2343,56 +2324,6 @@ state_machine_tick (TransferTask *task)
}
}
static inline void
format_transfer_task_detail (TransferTask *task, GString *buf)
{
SeafRepo *repo = seaf_repo_manager_get_repo (seaf->repo_mgr,
task->repo_id);
char *repo_name;
char *type;
if (repo) {
repo_name = repo->name;
type = (task->type == TASK_TYPE_UPLOAD) ? "upload" : "download";
} else if (task->is_clone) {
CloneTask *ctask;
ctask = seaf_clone_manager_get_task (seaf->clone_mgr, task->repo_id);
repo_name = ctask->repo_name;
type = "download";
} else {
return;
}
int rate = transfer_task_get_rate(task);
g_string_append_printf (buf, "%s\t%d %s\n", type, rate, repo_name);
}
/*
* Publish a notification message to report :
*
* [uploading/downloading]\t[transfer-rate] [repo-name]\n
*/
static void
send_transfer_message (GList *tasks)
{
GList *ptr;
TransferTask *task;
GString *buf = g_string_new (NULL);
for (ptr = tasks; ptr; ptr = ptr->next) {
task = ptr->data;
format_transfer_task_detail(task, buf);
}
seaf_mq_manager_publish_notification (seaf->mq_mgr, "transfer",
buf->str);
g_string_free (buf, TRUE);
}
static int
schedule_task_pulse (void *vmanager)
{
@ -2401,58 +2332,17 @@ schedule_task_pulse (void *vmanager)
gpointer key, value;
TransferTask *task;
GList *tasks_in_transfer = NULL;
g_hash_table_iter_init (&iter, mgr->download_tasks);
while (g_hash_table_iter_next (&iter, &key, &value)) {
task = value;
state_machine_tick (task);
if ((task->state == TASK_STATE_NORMAL)
&& (task->runtime_state == TASK_RT_STATE_COMMIT ||
task->runtime_state == TASK_RT_STATE_FS ||
task->runtime_state == TASK_RT_STATE_CHECK_BLOCKS ||
task->runtime_state == TASK_RT_STATE_CHUNK_SERVER ||
task->runtime_state == TASK_RT_STATE_DATA)) {
tasks_in_transfer = g_list_prepend (tasks_in_transfer, task);
}
}
g_hash_table_iter_init (&iter, mgr->upload_tasks);
while (g_hash_table_iter_next (&iter, &key, &value)) {
task = value;
state_machine_tick (task);
if ((task->state == TASK_STATE_NORMAL)
&& (task->runtime_state == TASK_RT_STATE_COMMIT ||
task->runtime_state == TASK_RT_STATE_FS ||
task->runtime_state == TASK_RT_STATE_CHECK_BLOCKS ||
task->runtime_state == TASK_RT_STATE_CHUNK_SERVER ||
task->runtime_state == TASK_RT_STATE_DATA)) {
tasks_in_transfer = g_list_prepend (tasks_in_transfer, task);
}
}
if (tasks_in_transfer) {
send_transfer_message (tasks_in_transfer);
g_list_free (tasks_in_transfer);
}
/* Save tx_bytes to last_tx_bytes and reset tx_bytes to 0 every second */
g_hash_table_iter_init (&iter, mgr->download_tasks);
while (g_hash_table_iter_next (&iter, &key, &value)) {
task = value;
task->last_tx_bytes = g_atomic_int_get (&task->tx_bytes);
g_atomic_int_set (&task->tx_bytes, 0);
}
g_hash_table_iter_init (&iter, mgr->upload_tasks);
while (g_hash_table_iter_next (&iter, &key, &value)) {
task = value;
task->last_tx_bytes = g_atomic_int_get (&task->tx_bytes);
g_atomic_int_set (&task->tx_bytes, 0);
}
g_atomic_int_set (&mgr->sent_bytes, 0);
g_atomic_int_set (&mgr->recv_bytes, 0);
return TRUE;
}

View File

@ -211,13 +211,6 @@ struct _SeafTransferManager {
GHashTable *upload_tasks;
CcnetTimer *schedule_timer;
/* Sent/recv bytes from all tasks in this second. */
gint sent_bytes;
gint recv_bytes;
/* Upload/download rate limits. */
gint upload_limit;
gint download_limit;
};
typedef struct _SeafTransferManager SeafTransferManager;

View File

@ -1,55 +0,0 @@
AM_CFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \
-DPACKAGE_DATA_DIR=\""$(pkgdatadir)"\" \
-DSEAFILE_SERVER \
-I$(top_srcdir)/include \
-I$(top_srcdir)/lib \
-I$(top_builddir)/lib \
-I$(top_srcdir)/common \
-I$(includedir) \
@CCNET_CFLAGS@ \
@SEARPC_CFLAGS@ \
@GLIB2_CFLAGS@ \
@ZDB_CFLAGS@ \
@CURL_CFLAGS@ \
@LIBARCHIVE_CFLAGS@ \
@MSVC_CFLAGS@ \
-Wall
bin_PROGRAMS = fileserver
noinst_HEADERS = seafile-session.h repo-mgr.h \
fileserver.h access-file.h upload-file.h pack-dir.h fileserver-config.h
fileserver_SOURCES = \
fileserver.c \
access-file.c \
upload-file.c \
seafile-session.c \
repo-mgr.c \
pack-dir.c \
fileserver-config.c \
../common/seaf-db.c \
../common/branch-mgr.c \
../common/fs-mgr.c \
../common/block-mgr.c \
../common/block-backend.c \
../common/block-backend-fs.c \
../common/commit-mgr.c \
../common/log.c \
../common/object-list.c \
../common/seaf-utils.c \
../common/obj-store.c \
../common/obj-backend-fs.c \
../common/seafile-crypt.c
# XXX: -levent_openssl must be behind in -levhtp
fileserver_LDADD = @LIBEVENT_LIBS@ -levhtp @SSL_LIBS@ -levent_openssl \
@GLIB2_LIBS@ @GOBJECT_LIBS@ @LIB_RT@ \
@CCNET_LIBS@ \
$(top_builddir)/lib/libseafile.la \
$(top_builddir)/common/cdc/libcdc.la \
@SEARPC_LIBS@ @JANSSON_LIBS@ @ZDB_LIBS@ @CURL_LIBS@ \
@LIBARCHIVE_LIBS@ ${LIB_WS32} @ZLIB_LIBS@
fileserver_LDFLAGS = @STATIC_COMPILE@

View File

@ -1,7 +0,0 @@
== Compile ==
1. compile and install libevhtp library first
cd libevhtp
cmake . # you need to install cmake program to compile it
make
make install

View File

@ -1,35 +0,0 @@
#include "common.h"
#include <glib.h>
#include "fileserver-config.h"
const char *OLD_GROUP_NAME = "httpserver";
const char *GROUP_NAME = "fileserver";
static const char *
get_group_name(GKeyFile *config)
{
return g_key_file_has_group (config, GROUP_NAME) ? GROUP_NAME : OLD_GROUP_NAME;
}
int
fileserver_config_get_integer(GKeyFile *config, char *key, GError **error)
{
const char *group = get_group_name(config);
return g_key_file_get_integer (config, group, key, error);
}
char *
fileserver_config_get_string(GKeyFile *config, char *key, GError **error)
{
const char *group = get_group_name(config);
return g_key_file_get_string (config, group, key, error);
}
gboolean
fileserver_config_get_boolean(GKeyFile *config, char *key, GError **error)
{
const char *group = get_group_name(config);
return g_key_file_get_boolean (config, group, key, error);
}

View File

@ -1,15 +0,0 @@
#ifndef SEAFILE_FILESERVER_CONFIG_H
#define SEAFILE_FILESERVER_CONFIG_H
struct GKeyFile;
int
fileserver_config_get_integer(GKeyFile *config, char *key, GError **error);
char *
fileserver_config_get_string(GKeyFile *config, char *key, GError **error);
gboolean
fileserver_config_get_boolean(GKeyFile *config, char *key, GError **error);
#endif // SEAFILE_FILESERVER_CONFIG_H

View File

@ -1,369 +0,0 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "common.h"
#include "log.h"
#include <getopt.h>
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <event2/event.h>
#else
#include <event.h>
#endif
#include <evhtp.h>
#include <ccnet.h>
#include "seafile-session.h"
#include "fileserver-config.h"
#include "fileserver.h"
#include "access-file.h"
#include "upload-file.h"
#include "utils.h"
#define DEFAULT_BIND_HOST "0.0.0.0"
#define DEFAULT_BIND_PORT 8082
#define DEFAULT_MAX_DOWNLOAD_DIR_SIZE 100 * ((gint64)1 << 20) /* 100MB */
static char *config_dir = NULL;
static char *seafile_dir = NULL;
static char *bind_addr = NULL;
static uint16_t bind_port = 0;
static int num_threads = 10;
static char *pidfile = NULL;
CcnetClient *ccnet_client;
SeafileSession *seaf;
static const char *short_opts = "hvfc:d:t:l:g:G:D:k:P:";
static const struct option long_opts[] = {
{ "help", no_argument, NULL, 'h', },
{ "version", no_argument, NULL, 'v', },
{ "foreground", no_argument, NULL, 'f', },
{ "ccnet-config-dir", required_argument, NULL, 'c', },
{ "seafdir", required_argument, NULL, 'd', },
{ "threads", required_argument, NULL, 't', },
{ "log", required_argument, NULL, 'l' },
{ "ccnet-debug-level", required_argument, NULL, 'g' },
{ "http-debug-level", required_argument, NULL, 'G' },
{ "debug", required_argument, NULL, 'D' },
{ "temp-file-dir", required_argument, NULL, 'k' },
{ "pidfile", required_argument, NULL, 'P' },
};
static void usage ()
{
fprintf (stderr,
"usage: fileserver [-c config_dir] [-d seafile_dir] \n");
}
static void
default_cb(evhtp_request_t *req, void *arg)
{
/* Return empty page. */
evhtp_send_reply (req, EVHTP_RES_OK);
}
static void
remove_pidfile (const char *pidfile)
{
if (pidfile) {
g_unlink (pidfile);
}
}
static int
write_pidfile (const char *pidfile_path)
{
if (!pidfile_path)
return -1;
pid_t pid = getpid();
FILE *pidfile = g_fopen(pidfile_path, "w");
if (!pidfile) {
seaf_warning ("Failed to fopen() pidfile %s: %s\n",
pidfile_path, strerror(errno));
return -1;
}
char buf[32];
snprintf (buf, sizeof(buf), "%d\n", pid);
if (fputs(buf, pidfile) < 0) {
seaf_warning ("Failed to write pidfile %s: %s\n",
pidfile_path, strerror(errno));
fclose (pidfile);
return -1;
}
fflush (pidfile);
fclose (pidfile);
return 0;
}
static void
on_fileserver_exit(void)
{
if (pidfile)
remove_pidfile (pidfile);
}
static void
load_fileserver_config (SeafileSession *session)
{
GError *error = NULL;
char *host = NULL;
int port = 0;
int max_upload_size_mb;
int max_download_dir_size_mb;
host = fileserver_config_get_string (session->config, "host", &error);
if (!error) {
bind_addr = host;
} else {
if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND &&
error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
seaf_warning ("[conf] Error: failed to read the value of 'host'\n");
exit (1);
}
bind_addr = DEFAULT_BIND_HOST;
g_clear_error (&error);
}
port = fileserver_config_get_integer (session->config, "port", &error);
if (!error) {
bind_port = port;
} else {
if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND &&
error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
seaf_warning ("[conf] Error: failed to read the value of 'port'\n");
exit (1);
}
bind_port = DEFAULT_BIND_PORT;
g_clear_error (&error);
}
max_upload_size_mb = fileserver_config_get_integer (session->config,
"max_upload_size",
&error);
if (error) {
session->max_upload_size = -1; /* no limit */
g_clear_error (&error);
} else {
if (max_upload_size_mb <= 0)
session->max_upload_size = -1; /* no limit */
else
session->max_upload_size = max_upload_size_mb * ((gint64)1 << 20);
}
max_download_dir_size_mb = fileserver_config_get_integer (session->config,
"max_download_dir_size",
&error);
if (error) {
session->max_download_dir_size = DEFAULT_MAX_DOWNLOAD_DIR_SIZE;
g_clear_error (&error);
} else {
if (max_download_dir_size_mb <= 0)
session->max_download_dir_size = DEFAULT_MAX_DOWNLOAD_DIR_SIZE;
else
session->max_download_dir_size = max_download_dir_size_mb * ((gint64)1 << 20);
}
}
#ifdef WIN32
/* Get the commandline arguments in unicode, then convert them to utf8 */
static char **
get_argv_utf8 (int *argc)
{
int i = 0;
char **argv = NULL;
const wchar_t *cmdline = NULL;
wchar_t **argv_w = NULL;
cmdline = GetCommandLineW();
argv_w = CommandLineToArgvW (cmdline, argc);
if (!argv_w) {
printf("failed to CommandLineToArgvW(), GLE=%lu\n", GetLastError());
return NULL;
}
argv = (char **)malloc (sizeof(char*) * (*argc));
for (i = 0; i < *argc; i++) {
argv[i] = wchar_to_utf8 (argv_w[i]);
}
return argv;
}
#endif
int
main(int argc, char *argv[])
{
evbase_t *evbase = NULL;
evhtp_t *htp = NULL;
int daemon_mode = 1;
int c;
char *logfile = NULL;
char *ccnet_debug_level_str = "info";
char *http_debug_level_str = "debug";
const char *debug_str = NULL;
char *temp_file_dir = NULL;
#ifdef WIN32
argv = get_argv_utf8 (&argc);
#endif
config_dir = DEFAULT_CONFIG_DIR;
while ((c = getopt_long(argc, argv,
short_opts, long_opts, NULL)) != EOF) {
switch (c) {
case 'h':
usage();
exit(0);
case 'v':
exit(-1);
break;
case 'c':
config_dir = strdup(optarg);
break;
case 'd':
seafile_dir = strdup(optarg);
break;
case 't':
num_threads = atoi(optarg);
break;
case 'f':
daemon_mode = 0;
break;
case 'l':
logfile = g_strdup(optarg);
break;
case 'g':
ccnet_debug_level_str = optarg;
break;
case 'G':
http_debug_level_str = optarg;
break;
case 'D':
debug_str = optarg;
break;
case 'k':
temp_file_dir = optarg;
break;
case 'P':
pidfile = optarg;
break;
default:
usage();
exit(-1);
}
}
#ifndef WIN32
if (daemon_mode) {
#ifndef __APPLE__
daemon (1, 0);
#else /* __APPLE */
/* daemon is deprecated under APPLE
* use fork() instead
* */
switch (fork ()) {
case -1:
seaf_warning ("Failed to daemonize");
exit (-1);
break;
case 0:
/* all good*/
break;
default:
/* kill origin process */
exit (0);
}
#endif /* __APPLE */
}
#endif /* !WIN32 */
#ifdef WIN32
WSADATA wsadata;
WSAStartup(0x0101, &wsadata);
#endif
#if !GLIB_CHECK_VERSION(2, 35, 0)
g_type_init();
#endif
if (!debug_str)
debug_str = g_getenv("SEAFILE_DEBUG");
seafile_debug_set_flags_string (debug_str);
if (seafile_dir == NULL)
seafile_dir = g_build_filename (config_dir, "seafile-data", NULL);
if (logfile == NULL)
logfile = g_build_filename (seafile_dir, "http.log", NULL);
if (seafile_log_init (logfile, ccnet_debug_level_str,
http_debug_level_str) < 0) {
g_warning ("Failed to init log.\n");
exit (1);
}
ccnet_client = ccnet_client_new();
if ((ccnet_client_load_confdir(ccnet_client, config_dir)) < 0) {
g_warning ("Read config dir error\n");
return -1;
}
seaf = seafile_session_new (seafile_dir, ccnet_client);
if (!seaf) {
g_warning ("Failed to create seafile session.\n");
exit (1);
}
if (seafile_session_init(seaf) < 0)
exit (1);
if (temp_file_dir == NULL)
seaf->http_temp_dir = g_build_filename (seaf->seaf_dir, "httptemp", NULL);
else
seaf->http_temp_dir = g_strdup(temp_file_dir);
seaf->client_pool = ccnet_client_pool_new (config_dir);
load_fileserver_config (seaf);
seaf_message ("host = %s, port = %d\n", bind_addr, bind_port);
evbase = event_base_new();
htp = evhtp_new(evbase, NULL);
if (access_file_init (htp) < 0)
exit (1);
if (upload_file_init (htp) < 0)
exit (1);
evhtp_set_gencb(htp, default_cb, NULL);
evhtp_use_threads(htp, NULL, num_threads, NULL);
if (evhtp_bind_socket(htp, bind_addr, bind_port, 128) < 0) {
g_warning ("Could not bind socket: %s\n", strerror(errno));
exit(-1);
}
if (pidfile) {
if (write_pidfile (pidfile) < 0) {
seaf_message ("Failed to write pidfile\n");
return -1;
}
}
atexit (on_fileserver_exit);
event_base_loop(evbase, 0);
return 0;
}

View File

@ -1,6 +0,0 @@
#ifndef SEAFILE_FILESERVER_H
#define SEAFILE_FILESERVER_H
extern SeafileSession *seaf;
#endif // SEAFILE_FILESERVER_H

View File

@ -1,394 +0,0 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#include "common.h"
#include <glib/gstdio.h>
#include <ccnet.h>
#include "utils.h"
#include "log.h"
#include "seafile-session.h"
#include "commit-mgr.h"
#include "branch-mgr.h"
#include "repo-mgr.h"
#include "fs-mgr.h"
#include "seafile-error.h"
#include "seaf-db.h"
#define INDEX_DIR "index"
struct _SeafRepoManagerPriv {
};
static SeafRepo *
load_repo (SeafRepoManager *manager, const char *repo_id);
gboolean
is_repo_id_valid (const char *id)
{
if (!id)
return FALSE;
return is_uuid_valid (id);
}
SeafRepo*
seaf_repo_new (const char *id, const char *name, const char *desc)
{
SeafRepo* repo;
/* valid check */
repo = g_new0 (SeafRepo, 1);
memcpy (repo->id, id, 36);
repo->id[36] = '\0';
repo->name = g_strdup(name);
repo->desc = g_strdup(desc);
repo->ref_cnt = 1;
return repo;
}
void
seaf_repo_free (SeafRepo *repo)
{
if (repo->name) g_free (repo->name);
if (repo->desc) g_free (repo->desc);
if (repo->category) g_free (repo->category);
if (repo->head) seaf_branch_unref (repo->head);
g_free (repo);
}
void
seaf_repo_ref (SeafRepo *repo)
{
g_atomic_int_inc (&repo->ref_cnt);
}
void
seaf_repo_unref (SeafRepo *repo)
{
if (!repo)
return;
if (g_atomic_int_dec_and_test (&repo->ref_cnt))
seaf_repo_free (repo);
}
static void
set_head_common (SeafRepo *repo, SeafBranch *branch)
{
if (repo->head)
seaf_branch_unref (repo->head);
repo->head = branch;
seaf_branch_ref(branch);
}
void
seaf_repo_from_commit (SeafRepo *repo, SeafCommit *commit)
{
repo->name = g_strdup (commit->repo_name);
repo->desc = g_strdup (commit->repo_desc);
repo->encrypted = commit->encrypted;
if (repo->encrypted) {
repo->enc_version = commit->enc_version;
if (repo->enc_version >= 1)
memcpy (repo->magic, commit->magic, 33);
}
repo->no_local_history = commit->no_local_history;
repo->version = commit->version;
}
void
seaf_repo_to_commit (SeafRepo *repo, SeafCommit *commit)
{
commit->repo_name = g_strdup (repo->name);
commit->repo_desc = g_strdup (repo->desc);
commit->encrypted = repo->encrypted;
if (commit->encrypted) {
commit->enc_version = repo->enc_version;
if (commit->enc_version >= 1)
commit->magic = g_strdup (repo->magic);
}
commit->no_local_history = repo->no_local_history;
commit->version = repo->version;
}
static gboolean
collect_commit (SeafCommit *commit, void *vlist, gboolean *stop)
{
GList **commits = vlist;
/* The traverse function will unref the commit, so we need to ref it.
*/
seaf_commit_ref (commit);
*commits = g_list_prepend (*commits, commit);
return TRUE;
}
GList *
seaf_repo_get_commits (SeafRepo *repo)
{
GList *branches;
GList *ptr;
SeafBranch *branch;
GList *commits = NULL;
branches = seaf_branch_manager_get_branch_list (seaf->branch_mgr, repo->id);
if (branches == NULL) {
g_warning ("Failed to get branch list of repo %s.\n", repo->id);
return NULL;
}
for (ptr = branches; ptr != NULL; ptr = ptr->next) {
branch = ptr->data;
gboolean res = seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr,
repo->id,
repo->version,
branch->commit_id,
collect_commit,
&commits,
FALSE);
if (!res) {
for (ptr = commits; ptr != NULL; ptr = ptr->next)
seaf_commit_unref ((SeafCommit *)(ptr->data));
g_list_free (commits);
goto out;
}
}
commits = g_list_reverse (commits);
out:
for (ptr = branches; ptr != NULL; ptr = ptr->next) {
seaf_branch_unref ((SeafBranch *)ptr->data);
}
return commits;
}
static int
compare_repo (const SeafRepo *srepo, const SeafRepo *trepo)
{
return g_strcmp0 (srepo->id, trepo->id);
}
SeafRepoManager*
seaf_repo_manager_new (SeafileSession *seaf)
{
SeafRepoManager *mgr = g_new0 (SeafRepoManager, 1);
mgr->priv = g_new0 (SeafRepoManagerPriv, 1);
mgr->seaf = seaf;
return mgr;
}
int
seaf_repo_manager_init (SeafRepoManager *mgr)
{
return 0;
}
int
seaf_repo_manager_start (SeafRepoManager *mgr)
{
return 0;
}
static gboolean
repo_exists_in_db (SeafDB *db, const char *id)
{
gboolean db_err = FALSE;
return seaf_db_statement_exists (db,
"SELECT repo_id FROM Repo WHERE repo_id = ?",
&db_err, 1, "string", id);
}
SeafRepo*
seaf_repo_manager_get_repo (SeafRepoManager *manager, const gchar *id)
{
SeafRepo repo;
int len = strlen(id);
if (len >= 37)
return NULL;
memcpy (repo.id, id, len + 1);
if (repo_exists_in_db (manager->seaf->db, id)) {
SeafRepo *ret = load_repo (manager, id);
if (!ret)
return NULL;
/* seaf_repo_ref (ret); */
return ret;
}
return NULL;
}
gboolean
seaf_repo_manager_repo_exists (SeafRepoManager *manager, const gchar *id)
{
SeafRepo repo;
memcpy (repo.id, id, 37);
return repo_exists_in_db (manager->seaf->db, id);
}
static void
load_repo_commit (SeafRepoManager *manager,
SeafRepo *repo,
SeafBranch *branch)
{
SeafCommit *commit;
commit = seaf_commit_manager_get_commit_compatible (manager->seaf->commit_mgr,
repo->id,
branch->commit_id);
if (!commit) {
g_warning ("Commit %s is missing\n", branch->commit_id);
repo->is_corrupted = TRUE;
return;
}
set_head_common (repo, branch);
seaf_repo_from_commit (repo, commit);
seaf_commit_unref (commit);
}
static gboolean
load_virtual_info (SeafDBRow *row, void *vrepo_id)
{
char *ret_repo_id = vrepo_id;
const char *origin_repo_id;
origin_repo_id = seaf_db_row_get_column_text (row, 0);
memcpy (ret_repo_id, origin_repo_id, 37);
return FALSE;
}
char *
get_origin_repo_id (SeafRepoManager *mgr, const char *repo_id)
{
char *sql;
char origin_repo_id[37];
memset (origin_repo_id, 0, 37);
sql = "SELECT origin_repo FROM VirtualRepo WHERE repo_id = ?";
seaf_db_statement_foreach_row (seaf->db, sql,
load_virtual_info, origin_repo_id,
1, "string", repo_id);
if (origin_repo_id[0] != 0)
return g_strdup(origin_repo_id);
else
return NULL;
}
static SeafRepo *
load_repo (SeafRepoManager *manager, const char *repo_id)
{
SeafRepo *repo;
SeafBranch *branch;
repo = seaf_repo_new(repo_id, NULL, NULL);
if (!repo) {
g_warning ("[repo mgr] failed to alloc repo.\n");
return NULL;
}
repo->manager = manager;
branch = seaf_branch_manager_get_branch (seaf->branch_mgr, repo_id, "master");
if (!branch) {
g_warning ("Failed to get master branch of repo %.8s.\n", repo_id);
repo->is_corrupted = TRUE;
} else {
load_repo_commit (manager, repo, branch);
seaf_branch_unref (branch);
}
if (repo->is_corrupted) {
g_warning ("Repo %.8s is corrupted.\n", repo->id);
seaf_repo_free (repo);
return NULL;
}
char *origin_repo_id = get_origin_repo_id (manager, repo->id);
if (origin_repo_id)
memcpy (repo->store_id, origin_repo_id, 36);
else
memcpy (repo->store_id, repo->id, 36);
g_free (origin_repo_id);
return repo;
}
static gboolean
collect_repo_id (SeafDBRow *row, void *data)
{
GList **p_ids = data;
const char *repo_id;
repo_id = seaf_db_row_get_column_text (row, 0);
*p_ids = g_list_prepend (*p_ids, g_strdup(repo_id));
return TRUE;
}
GList *
seaf_repo_manager_get_repo_id_list (SeafRepoManager *mgr)
{
GList *ret = NULL;
char sql[256];
snprintf (sql, 256, "SELECT repo_id FROM Repo");
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
collect_repo_id, &ret) < 0)
return NULL;
return ret;
}
GList *
seaf_repo_manager_get_repo_list (SeafRepoManager *mgr, int start, int limit)
{
GList *id_list = NULL, *ptr;
GList *ret = NULL;
SeafRepo *repo;
int rc;
if (start == -1 && limit == -1)
rc = seaf_db_statement_foreach_row (mgr->seaf->db,
"SELECT repo_id FROM Repo",
collect_repo_id, &id_list,
0);
else
rc = seaf_db_statement_foreach_row (mgr->seaf->db,
"SELECT repo_id FROM Repo LIMIT ?, ?",
collect_repo_id, &id_list,
2, "int", start, "int", limit);
if (rc < 0)
return NULL;
for (ptr = id_list; ptr; ptr = ptr->next) {
char *repo_id = ptr->data;
repo = seaf_repo_manager_get_repo (mgr, repo_id);
if (repo != NULL)
ret = g_list_prepend (ret, repo);
}
string_list_free (id_list);
return g_list_reverse (ret);
}

View File

@ -1,104 +0,0 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
#ifndef SEAF_REPO_MGR_H
#define SEAF_REPO_MGR_H
#include <pthread.h>
#include "seafile-object.h"
#include "commit-mgr.h"
#include "branch-mgr.h"
#define REPO_AUTO_SYNC "auto-sync"
#define REPO_AUTO_FETCH "auto-fetch"
#define REPO_AUTO_UPLOAD "auto-upload"
#define REPO_AUTO_MERGE "auto-merge"
#define REPO_AUTO_COMMIT "auto-commit"
#define REPO_RELAY_ID "relay-id"
#define REPO_NET_BROWSABLE "net-browsable"
#define REPO_DOUBLE_SYNC "double-sync"
#define REPO_REMOTE_HEAD "remote-head"
#define REPO_ENCRYPTED 0x1
struct _SeafRepoManager;
typedef struct _SeafRepo SeafRepo;
struct _SeafRepo {
struct _SeafRepoManager *manager;
gchar id[37];
gchar *name;
gchar *desc;
gchar *category; /* not used yet */
gboolean encrypted;
int enc_version;
gchar magic[33]; /* hash(repo_id + passwd), key stretched. */
gboolean no_local_history;
SeafBranch *head;
gboolean is_corrupted;
gboolean delete_pending;
int ref_cnt;
int version;
/* Used to access fs and block sotre.
* This id is different from repo_id when this repo is virtual.
* Virtual repos share fs and block store with its origin repo.
* However, commit store for each repo is always independent.
* So always use repo_id to access commit store.
*/
gchar store_id[37];
};
gboolean is_repo_id_valid (const char *id);
SeafRepo*
seaf_repo_new (const char *id, const char *name, const char *desc);
void
seaf_repo_free (SeafRepo *repo);
void
seaf_repo_ref (SeafRepo *repo);
void
seaf_repo_unref (SeafRepo *repo);
typedef struct _SeafRepoManager SeafRepoManager;
typedef struct _SeafRepoManagerPriv SeafRepoManagerPriv;
struct _SeafRepoManager {
struct _SeafileSession *seaf;
SeafRepoManagerPriv *priv;
};
SeafRepoManager*
seaf_repo_manager_new (struct _SeafileSession *seaf);
int
seaf_repo_manager_init (SeafRepoManager *mgr);
int
seaf_repo_manager_start (SeafRepoManager *mgr);
int
seaf_repo_manager_add_repo (SeafRepoManager *mgr, SeafRepo *repo);
int
seaf_repo_manager_del_repo (SeafRepoManager *mgr, SeafRepo *repo);
SeafRepo*
seaf_repo_manager_get_repo (SeafRepoManager *manager, const gchar *id);
gboolean
seaf_repo_manager_repo_exists (SeafRepoManager *manager, const gchar *id);
GList*
seaf_repo_manager_get_repo_list (SeafRepoManager *mgr, int start, int limit);
GList *
seaf_repo_manager_get_repo_id_list (SeafRepoManager *mgr);
#endif

View File

@ -1,137 +0,0 @@
#include "common.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ccnet.h>
#include <utils.h>
#include <locale.h>
#include "seafile-session.h"
#include "seaf-utils.h"
/* Zip filename in windows should be encoded in UTF-8 to be consistent across
* all system encodings. However, WinRAR(a much popular compress software)
* does not support UTF-8 filename.
*
* To sovle this problem, set the `windows_encoding` under the [zip] category
* in seafile.conf. If set, file name would be converted to the specified
* encoding. Otherwise, the UTF-8 way would be used.
*/
void
load_zip_encoding_config (SeafileSession *session)
{
char *encoding;
GError *error = NULL;
encoding = g_key_file_get_string (session->config, "zip", "windows_encoding", &error);
if (encoding) {
session->windows_encoding = encoding;
} else {
/* No windows specific encoding is specified. Set the ZIP_UTF8 flag. */
setlocale (LC_ALL, "en_US.UTF-8");
}
}
SeafileSession *
seafile_session_new(const char *seafile_dir,
CcnetClient *ccnet_session)
{
char *abs_seafile_dir;
char *tmp_file_dir;
char *config_file_path;
struct stat st;
GKeyFile *config;
SeafileSession *session = NULL;
if (!ccnet_session)
return NULL;
abs_seafile_dir = ccnet_expand_path (seafile_dir);
tmp_file_dir = g_build_filename (abs_seafile_dir, "tmpfiles", NULL);
config_file_path = g_build_filename (abs_seafile_dir, "seafile.conf", NULL);
if (g_stat(abs_seafile_dir, &st) < 0 || !S_ISDIR(st.st_mode)) {
g_warning ("Seafile data dir %s does not exist and is unable to create\n",
abs_seafile_dir);
goto onerror;
}
if (g_stat(tmp_file_dir, &st) < 0 || !S_ISDIR(st.st_mode)) {
g_warning ("Seafile tmp dir %s does not exist and is unable to create\n",
tmp_file_dir);
goto onerror;
}
GError *error = NULL;
config = g_key_file_new ();
if (!g_key_file_load_from_file (config, config_file_path,
G_KEY_FILE_NONE, &error)) {
g_warning ("Failed to load config file.\n");
g_key_file_free (config);
goto onerror;
}
session = g_new0(SeafileSession, 1);
session->seaf_dir = abs_seafile_dir;
session->tmp_file_dir = tmp_file_dir;
session->session = ccnet_session;
session->config = config;
if (load_database_config (session) < 0) {
g_warning ("Failed to load database config.\n");
goto onerror;
}
load_zip_encoding_config (session);
session->fs_mgr = seaf_fs_manager_new (session, abs_seafile_dir);
if (!session->fs_mgr)
goto onerror;
session->block_mgr = seaf_block_manager_new (session, abs_seafile_dir);
if (!session->block_mgr)
goto onerror;
session->commit_mgr = seaf_commit_manager_new (session);
if (!session->commit_mgr)
goto onerror;
session->repo_mgr = seaf_repo_manager_new (session);
if (!session->repo_mgr)
goto onerror;
session->branch_mgr = seaf_branch_manager_new (session);
if (!session->branch_mgr)
goto onerror;
return session;
onerror:
free (abs_seafile_dir);
g_free (tmp_file_dir);
g_free (config_file_path);
g_free (session);
return NULL;
}
int
seafile_session_init (SeafileSession *session)
{
if (seaf_commit_manager_init (session->commit_mgr) < 0)
return -1;
if (seaf_fs_manager_init (session->fs_mgr) < 0)
return -1;
if (seaf_branch_manager_init (session->branch_mgr) < 0)
return -1;
if (seaf_repo_manager_init (session->repo_mgr) < 0)
return -1;
return 0;
}
int
seafile_session_start (SeafileSession *session)
{
return 0;
}

View File

@ -1,55 +0,0 @@
#ifndef SEAFILE_SESSION_H
#define SEAFILE_SESSION_H
#include <stdint.h>
#include <glib.h>
#include "block-mgr.h"
#include "fs-mgr.h"
#include "commit-mgr.h"
#include "branch-mgr.h"
#include "repo-mgr.h"
#include "db.h"
#include "seaf-db.h"
struct _CcnetClient;
typedef struct _SeafileSession SeafileSession;
struct CcnetClientPool;
struct _SeafileSession {
struct _CcnetClient *session;
char *seaf_dir;
char *tmp_file_dir;
/* Config that's only loaded on start */
GKeyFile *config;
SeafDB *db;
char *windows_encoding;
gint64 max_upload_size;
gint64 max_download_dir_size;
char *http_temp_dir;
struct CcnetClientPool *client_pool;
SeafBlockManager *block_mgr;
SeafFSManager *fs_mgr;
SeafCommitManager *commit_mgr;
SeafBranchManager *branch_mgr;
SeafRepoManager *repo_mgr;
};
extern SeafileSession *seaf;
SeafileSession *
seafile_session_new (const char *seafile_dir,
struct _CcnetClient *ccnet);
int
seafile_session_init (SeafileSession *session);
int
seafile_session_start (SeafileSession *session);
#endif

View File

@ -1,7 +0,0 @@
#ifndef UPLOAD_FILE_H
#define UPLOAD_FILE_H
int
upload_file_init (evhtp_t *htp);
#endif

View File

@ -334,26 +334,6 @@ seafile_gc (GError **error);
/* ----------------- Task Related -------------- */
/**
* seafile_get_upload_task_list:
*
* List all the upload tasks.
*
* Returns: A list of task info.
*/
GList* seafile_get_upload_task_list (GError **error);
/**
* seafile_get_download_task_list:
*
* List all the download tasks.
*
* Returns: A list of task info.
*/
GList* seafile_get_download_task_list (GError **error);
/**
* seafile_find_transfer:
*

View File

@ -105,13 +105,13 @@ class SeafileRpcClient(ccnet.RpcClientBase):
def gen_default_worktree(worktree_parent, repo_name):
pass
@searpc_func("string", ["string", "int", "string", "string", "string", "string", "string", "string", "string", "string", "string", "int"])
def seafile_clone(repo_id, repo_version, peer_id, repo_name, worktree, token, password, magic, peer_addr, peer_port, email, random_key, enc_version):
@searpc_func("string", ["string", "int", "string", "string", "string", "string", "string", "string", "string", "string", "string", "int", "string"])
def seafile_clone(repo_id, repo_version, peer_id, repo_name, worktree, token, password, magic, peer_addr, peer_port, email, random_key, enc_version, server_url):
pass
clone = seafile_clone
@searpc_func("string", ["string", "int", "string", "string", "string", "string", "string", "string", "string", "string", "string", "int"])
def seafile_download(repo_id, repo_version, peer_id, repo_name, wt_parent, token, password, magic, peer_addr, peer_port, email, random_key, enc_version):
@searpc_func("string", ["string", "int", "string", "string", "string", "string", "string", "string", "string", "string", "string", "int", "string"])
def seafile_download(repo_id, repo_version, peer_id, repo_name, wt_parent, token, password, magic, peer_addr, peer_port, email, random_key, enc_version, server_url):
pass
download = seafile_download

View File

@ -14,6 +14,7 @@ AM_CFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \
@ZDB_CFLAGS@ \
@MSVC_CFLAGS@ \
@CURL_CFLAGS@ \
@LIBARCHIVE_CFLAGS@
-Wall
bin_PROGRAMS = seaf-server
@ -50,6 +51,11 @@ noinst_HEADERS = web-accesstoken-mgr.h chunkserv-mgr.h seafile-session.h \
size-sched.h \
block-tx-server.h \
copy-mgr.h \
http-server.h \
upload-file.h \
access-file.h \
pack-dir.h \
http-status-codes.h \
$(proc_headers)
seaf_server_SOURCES = \
@ -65,6 +71,10 @@ seaf_server_SOURCES = \
size-sched.c \
virtual-repo.c \
copy-mgr.c \
http-server.c \
upload-file.c \
access-file.c \
pack-dir.c \
monitor-rpc-wrappers.c ../common/seaf-db.c \
../common/branch-mgr.c ../common/fs-mgr.c \
repo-mgr.c ../common/commit-mgr.c \
@ -105,8 +115,9 @@ seaf_server_SOURCES = \
seaf_server_LDADD = @CCNET_LIBS@ \
$(top_builddir)/lib/libseafile_common.la \
$(top_builddir)/common/index/libindex.la \
@GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_RT@ @LIB_UUID@ -lsqlite3 @LIBEVENT_LIBS@ \
@GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_RT@ @LIB_UUID@ -lsqlite3 @LIBEVENT_LIBS@ -levhtp -levent_openssl \
$(top_builddir)/common/cdc/libcdc.la \
@SEARPC_LIBS@ @JANSSON_LIBS@ @ZDB_LIBS@ @CURL_LIBS@ ${LIB_WS32} @ZLIB_LIBS@
@SEARPC_LIBS@ @JANSSON_LIBS@ @ZDB_LIBS@ @CURL_LIBS@ ${LIB_WS32} @ZLIB_LIBS@ \
@LIBARCHIVE_LIBS@
seaf_server_LDFLAGS = @STATIC_COMPILE@ @SERVER_PKG_RPATH@

View File

@ -21,12 +21,10 @@
#include "seafile-object.h"
#include "seafile-crypt.h"
#include "seafile.h"
#include "utils.h"
#include "seafile-session.h"
#include "fileserver.h"
#include "access-file.h"
#include "pack-dir.h"
@ -618,7 +616,7 @@ do_dir (evhtp_request_t *req, SeafRepo *repo, const char *dir_id,
dir_size = seaf_fs_manager_get_fs_size (seaf->fs_mgr,
repo->store_id, repo->version,
dir_id);
if (dir_size < 0 || dir_size > seaf->max_download_dir_size) {
if (dir_size < 0 || dir_size > seaf->http_server->max_download_dir_size) {
seaf_warning ("invalid dir size: %"G_GINT64_FORMAT"\n", dir_size);
ret = -1;
goto out;
@ -745,7 +743,6 @@ access_cb(evhtp_request_t *req, void *arg)
const char *operation = NULL;
const char *user = NULL;
SearpcClient *rpc_client = NULL;
GError *err = NULL;
char *repo_role = NULL;
SeafileCryptKey *key = NULL;
@ -762,13 +759,7 @@ access_cb(evhtp_request_t *req, void *arg)
token = parts[1];
filename = parts[2];
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-rpcserver");
webaccess = (SeafileWebAccess *) searpc_client_call__object (
rpc_client, "seafile_web_query_access_token", SEAFILE_TYPE_WEB_ACCESS,
NULL, 1, "string", token);
webaccess = seaf_web_at_manager_query_access_token (seaf->web_at_mgr, token);
if (!webaccess) {
error = "Bad access token";
goto bad_req;
@ -814,8 +805,8 @@ access_cb(evhtp_request_t *req, void *arg)
if (repo->encrypted) {
err = NULL;
key = (SeafileCryptKey *) seafile_get_decrypt_key (rpc_client,
repo_id, user, &err);
key = seaf_passwd_manager_get_decrypt_key (seaf->passwd_mgr,
repo_id, user);
if (!key) {
error = "Repo is encrypted. Please provide password to view it.";
goto bad_req;
@ -840,8 +831,6 @@ access_cb(evhtp_request_t *req, void *arg)
}
success:
ccnet_rpc_client_free (rpc_client);
g_strfreev (parts);
if (repo != NULL)
seaf_repo_unref (repo);
@ -862,10 +851,6 @@ bad_req:
if (webaccess != NULL)
g_object_unref (webaccess);
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
seaf_warning ("fetch failed: %s\n", error);
evbuffer_add_printf(req->buffer_out, "%s\n", error);
evhtp_send_reply(req, EVHTP_RES_BADREQ);
}
@ -975,7 +960,6 @@ access_blks_cb(evhtp_request_t *req, void *arg)
const char *id = NULL;
const char *operation = NULL;
SearpcClient *rpc_client = NULL;
char *repo_role = NULL;
SeafileWebAccess *webaccess = NULL;
@ -990,13 +974,7 @@ access_blks_cb(evhtp_request_t *req, void *arg)
token = parts[1];
blkid = parts[2];
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-rpcserver");
webaccess = (SeafileWebAccess *) searpc_client_call__object (
rpc_client, "seafile_web_query_access_token", SEAFILE_TYPE_WEB_ACCESS,
NULL, 1, "string", token);
webaccess = seaf_web_at_manager_query_access_token (seaf->web_at_mgr, token);
if (!webaccess) {
error = "Bad access token";
goto bad_req;
@ -1053,8 +1031,6 @@ access_blks_cb(evhtp_request_t *req, void *arg)
}
success:
ccnet_rpc_client_free (rpc_client);
g_strfreev (parts);
if (repo != NULL)
seaf_repo_unref (repo);
@ -1071,10 +1047,6 @@ bad_req:
if (webaccess != NULL)
g_object_unref (webaccess);
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
seaf_warning ("fetch failed: %s\n", error);
evbuffer_add_printf(req->buffer_out, "%s\n", error);
evhtp_send_reply(req, EVHTP_RES_BADREQ);
}

1511
server/http-server.c Normal file

File diff suppressed because it is too large Load Diff

46
server/http-server.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef HTTP_SERVER_H
#define HTTP_SERVER_H
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <event2/event.h>
#else
#include <event.h>
#endif
#include <evhtp.h>
struct _SeafileSession;
typedef struct HttpServer {
char *bind_addr;
int bind_port;
evbase_t *evbase;
evhtp_t *evhtp;
pthread_t thread_id;
GHashTable *token_cache;
pthread_mutex_t token_cache_lock; /* token -> username */
GHashTable *perm_cache;
pthread_mutex_t perm_cache_lock; /* repo_id:username -> permission */
event_t *reap_timer;
struct _SeafileSession *seaf_session;
char *http_temp_dir; /* temp dir for file upload */
char *windows_encoding;
gint64 max_upload_size;
gint64 max_download_dir_size;
} HttpServer;
HttpServer *
seaf_http_server_new (struct _SeafileSession *session);
void seaf_http_server_release (HttpServer *htp_server);
int
seaf_http_server_start (HttpServer *htp_server);
#endif

View File

@ -0,0 +1,12 @@
#ifndef HTTP_STATUS_CODES_H
#define HTTP_STATUS_CODES_H
/* Seafile specific http status codes. */
#define SEAF_HTTP_RES_BADFILENAME 440
#define SEAF_HTTP_RES_EXISTS 441
#define SEAF_HTTP_RES_NOT_EXISTS 441
#define SEAF_HTTP_RES_TOOLARGE 442
#define SEAF_HTTP_RES_NOQUOTA 443
#endif

View File

@ -12,7 +12,6 @@
#include "utils.h"
#include "seafile-session.h"
#include "fileserver.h"
#include <archive.h>
#include <archive_entry.h>
@ -103,10 +102,13 @@ add_file_to_archive (PackDirData *data,
entry = archive_entry_new ();
/* File name fixup for WinRAR */
if (is_windows && seaf->windows_encoding) {
char *win_file_name = do_iconv ("UTF-8", seaf->windows_encoding, pathname);
if (is_windows && seaf->http_server->windows_encoding) {
char *win_file_name = do_iconv ("UTF-8",
seaf->http_server->windows_encoding,
pathname);
if (!win_file_name) {
seaf_warning ("Failed to convert file name to %s\n", seaf->windows_encoding);
seaf_warning ("Failed to convert file name to %s\n",
seaf->http_server->windows_encoding);
ret = -1;
goto out;
}

View File

@ -1061,7 +1061,8 @@ seaf_repo_manager_post_multi_files (SeafRepoManager *mgr,
seaf_repo_manager_merge_virtual_repo (mgr, repo_id, NULL);
*ret_json = format_json_ret (name_list, id_list);
if (ret_json)
*ret_json = format_json_ret (name_list, id_list);
out:
if (repo)
@ -2775,7 +2776,8 @@ seaf_repo_manager_put_file (SeafRepoManager *mgr,
fullpath, NULL, NULL);
if (g_strcmp0(old_file_id, new_dent->id) == 0) {
*new_file_id = g_strdup(new_dent->id);
if (new_file_id)
*new_file_id = g_strdup(new_dent->id);
goto out;
}
@ -2795,7 +2797,8 @@ seaf_repo_manager_put_file (SeafRepoManager *mgr,
goto out;
}
*new_file_id = g_strdup(new_dent->id);
if (new_file_id)
*new_file_id = g_strdup(new_dent->id);
seaf_repo_manager_merge_virtual_repo (mgr, repo_id, NULL);
@ -3024,7 +3027,8 @@ seaf_repo_manager_put_file_blocks (SeafRepoManager *mgr,
fullpath, NULL, NULL);
if (g_strcmp0(old_file_id, new_dent->id) == 0) {
*new_file_id = g_strdup(new_dent->id);
if (new_file_id)
*new_file_id = g_strdup(new_dent->id);
goto out;
}
@ -3044,7 +3048,8 @@ seaf_repo_manager_put_file_blocks (SeafRepoManager *mgr,
goto out;
}
*new_file_id = g_strdup(new_dent->id);
if (new_file_id)
*new_file_id = g_strdup(new_dent->id);
out:
if (repo)

View File

@ -151,6 +151,10 @@ seafile_session_new(const char *seafile_dir,
if (!session->mq_mgr)
goto onerror;
session->http_server = seaf_http_server_new (session);
if (!session->http_server)
goto onerror;
return session;
onerror:
@ -234,6 +238,11 @@ seafile_session_start (SeafileSession *session)
return -1;
}
if (seaf_http_server_start (session->http_server) < 0) {
g_error ("Failed to start http server thread.\n");
return -1;
}
return 0;
}

View File

@ -27,6 +27,8 @@
#include "mq-mgr.h"
#include "http-server.h"
#include <searpc-client.h>
struct _CcnetClient;
@ -81,6 +83,8 @@ struct _SeafileSession {
int rpc_thread_pool_size;
int sync_thread_pool_size;
HttpServer *http_server;
};
extern SeafileSession *seaf;

View File

@ -18,23 +18,13 @@
#include <pthread.h>
#include <ccnet.h>
#include "seafile-object.h"
#include "seafile.h"
#include "utils.h"
#include "seafile-session.h"
#include "fileserver.h"
#include "upload-file.h"
#define SEAF_HTTP_RES_BADFILENAME 440
#define SEAF_HTTP_RES_EXISTS 441
#define SEAF_HTTP_RES_NOT_EXISTS 441
#define SEAF_HTTP_RES_TOOLARGE 442
#define SEAF_HTTP_RES_NOQUOTA 443
#include "http-status-codes.h"
enum RecvState {
RECV_INIT,
@ -84,6 +74,8 @@ typedef struct RecvFSM {
#define MAX_CONTENT_LINE 10240
#define POST_FILE_ERR_FILENAME 401
static GHashTable *upload_progress;
static pthread_mutex_t pg_lock;
@ -221,7 +213,8 @@ check_tmp_file_list (GList *tmp_files, int *error_code)
total_size += (gint64)st.st_size;
}
if (seaf->max_upload_size != -1 && total_size > seaf->max_upload_size) {
if (seaf->http_server->max_upload_size != -1 &&
total_size > seaf->http_server->max_upload_size) {
seaf_warning ("[upload] File size is too large.\n");
*error_code = ERROR_SIZE;
return FALSE;
@ -258,7 +251,6 @@ static void
upload_cb(evhtp_request_t *req, void *arg)
{
RecvFSM *fsm = arg;
SearpcClient *rpc_client = NULL;
char *parent_dir;
GError *error = NULL;
int error_code = ERROR_INTERNAL;
@ -291,12 +283,7 @@ upload_cb(evhtp_request_t *req, void *arg)
if (!check_tmp_file_list (fsm->files, &error_code))
goto error;
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-threaded-rpcserver");
if (seafile_check_quota (rpc_client, fsm->repo_id, NULL) < 0) {
seaf_warning ("[upload] Out of quota.\n");
if (seaf_quota_manager_check_quota (seaf->quota_mgr, fsm->repo_id) < 0) {
error_code = ERROR_QUOTA;
goto error;
}
@ -304,36 +291,33 @@ upload_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 *ret_json = seafile_post_multi_files (rpc_client,
fsm->repo_id,
parent_dir,
filenames_json,
tmp_files_json,
fsm->user,
0,
&error);
int rc = seaf_repo_manager_post_multi_files (seaf->repo_mgr,
fsm->repo_id,
parent_dir,
filenames_json,
tmp_files_json,
fsm->user,
0,
NULL,
&error);
g_free (filenames_json);
g_free (tmp_files_json);
g_free (ret_json);
if (error) {
if (error->code == POST_FILE_ERR_FILENAME) {
error_code = ERROR_FILENAME;
err_file = g_strdup(error->message);
if (rc < 0) {
if (error) {
if (error->code == POST_FILE_ERR_FILENAME) {
error_code = ERROR_FILENAME;
err_file = g_strdup(error->message);
}
g_clear_error (&error);
}
g_clear_error (&error);
goto error;
}
ccnet_rpc_client_free (rpc_client);
/* Redirect to repo dir page after upload finishes. */
redirect_to_success_page (req, fsm->repo_id, parent_dir);
return;
error:
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
redirect_to_upload_error (req, fsm->repo_id, parent_dir,
err_file, error_code);
g_free (err_file);
@ -372,7 +356,6 @@ static void
upload_api_cb(evhtp_request_t *req, void *arg)
{
RecvFSM *fsm = arg;
SearpcClient *rpc_client = NULL;
char *parent_dir, *replace_str;
GError *error = NULL;
int error_code = ERROR_INTERNAL;
@ -416,12 +399,7 @@ upload_api_cb(evhtp_request_t *req, void *arg)
if (!check_tmp_file_list (fsm->files, &error_code))
goto error;
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-threaded-rpcserver");
if (seafile_check_quota (rpc_client, fsm->repo_id, NULL) < 0) {
seaf_warning ("[upload] Out of quota.\n");
if (seaf_quota_manager_check_quota (seaf->quota_mgr, fsm->repo_id) < 0) {
error_code = ERROR_QUOTA;
goto error;
}
@ -429,27 +407,28 @@ upload_api_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 *ret_json = seafile_post_multi_files (rpc_client,
fsm->repo_id,
parent_dir,
filenames_json,
tmp_files_json,
fsm->user,
replace,
&error);
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,
replace,
&ret_json,
&error);
g_free (filenames_json);
g_free (tmp_files_json);
if (error) {
if (error->code == POST_FILE_ERR_FILENAME) {
error_code = ERROR_FILENAME;
seaf_warning ("[upload] Bad filename.\n");
if (rc < 0) {
if (error) {
if (error->code == POST_FILE_ERR_FILENAME) {
error_code = ERROR_FILENAME;
}
g_clear_error (&error);
}
g_clear_error (&error);
goto error;
}
ccnet_rpc_client_free (rpc_client);
const char *use_json = evhtp_kv_find (req->uri->query, "ret-json");
if (use_json) {
evbuffer_add (req->buffer_out, ret_json, strlen(ret_json));
@ -466,9 +445,6 @@ upload_api_cb(evhtp_request_t *req, void *arg)
return;
error:
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
switch (error_code) {
case ERROR_FILENAME:
evbuffer_add_printf(req->buffer_out, "Invalid filename.\n");
@ -502,7 +478,6 @@ static void
upload_blks_api_cb(evhtp_request_t *req, void *arg)
{
RecvFSM *fsm = arg;
SearpcClient *rpc_client = NULL;
char *parent_dir, *file_name, *size_str, *replace_str;
GError *error = NULL;
int error_code = ERROR_INTERNAL;
@ -544,12 +519,7 @@ upload_blks_api_cb(evhtp_request_t *req, void *arg)
if (!check_tmp_file_list (fsm->files, &error_code))
goto error;
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-threaded-rpcserver");
if (seafile_check_quota (rpc_client, fsm->repo_id, NULL) < 0) {
seaf_warning ("[upload-blks] Out of quota.\n");
if (seaf_quota_manager_check_quota (seaf->quota_mgr, fsm->repo_id) < 0) {
error_code = ERROR_QUOTA;
goto error;
}
@ -557,29 +527,30 @@ upload_blks_api_cb(evhtp_request_t *req, void *arg)
blockids_json = file_list_to_json (fsm->filenames);
tmp_files_json = file_list_to_json (fsm->files);
char *new_file_ids = seafile_post_file_blocks (rpc_client,
fsm->repo_id,
parent_dir,
file_name,
blockids_json,
tmp_files_json,
fsm->user,
file_size,
replace,
&error);
char *new_file_ids = NULL;
int rc = seaf_repo_manager_post_file_blocks (seaf->repo_mgr,
fsm->repo_id,
parent_dir,
file_name,
blockids_json,
tmp_files_json,
fsm->user,
file_size,
replace,
&new_file_ids,
&error);
g_free (blockids_json);
g_free (tmp_files_json);
if (error) {
if (error->code == POST_FILE_ERR_FILENAME) {
error_code = ERROR_FILENAME;
seaf_warning ("[upload-blks] Bad filename.\n");
if (rc < 0) {
if (error) {
if (error->code == POST_FILE_ERR_FILENAME) {
error_code = ERROR_FILENAME;
}
g_clear_error (&error);
}
g_clear_error (&error);
goto error;
}
ccnet_rpc_client_free (rpc_client);
evbuffer_add (req->buffer_out, new_file_ids, strlen(new_file_ids));
g_free (new_file_ids);
set_content_length_header (req);
@ -587,9 +558,6 @@ upload_blks_api_cb(evhtp_request_t *req, void *arg)
return;
error:
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
switch (error_code) {
case ERROR_FILENAME:
evbuffer_add_printf(req->buffer_out, "Invalid filename.\n");
@ -623,7 +591,6 @@ static void
upload_blks_ajax_cb(evhtp_request_t *req, void *arg)
{
RecvFSM *fsm = arg;
SearpcClient *rpc_client = NULL;
char *parent_dir, *file_name, *size_str;
GError *error = NULL;
int error_code = ERROR_INTERNAL;
@ -679,12 +646,7 @@ upload_blks_ajax_cb(evhtp_request_t *req, void *arg)
if (!check_tmp_file_list (fsm->files, &error_code))
goto error;
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-threaded-rpcserver");
if (seafile_check_quota (rpc_client, fsm->repo_id, NULL) < 0) {
seaf_warning ("[upload-blks] Out of quota.\n");
if (seaf_quota_manager_check_quota (seaf->quota_mgr, fsm->repo_id) < 0) {
error_code = ERROR_QUOTA;
goto error;
}
@ -692,38 +654,34 @@ upload_blks_ajax_cb(evhtp_request_t *req, void *arg)
blockids_json = file_list_to_json (fsm->filenames);
tmp_files_json = file_list_to_json (fsm->files);
char *new_file_ids = seafile_post_file_blocks (rpc_client,
fsm->repo_id,
parent_dir,
file_name,
blockids_json,
tmp_files_json,
fsm->user,
file_size,
0,
&error);
int rc = seaf_repo_manager_post_file_blocks (seaf->repo_mgr,
fsm->repo_id,
parent_dir,
file_name,
blockids_json,
tmp_files_json,
fsm->user,
file_size,
0,
NULL,
&error);
g_free (blockids_json);
g_free (tmp_files_json);
if (error) {
if (error->code == POST_FILE_ERR_FILENAME) {
error_code = ERROR_FILENAME;
seaf_warning ("[upload-blks] Bad filename.\n");
if (rc < 0) {
if (error) {
if (error->code == POST_FILE_ERR_FILENAME) {
error_code = ERROR_FILENAME;
}
g_clear_error (&error);
}
g_clear_error (&error);
goto error;
}
g_free (new_file_ids);
ccnet_rpc_client_free (rpc_client);
set_content_length_header (req);
evhtp_send_reply (req, EVHTP_RES_OK);
return;
error:
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
switch (error_code) {
case ERROR_FILENAME:
evbuffer_add_printf(req->buffer_out, "{\"error\": \"Invalid filename.\"}");
@ -761,7 +719,6 @@ static void
upload_ajax_cb(evhtp_request_t *req, void *arg)
{
RecvFSM *fsm = arg;
SearpcClient *rpc_client = NULL;
char *parent_dir;
GError *error = NULL;
int error_code = ERROR_INTERNAL;
@ -820,12 +777,7 @@ upload_ajax_cb(evhtp_request_t *req, void *arg)
if (!check_tmp_file_list (fsm->files, &error_code))
goto error;
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-threaded-rpcserver");
if (seafile_check_quota (rpc_client, fsm->repo_id, NULL) < 0) {
seaf_warning ("[upload] Out of quota.\n");
if (seaf_quota_manager_check_quota (seaf->quota_mgr, fsm->repo_id) < 0) {
error_code = ERROR_QUOTA;
goto error;
}
@ -833,27 +785,28 @@ 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 *ret_json = seafile_post_multi_files (rpc_client,
fsm->repo_id,
parent_dir,
filenames_json,
tmp_files_json,
fsm->user,
0,
&error);
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);
g_free (filenames_json);
g_free (tmp_files_json);
if (error) {
if (error->code == POST_FILE_ERR_FILENAME) {
error_code = ERROR_FILENAME;
seaf_warning ("[upload] Bad filename.\n");
if (rc < 0) {
if (error) {
if (error->code == POST_FILE_ERR_FILENAME) {
error_code = ERROR_FILENAME;
}
g_clear_error (&error);
}
g_clear_error (&error);
goto error;
}
ccnet_rpc_client_free (rpc_client);
evbuffer_add (req->buffer_out, ret_json, strlen(ret_json));
g_free (ret_json);
@ -862,9 +815,6 @@ upload_ajax_cb(evhtp_request_t *req, void *arg)
return;
error:
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
switch (error_code) {
case ERROR_FILENAME:
evbuffer_add_printf(req->buffer_out, "{\"error\": \"Invalid filename.\"}");
@ -898,7 +848,6 @@ static void
update_cb(evhtp_request_t *req, void *arg)
{
RecvFSM *fsm = arg;
SearpcClient *rpc_client = NULL;
char *target_file, *parent_dir = NULL, *filename = NULL;
const char *head_id = NULL;
GError *error = NULL;
@ -931,37 +880,30 @@ update_cb(evhtp_request_t *req, void *arg)
head_id = evhtp_kv_find (req->uri->query, "head");
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-threaded-rpcserver");
if (seafile_check_quota (rpc_client, fsm->repo_id, NULL) < 0) {
seaf_warning ("[update] Out of quota.\n");
if (seaf_quota_manager_check_quota (seaf->quota_mgr, fsm->repo_id) < 0) {
error_code = ERROR_QUOTA;
goto error;
}
char *new_file_id = seafile_put_file (rpc_client,
fsm->repo_id,
(char *)(fsm->files->data),
parent_dir,
filename,
fsm->user,
head_id,
&error);
if (error) {
if (g_strcmp0 (error->message, "file does not exist") == 0) {
error_code = ERROR_NOT_EXIST;
int rc = seaf_repo_manager_put_file (seaf->repo_mgr,
fsm->repo_id,
(char *)(fsm->files->data),
parent_dir,
filename,
fsm->user,
head_id,
NULL,
&error);
if (rc < 0) {
if (error) {
if (g_strcmp0 (error->message, "file does not exist") == 0) {
error_code = ERROR_NOT_EXIST;
}
g_clear_error (&error);
}
if (error->message)
seaf_warning ("%s\n", error->message);
g_clear_error (&error);
goto error;
}
g_free (new_file_id);
ccnet_rpc_client_free (rpc_client);
/* Redirect to repo dir page after upload finishes. */
redirect_to_success_page (req, fsm->repo_id, parent_dir);
g_free (parent_dir);
@ -969,9 +911,6 @@ update_cb(evhtp_request_t *req, void *arg)
return;
error:
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
redirect_to_update_error (req, fsm->repo_id, target_file, error_code);
g_free (parent_dir);
g_free (filename);
@ -981,7 +920,6 @@ static void
update_api_cb(evhtp_request_t *req, void *arg)
{
RecvFSM *fsm = arg;
SearpcClient *rpc_client = NULL;
char *target_file, *parent_dir = NULL, *filename = NULL;
const char *head_id = NULL;
GError *error = NULL;
@ -1015,38 +953,33 @@ update_api_cb(evhtp_request_t *req, void *arg)
head_id = evhtp_kv_find (req->uri->query, "head");
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-threaded-rpcserver");
if (seafile_check_quota (rpc_client, fsm->repo_id, NULL) < 0) {
seaf_warning ("[update] Out of quota.\n");
if (seaf_quota_manager_check_quota (seaf->quota_mgr, fsm->repo_id) < 0) {
error_code = ERROR_QUOTA;
goto error;
}
new_file_id = seafile_put_file (rpc_client,
fsm->repo_id,
(char *)(fsm->files->data),
parent_dir,
filename,
fsm->user,
head_id,
&error);
int rc = seaf_repo_manager_put_file (seaf->repo_mgr,
fsm->repo_id,
(char *)(fsm->files->data),
parent_dir,
filename,
fsm->user,
head_id,
&new_file_id,
&error);
g_free (parent_dir);
g_free (filename);
if (error) {
if (g_strcmp0 (error->message, "file does not exist") == 0) {
error_code = ERROR_NOT_EXIST;
if (rc < 0) {
if (error) {
if (g_strcmp0 (error->message, "file does not exist") == 0) {
error_code = ERROR_NOT_EXIST;
}
g_clear_error (&error);
}
if (error->message)
seaf_warning ("%s\n", error->message);
g_clear_error (&error);
goto error;
}
ccnet_rpc_client_free (rpc_client);
/* Send back the new file id, so that the mobile client can update local cache */
evbuffer_add(req->buffer_out, new_file_id, strlen(new_file_id));
set_content_length_header (req);
@ -1056,9 +989,6 @@ update_api_cb(evhtp_request_t *req, void *arg)
return;
error:
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
switch (error_code) {
case ERROR_FILENAME:
evbuffer_add_printf(req->buffer_out, "Invalid filename.\n");
@ -1098,7 +1028,6 @@ static void
update_blks_api_cb(evhtp_request_t *req, void *arg)
{
RecvFSM *fsm = arg;
SearpcClient *rpc_client = NULL;
char *target_file, *parent_dir = NULL, *filename = NULL, *size_str = NULL;
const char *head_id = NULL;
GError *error = NULL;
@ -1128,44 +1057,39 @@ update_blks_api_cb(evhtp_request_t *req, void *arg)
head_id = evhtp_kv_find (req->uri->query, "head");
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-threaded-rpcserver");
if (seafile_check_quota (rpc_client, fsm->repo_id, NULL) < 0) {
seaf_warning ("[update-blks] Out of quota.\n");
if (seaf_quota_manager_check_quota (seaf->quota_mgr, fsm->repo_id) < 0) {
error_code = ERROR_QUOTA;
goto error;
}
blockids_json = file_list_to_json (fsm->filenames);
tmp_files_json = file_list_to_json (fsm->files);
new_file_id = seafile_put_file_blocks (rpc_client,
fsm->repo_id,
parent_dir,
filename,
blockids_json,
tmp_files_json,
fsm->user,
head_id,
file_size,
&error);
int rc = seaf_repo_manager_put_file_blocks (seaf->repo_mgr,
fsm->repo_id,
parent_dir,
filename,
blockids_json,
tmp_files_json,
fsm->user,
head_id,
file_size,
&new_file_id,
&error);
g_free (blockids_json);
g_free (tmp_files_json);
g_free (parent_dir);
g_free (filename);
if (error) {
if (g_strcmp0 (error->message, "file does not exist") == 0) {
error_code = ERROR_NOT_EXIST;
if (rc < 0) {
if (error) {
if (g_strcmp0 (error->message, "file does not exist") == 0) {
error_code = ERROR_NOT_EXIST;
}
g_clear_error (&error);
}
if (error->message)
seaf_warning ("%s\n", error->message);
g_clear_error (&error);
goto error;
}
ccnet_rpc_client_free (rpc_client);
/* Send back the new file id, so that the mobile client can update local cache */
evbuffer_add(req->buffer_out, new_file_id, strlen(new_file_id));
set_content_length_header (req);
@ -1175,9 +1099,6 @@ update_blks_api_cb(evhtp_request_t *req, void *arg)
return;
error:
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
switch (error_code) {
case ERROR_FILENAME:
evbuffer_add_printf(req->buffer_out, "Invalid filename.\n");
@ -1217,12 +1138,10 @@ static void
update_blks_ajax_cb(evhtp_request_t *req, void *arg)
{
RecvFSM *fsm = arg;
SearpcClient *rpc_client = NULL;
char *target_file, *parent_dir = NULL, *filename = NULL, *size_str = NULL;
const char *head_id = NULL;
GError *error = NULL;
int error_code = ERROR_INTERNAL;
char *new_file_id = NULL;
char *blockids_json, *tmp_files_json;
gint64 file_size = -1;
@ -1273,55 +1192,45 @@ update_blks_ajax_cb(evhtp_request_t *req, void *arg)
head_id = evhtp_kv_find (req->uri->query, "head");
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-threaded-rpcserver");
if (seafile_check_quota (rpc_client, fsm->repo_id, NULL) < 0) {
seaf_warning ("[update-blks] Out of quota.\n");
if (seaf_quota_manager_check_quota (seaf->quota_mgr, fsm->repo_id) < 0) {
error_code = ERROR_QUOTA;
goto error;
}
blockids_json = file_list_to_json (fsm->filenames);
tmp_files_json = file_list_to_json (fsm->files);
new_file_id = seafile_put_file_blocks (rpc_client,
fsm->repo_id,
parent_dir,
filename,
blockids_json,
tmp_files_json,
fsm->user,
head_id,
file_size,
&error);
int rc = seaf_repo_manager_put_file_blocks (seaf->repo_mgr,
fsm->repo_id,
parent_dir,
filename,
blockids_json,
tmp_files_json,
fsm->user,
head_id,
file_size,
NULL,
&error);
g_free (blockids_json);
g_free (tmp_files_json);
g_free (parent_dir);
g_free (filename);
if (error) {
if (g_strcmp0 (error->message, "file does not exist") == 0) {
error_code = ERROR_NOT_EXIST;
if (rc < 0) {
if (error) {
if (g_strcmp0 (error->message, "file does not exist") == 0) {
error_code = ERROR_NOT_EXIST;
}
g_clear_error (&error);
}
if (error->message)
seaf_warning ("%s\n", error->message);
g_clear_error (&error);
goto error;
}
ccnet_rpc_client_free (rpc_client);
set_content_length_header (req);
evhtp_send_reply (req, EVHTP_RES_OK);
g_free (new_file_id);
return;
error:
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
switch (error_code) {
case ERROR_FILENAME:
evbuffer_add_printf(req->buffer_out, "Invalid filename.\n");
@ -1361,7 +1270,6 @@ static void
update_ajax_cb(evhtp_request_t *req, void *arg)
{
RecvFSM *fsm = arg;
SearpcClient *rpc_client = NULL;
char *target_file, *parent_dir = NULL, *filename = NULL;
const char *head_id = NULL;
GError *error = NULL;
@ -1421,39 +1329,33 @@ update_ajax_cb(evhtp_request_t *req, void *arg)
head_id = evhtp_kv_find (req->uri->query, "head");
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-threaded-rpcserver");
if (seafile_check_quota (rpc_client, fsm->repo_id, NULL) < 0) {
seaf_warning ("[update] Out of quota.\n");
if (seaf_quota_manager_check_quota (seaf->quota_mgr, fsm->repo_id) < 0) {
error_code = ERROR_QUOTA;
goto error;
}
new_file_id = seafile_put_file (rpc_client,
fsm->repo_id,
(char *)(fsm->files->data),
parent_dir,
filename,
fsm->user,
head_id,
&error);
int rc = seaf_repo_manager_put_file (seaf->repo_mgr,
fsm->repo_id,
(char *)(fsm->files->data),
parent_dir,
filename,
fsm->user,
head_id,
NULL,
&error);
g_free (parent_dir);
g_free (filename);
if (error) {
if (g_strcmp0 (error->message, "file does not exist") == 0) {
error_code = ERROR_NOT_EXIST;
if (rc < 0) {
if (error) {
if (g_strcmp0 (error->message, "file does not exist") == 0) {
error_code = ERROR_NOT_EXIST;
}
g_clear_error (&error);
}
if (error->message)
seaf_warning ("%s\n", error->message);
g_clear_error (&error);
goto error;
}
ccnet_rpc_client_free (rpc_client);
GString *res_buf = g_string_new (NULL);
GList *ptr;
@ -1473,13 +1375,9 @@ update_ajax_cb(evhtp_request_t *req, void *arg)
set_content_length_header (req);
evhtp_send_reply (req, EVHTP_RES_OK);
g_free (new_file_id);
return;
error:
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
switch (error_code) {
case ERROR_FILENAME:
evbuffer_add_printf(req->buffer_out, "Invalid filename.\n");
@ -1644,7 +1542,7 @@ open_temp_file (RecvFSM *fsm)
GString *temp_file = g_string_new (NULL);
g_string_printf (temp_file, "%s/%sXXXXXX",
seaf->http_temp_dir, get_basename(fsm->file_name));
seaf->http_server->http_temp_dir, get_basename(fsm->file_name));
fsm->fd = g_mkstemp (temp_file->str);
if (fsm->fd < 0) {
@ -1957,15 +1855,14 @@ get_boundary (evhtp_headers_t *hdr)
}
static int
check_access_token (SearpcClient *rpc,
const char *token,
check_access_token (const char *token,
char **repo_id,
char **user)
{
SeafileWebAccess *webaccess;
webaccess = (SeafileWebAccess *)
seafile_web_query_access_token (rpc, token, NULL);
seaf_web_at_manager_query_access_token (seaf->web_at_mgr, token);
if (!webaccess)
return -1;
@ -2005,7 +1902,6 @@ get_progress_info (evhtp_request_t *req,
static evhtp_res
upload_headers_cb (evhtp_request_t *req, evhtp_headers_t *hdr, void *arg)
{
SearpcClient *rpc_client = NULL;
char *token, *repo_id = NULL, *user = NULL;
char *boundary = NULL;
gint64 content_len;
@ -2026,12 +1922,7 @@ upload_headers_cb (evhtp_request_t *req, evhtp_headers_t *hdr, void *arg)
goto err;
}
rpc_client = ccnet_create_pooled_rpc_client (seaf->client_pool,
NULL,
"seafserv-rpcserver");
if (check_access_token (rpc_client, token, &repo_id, &user) < 0) {
seaf_warning ("[upload] Invalid token.\n");
if (check_access_token (token, &repo_id, &user) < 0) {
err_msg = "Access denied";
goto err;
}
@ -2079,8 +1970,6 @@ upload_headers_cb (evhtp_request_t *req, evhtp_headers_t *hdr, void *arg)
/* Set arg for upload_cb or update_cb. */
req->cbarg = fsm;
ccnet_rpc_client_free (rpc_client);
return EVHTP_RES_OK;
err:
@ -2096,9 +1985,6 @@ err:
set_content_length_header (req);
evhtp_send_reply (req, EVHTP_RES_BADREQ);
if (rpc_client)
ccnet_rpc_client_free (rpc_client);
g_free (repo_id);
g_free (user);
g_free (boundary);
@ -2152,13 +2038,14 @@ upload_progress_cb(evhtp_request_t *req, void *arg)
}
int
upload_file_init (evhtp_t *htp)
upload_file_init (HttpServer *http_server)
{
evhtp_t *htp = http_server->evhtp;
evhtp_callback_t *cb;
if (g_mkdir_with_parents (seaf->http_temp_dir, 0777) < 0) {
if (g_mkdir_with_parents (http_server->http_temp_dir, 0777) < 0) {
seaf_warning ("Failed to create temp file dir %s.\n",
seaf->http_temp_dir);
http_server->http_temp_dir);
return -1;
}

9
server/upload-file.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef UPLOAD_FILE_H
#define UPLOAD_FILE_H
struct HttpServer;
int
upload_file_init (struct HttpServer *http_server);
#endif