1 #include "common.h"
2
3 #include <pthread.h>
4 #include <string.h>
5 #include <jansson.h>
6 #include <locale.h>
7 #include <sys/types.h>
8
9 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
10 #include <event2/event.h>
11 #else
12 #include <event.h>
13 #endif
14
15 #include <evhtp.h>
16
17 #include "mq-mgr.h"
18 #include "utils.h"
19 #include "log.h"
20 #include "http-server.h"
21 #include "seafile-session.h"
22 #include "diff-simple.h"
23 #include "merge-new.h"
24 #include "seaf-db.h"
25
26 #include "access-file.h"
27 #include "upload-file.h"
28 #include "fileserver-config.h"
29
30 #include "http-status-codes.h"
31
32 #define DEFAULT_BIND_HOST "0.0.0.0"
33 #define DEFAULT_BIND_PORT 8082
34 #define DEFAULT_WORKER_THREADS 10
35 #define DEFAULT_MAX_DOWNLOAD_DIR_SIZE 100 * ((gint64)1 << 20) /* 100MB */
36 #define DEFAULT_MAX_INDEXING_THREADS 1
37 #define DEFAULT_MAX_INDEX_PROCESSING_THREADS 3
38 #define DEFAULT_FIXED_BLOCK_SIZE ((gint64)1 << 23) /* 8MB */
39 #define DEFAULT_CLUSTER_SHARED_TEMP_FILE_MODE 0600
40
41 #define HOST "host"
42 #define PORT "port"
43
44 #define HTTP_TEMP_FILE_SCAN_INTERVAL 3600 /*1h*/
45 #define HTTP_TEMP_FILE_DEFAULT_TTL 3600 * 24 * 3 /*3days*/
46 #define HTTP_TEMP_FILE_TTL "http_temp_file_ttl"
47 #define HTTP_SCAN_INTERVAL "http_temp_scan_interval"
48
49 #define INIT_INFO "If you see this page, Seafile HTTP syncing component works."
50 #define PROTO_VERSION "{\"version\": 2}"
51
52 #define CLEANING_INTERVAL_SEC 300 /* 5 minutes */
53 #define TOKEN_EXPIRE_TIME 7200 /* 2 hours */
54 #define PERM_EXPIRE_TIME 7200 /* 2 hours */
55 #define VIRINFO_EXPIRE_TIME 7200 /* 2 hours */
56
57 #define FS_ID_LIST_MAX_WORKERS 3
58 #define FS_ID_LIST_TOKEN_LEN 36
59
60 struct _HttpServer {
61 evbase_t *evbase;
62 evhtp_t *evhtp;
63 pthread_t thread_id;
64
65 GHashTable *token_cache;
66 pthread_mutex_t token_cache_lock; /* token -> username */
67
68 GHashTable *perm_cache;
69 pthread_mutex_t perm_cache_lock; /* repo_id:username -> permission */
70
71 GHashTable *vir_repo_info_cache;
72 pthread_mutex_t vir_repo_info_cache_lock;
73
74 event_t *reap_timer;
75
76 GThreadPool *compute_fs_obj_id_pool;
77
78 GHashTable *fs_obj_ids;
79 pthread_mutex_t fs_obj_ids_lock;
80 };
81 typedef struct _HttpServer HttpServer;
82
83 struct _StatsEventData {
84 char *etype;
85 char *user;
86 char *operation;
87 char repo_id[37];
88 guint64 bytes;
89 };
90 typedef struct _StatsEventData StatsEventData;
91
92 typedef struct TokenInfo {
93 char *repo_id;
94 char *email;
95 gint64 expire_time;
96 } TokenInfo;
97
98 typedef struct PermInfo {
99 char *perm;
100 gint64 expire_time;
101 } PermInfo;
102
103 typedef struct VirRepoInfo {
104 char *store_id;
105 gint64 expire_time;
106 } VirRepoInfo;
107
108 typedef struct FsHdr {
109 char obj_id[40];
110 guint32 obj_size;
111 } __attribute__((__packed__)) FsHdr;
112
113 typedef enum CheckExistType {
114 CHECK_FS_EXIST,
115 CHECK_BLOCK_EXIST
116 } CheckExistType;
117
118 const char *GET_PROTO_PATH = "/protocol-version";
119 const char *OP_PERM_CHECK_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/permission-check/.*";
120 const char *GET_CHECK_QUOTA_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/quota-check/.*";
121 const char *HEAD_COMMIT_OPER_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/commit/HEAD";
122 const char *GET_HEAD_COMMITS_MULTI_REGEX = "^/repo/head-commits-multi";
123 const char *COMMIT_OPER_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/commit/[\\da-z]{40}";
124 const char *PUT_COMMIT_INFO_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/commit/[\\da-z]{40}";
125 const char *GET_FS_OBJ_ID_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/fs-id-list/.*";
126 const char *START_FS_OBJ_ID_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/start-fs-id-list/.*";
127 const char *QUERY_FS_OBJ_ID_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/query-fs-id-list/.*";
128 const char *RETRIEVE_FS_OBJ_ID_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/retrieve-fs-id-list/.*";
129 const char *BLOCK_OPER_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/block/[\\da-z]{40}";
130 const char *POST_CHECK_FS_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/check-fs";
131 const char *POST_CHECK_BLOCK_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/check-blocks";
132 const char *POST_RECV_FS_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/recv-fs";
133 const char *POST_PACK_FS_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/pack-fs";
134 const char *GET_BLOCK_MAP_REGEX = "^/repo/[\\da-z]{8}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{4}-[\\da-z]{12}/block-map/[\\da-z]{40}";
135
136 //accessible repos
137 const char *GET_ACCESSIBLE_REPO_LIST_REGEX = "/accessible-repos";
138
139 static void
load_http_config(HttpServerStruct * htp_server,SeafileSession * session)140 load_http_config (HttpServerStruct *htp_server, SeafileSession *session)
141 {
142 GError *error = NULL;
143 char *host = NULL;
144 int port = 0;
145 int worker_threads;
146 int web_token_expire_time;
147 int fixed_block_size_mb;
148 char *encoding;
149 int max_indexing_threads;
150 int max_index_processing_threads;
151 char *cluster_shared_temp_file_mode = NULL;
152
153 host = fileserver_config_get_string (session->config, HOST, &error);
154 if (!error) {
155 htp_server->bind_addr = host;
156 } else {
157 if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND &&
158 error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
159 seaf_warning ("[conf] Error: failed to read the value of 'host'\n");
160 exit (1);
161 }
162
163 htp_server->bind_addr = g_strdup (DEFAULT_BIND_HOST);
164 g_clear_error (&error);
165 }
166
167 port = fileserver_config_get_integer (session->config, PORT, &error);
168 if (!error) {
169 htp_server->bind_port = port;
170 } else {
171 if (error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND &&
172 error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND) {
173 seaf_warning ("[conf] Error: failed to read the value of 'port'\n");
174 exit (1);
175 }
176
177 htp_server->bind_port = DEFAULT_BIND_PORT;
178 g_clear_error (&error);
179 }
180
181 worker_threads = fileserver_config_get_integer (session->config, "worker_threads",
182 &error);
183 if (error) {
184 htp_server->worker_threads = DEFAULT_WORKER_THREADS;
185 g_clear_error (&error);
186 } else {
187 if (worker_threads <= 0)
188 htp_server->worker_threads = DEFAULT_WORKER_THREADS;
189 else
190 htp_server->worker_threads = worker_threads;
191 }
192 seaf_message ("fileserver: worker_threads = %d\n", htp_server->worker_threads);
193
194 fixed_block_size_mb = fileserver_config_get_integer (session->config,
195 "fixed_block_size",
196 &error);
197 if (error){
198 htp_server->fixed_block_size = DEFAULT_FIXED_BLOCK_SIZE;
199 g_clear_error(&error);
200 } else {
201 if (fixed_block_size_mb <= 0)
202 htp_server->fixed_block_size = DEFAULT_FIXED_BLOCK_SIZE;
203 else
204 htp_server->fixed_block_size = fixed_block_size_mb * ((gint64)1 << 20);
205 }
206 seaf_message ("fileserver: fixed_block_size = %"G_GINT64_FORMAT"\n",
207 htp_server->fixed_block_size);
208
209 web_token_expire_time = fileserver_config_get_integer (session->config,
210 "web_token_expire_time",
211 &error);
212 if (error){
213 htp_server->web_token_expire_time = 3600; /* default 3600s */
214 g_clear_error(&error);
215 } else {
216 if (web_token_expire_time <= 0)
217 htp_server->web_token_expire_time = 3600; /* default 3600s */
218 else
219 htp_server->web_token_expire_time = web_token_expire_time;
220 }
221 seaf_message ("fileserver: web_token_expire_time = %d\n",
222 htp_server->web_token_expire_time);
223
224 max_indexing_threads = fileserver_config_get_integer (session->config,
225 "max_indexing_threads",
226 &error);
227 if (error) {
228 htp_server->max_indexing_threads = DEFAULT_MAX_INDEXING_THREADS;
229 g_clear_error (&error);
230 } else {
231 if (max_indexing_threads <= 0)
232 htp_server->max_indexing_threads = DEFAULT_MAX_INDEXING_THREADS;
233 else
234 htp_server->max_indexing_threads = max_indexing_threads;
235 }
236 seaf_message ("fileserver: max_indexing_threads = %d\n",
237 htp_server->max_indexing_threads);
238
239 max_index_processing_threads = fileserver_config_get_integer (session->config,
240 "max_index_processing_threads",
241 &error);
242 if (error) {
243 htp_server->max_index_processing_threads = DEFAULT_MAX_INDEX_PROCESSING_THREADS;
244 g_clear_error (&error);
245 } else {
246 if (max_index_processing_threads <= 0)
247 htp_server->max_index_processing_threads = DEFAULT_MAX_INDEX_PROCESSING_THREADS;
248 else
249 htp_server->max_index_processing_threads = max_index_processing_threads;
250 }
251 seaf_message ("fileserver: max_index_processing_threads= %d\n",
252 htp_server->max_index_processing_threads);
253
254 cluster_shared_temp_file_mode = fileserver_config_get_string (session->config,
255 "cluster_shared_temp_file_mode",
256 &error);
257 if (error) {
258 htp_server->cluster_shared_temp_file_mode = DEFAULT_CLUSTER_SHARED_TEMP_FILE_MODE;
259 g_clear_error (&error);
260 } else {
261 if (!cluster_shared_temp_file_mode) {
262 htp_server->cluster_shared_temp_file_mode = DEFAULT_CLUSTER_SHARED_TEMP_FILE_MODE;
263 } else {
264 htp_server->cluster_shared_temp_file_mode = strtol(cluster_shared_temp_file_mode, NULL, 8);
265
266 if (htp_server->cluster_shared_temp_file_mode < 0001 ||
267 htp_server->cluster_shared_temp_file_mode > 0777)
268 htp_server->cluster_shared_temp_file_mode = DEFAULT_CLUSTER_SHARED_TEMP_FILE_MODE;
269
270 g_free (cluster_shared_temp_file_mode);
271 }
272 }
273 seaf_message ("fileserver: cluster_shared_temp_file_mode = %o\n",
274 htp_server->cluster_shared_temp_file_mode);
275
276 encoding = g_key_file_get_string (session->config,
277 "zip", "windows_encoding",
278 &error);
279 if (encoding) {
280 htp_server->windows_encoding = encoding;
281 } else {
282 g_clear_error (&error);
283 /* No windows specific encoding is specified. Set the ZIP_UTF8 flag. */
284 setlocale (LC_ALL, "en_US.UTF-8");
285 }
286 }
287
288 static int
validate_token(HttpServer * htp_server,evhtp_request_t * req,const char * repo_id,char ** username,gboolean skip_cache)289 validate_token (HttpServer *htp_server, evhtp_request_t *req,
290 const char *repo_id, char **username,
291 gboolean skip_cache)
292 {
293 char *email = NULL;
294 TokenInfo *token_info;
295
296 const char *token = evhtp_kv_find (req->headers_in, "Seafile-Repo-Token");
297 if (token == NULL) {
298 evhtp_send_reply (req, EVHTP_RES_BADREQ);
299 return EVHTP_RES_BADREQ;
300 }
301
302 if (!skip_cache) {
303 pthread_mutex_lock (&htp_server->token_cache_lock);
304
305 token_info = g_hash_table_lookup (htp_server->token_cache, token);
306 if (token_info) {
307 if (username)
308 *username = g_strdup(token_info->email);
309 pthread_mutex_unlock (&htp_server->token_cache_lock);
310 return EVHTP_RES_OK;
311 }
312
313 pthread_mutex_unlock (&htp_server->token_cache_lock);
314 }
315
316 email = seaf_repo_manager_get_email_by_token (seaf->repo_mgr,
317 repo_id, token);
318 if (email == NULL) {
319 pthread_mutex_lock (&htp_server->token_cache_lock);
320 g_hash_table_remove (htp_server->token_cache, token);
321 pthread_mutex_unlock (&htp_server->token_cache_lock);
322 return EVHTP_RES_FORBIDDEN;
323 }
324
325 token_info = g_new0 (TokenInfo, 1);
326 token_info->repo_id = g_strdup (repo_id);
327 token_info->expire_time = (gint64)time(NULL) + TOKEN_EXPIRE_TIME;
328 token_info->email = email;
329
330 pthread_mutex_lock (&htp_server->token_cache_lock);
331 g_hash_table_insert (htp_server->token_cache, g_strdup (token), token_info);
332 pthread_mutex_unlock (&htp_server->token_cache_lock);
333
334 if (username)
335 *username = g_strdup(email);
336 return EVHTP_RES_OK;
337 }
338
339 static PermInfo *
lookup_perm_cache(HttpServer * htp_server,const char * repo_id,const char * username)340 lookup_perm_cache (HttpServer *htp_server, const char *repo_id, const char *username)
341 {
342 PermInfo *ret = NULL;
343 char *key = g_strdup_printf ("%s:%s", repo_id, username);
344
345 pthread_mutex_lock (&htp_server->perm_cache_lock);
346 ret = g_hash_table_lookup (htp_server->perm_cache, key);
347 pthread_mutex_unlock (&htp_server->perm_cache_lock);
348 g_free (key);
349
350 return ret;
351 }
352
353 static void
insert_perm_cache(HttpServer * htp_server,const char * repo_id,const char * username,PermInfo * perm)354 insert_perm_cache (HttpServer *htp_server,
355 const char *repo_id, const char *username,
356 PermInfo *perm)
357 {
358 char *key = g_strdup_printf ("%s:%s", repo_id, username);
359
360 pthread_mutex_lock (&htp_server->perm_cache_lock);
361 g_hash_table_insert (htp_server->perm_cache, key, perm);
362 pthread_mutex_unlock (&htp_server->perm_cache_lock);
363 }
364
365 static void
remove_perm_cache(HttpServer * htp_server,const char * repo_id,const char * username)366 remove_perm_cache (HttpServer *htp_server,
367 const char *repo_id, const char *username)
368 {
369 char *key = g_strdup_printf ("%s:%s", repo_id, username);
370
371 pthread_mutex_lock (&htp_server->perm_cache_lock);
372 g_hash_table_remove (htp_server->perm_cache, key);
373 pthread_mutex_unlock (&htp_server->perm_cache_lock);
374
375 g_free (key);
376 }
377
378 static int
check_permission(HttpServer * htp_server,const char * repo_id,const char * username,const char * op,gboolean skip_cache)379 check_permission (HttpServer *htp_server, const char *repo_id, const char *username,
380 const char *op, gboolean skip_cache)
381 {
382 PermInfo *perm_info = NULL;
383
384 if (!skip_cache)
385 perm_info = lookup_perm_cache (htp_server, repo_id, username);
386
387 if (perm_info) {
388 if (strcmp(perm_info->perm, "r") == 0 && strcmp(op, "upload") == 0)
389 return EVHTP_RES_FORBIDDEN;
390 return EVHTP_RES_OK;
391 }
392
393 if (strcmp(op, "upload") == 0) {
394 int status = seaf_repo_manager_get_repo_status(seaf->repo_mgr, repo_id);
395 if (status != REPO_STATUS_NORMAL && status != -1)
396 return EVHTP_RES_FORBIDDEN;
397 }
398
399 char *perm = seaf_repo_manager_check_permission (seaf->repo_mgr,
400 repo_id, username, NULL);
401 if (perm) {
402 perm_info = g_new0 (PermInfo, 1);
403 /* Take the reference of perm. */
404 perm_info->perm = perm;
405 perm_info->expire_time = (gint64)time(NULL) + PERM_EXPIRE_TIME;
406 insert_perm_cache (htp_server, repo_id, username, perm_info);
407
408 if ((strcmp (perm, "r") == 0 && strcmp (op, "upload") == 0))
409 return EVHTP_RES_FORBIDDEN;
410 return EVHTP_RES_OK;
411 }
412
413 /* Invalidate cache if perm not found in db. */
414 remove_perm_cache (htp_server, repo_id, username);
415 return EVHTP_RES_FORBIDDEN;
416 }
417
418 static gboolean
get_vir_repo_info(SeafDBRow * row,void * data)419 get_vir_repo_info (SeafDBRow *row, void *data)
420 {
421 const char *repo_id = seaf_db_row_get_column_text (row, 0);
422 if (!repo_id)
423 return FALSE;
424 const char *origin_id = seaf_db_row_get_column_text (row, 1);
425 if (!origin_id)
426 return FALSE;
427
428 VirRepoInfo **vinfo = data;
429 *vinfo = g_new0 (VirRepoInfo, 1);
430 if (!*vinfo)
431 return FALSE;
432 (*vinfo)->store_id = g_strdup (origin_id);
433 if (!(*vinfo)->store_id)
434 return FALSE;
435 (*vinfo)->expire_time = time (NULL) + VIRINFO_EXPIRE_TIME;
436
437 return TRUE;
438 }
439
440 static char *
get_store_id_from_vir_repo_info_cache(HttpServer * htp_server,const char * repo_id)441 get_store_id_from_vir_repo_info_cache (HttpServer *htp_server, const char *repo_id)
442 {
443 char *store_id = NULL;
444 VirRepoInfo *vinfo = NULL;
445
446 pthread_mutex_lock (&htp_server->vir_repo_info_cache_lock);
447 vinfo = g_hash_table_lookup (htp_server->vir_repo_info_cache, repo_id);
448
449 if (vinfo) {
450 if (vinfo->store_id)
451 store_id = g_strdup (vinfo->store_id);
452 else
453 store_id = g_strdup (repo_id);
454
455 vinfo->expire_time = time (NULL) + VIRINFO_EXPIRE_TIME;
456 }
457
458 pthread_mutex_unlock (&htp_server->vir_repo_info_cache_lock);
459
460 return store_id;
461 }
462
463 static void
add_vir_info_to_cache(HttpServer * htp_server,const char * repo_id,VirRepoInfo * vinfo)464 add_vir_info_to_cache (HttpServer *htp_server, const char *repo_id,
465 VirRepoInfo *vinfo)
466 {
467 pthread_mutex_lock (&htp_server->vir_repo_info_cache_lock);
468 g_hash_table_insert (htp_server->vir_repo_info_cache, g_strdup (repo_id), vinfo);
469 pthread_mutex_unlock (&htp_server->vir_repo_info_cache_lock);
470 }
471
472 static char *
get_repo_store_id(HttpServer * htp_server,const char * repo_id)473 get_repo_store_id (HttpServer *htp_server, const char *repo_id)
474 {
475 char *store_id = get_store_id_from_vir_repo_info_cache (htp_server,
476 repo_id);
477 if (store_id) {
478 return store_id;
479 }
480
481 VirRepoInfo *vinfo = NULL;
482 char *sql = "SELECT repo_id, origin_repo FROM VirtualRepo where repo_id = ?";
483 int n_row = seaf_db_statement_foreach_row (seaf->db, sql, get_vir_repo_info,
484 &vinfo, 1, "string", repo_id);
485 if (n_row < 0) {
486 // db error, return NULL
487 return NULL;
488 } else if (n_row == 0) {
489 // repo is not virtual repo
490 vinfo = g_new0 (VirRepoInfo, 1);
491 if (!vinfo)
492 return NULL;
493 vinfo->expire_time = time (NULL) + VIRINFO_EXPIRE_TIME;
494
495 add_vir_info_to_cache (htp_server, repo_id, vinfo);
496
497 return g_strdup (repo_id);
498 } else if (!vinfo || !vinfo->store_id) {
499 // out of memory, return NULL
500 return NULL;
501 }
502
503 add_vir_info_to_cache (htp_server, repo_id, vinfo);
504
505 return g_strdup (vinfo->store_id);
506 }
507
508 typedef struct {
509 char *etype;
510 char *user;
511 char *ip;
512 char repo_id[37];
513 char *path;
514 char *client_name;
515 } RepoEventData;
516
517
518 static void
free_repo_event_data(RepoEventData * data)519 free_repo_event_data (RepoEventData *data)
520 {
521 if (!data)
522 return;
523
524 g_free (data->etype);
525 g_free (data->user);
526 g_free (data->ip);
527 g_free (data->path);
528 g_free (data->client_name);
529 g_free (data);
530 }
531
532 static void
free_stats_event_data(StatsEventData * data)533 free_stats_event_data (StatsEventData *data)
534 {
535 if (!data)
536 return;
537
538 g_free (data->etype);
539 g_free (data->user);
540 g_free (data->operation);
541 g_free (data);
542 }
543
544 static void
publish_repo_event(RepoEventData * rdata)545 publish_repo_event (RepoEventData *rdata)
546 {
547 GString *buf = g_string_new (NULL);
548 g_string_printf (buf, "%s\t%s\t%s\t%s\t%s\t%s",
549 rdata->etype, rdata->user, rdata->ip,
550 rdata->client_name ? rdata->client_name : "",
551 rdata->repo_id, rdata->path ? rdata->path : "/");
552
553 seaf_mq_manager_publish_event (seaf->mq_mgr, SEAFILE_SERVER_CHANNEL_EVENT, buf->str);
554
555 g_string_free (buf, TRUE);
556 }
557
558 static void
publish_stats_event(StatsEventData * rdata)559 publish_stats_event (StatsEventData *rdata)
560 {
561 GString *buf = g_string_new (NULL);
562 g_string_printf (buf, "%s\t%s\t%s\t%"G_GUINT64_FORMAT,
563 rdata->etype, rdata->user,
564 rdata->repo_id, rdata->bytes);
565
566 seaf_mq_manager_publish_event (seaf->mq_mgr, SEAFILE_SERVER_CHANNEL_STATS, buf->str);
567
568 g_string_free (buf, TRUE);
569 }
570
571 static void
on_repo_oper(HttpServer * htp_server,const char * etype,const char * repo_id,char * user,char * ip,char * client_name)572 on_repo_oper (HttpServer *htp_server, const char *etype,
573 const char *repo_id, char *user, char *ip, char *client_name)
574 {
575 RepoEventData *rdata = g_new0 (RepoEventData, 1);
576 SeafVirtRepo *vinfo = seaf_repo_manager_get_virtual_repo_info (seaf->repo_mgr,
577 repo_id);
578
579 if (vinfo) {
580 memcpy (rdata->repo_id, vinfo->origin_repo_id, 36);
581 rdata->path = g_strdup(vinfo->path);
582 } else
583 memcpy (rdata->repo_id, repo_id, 36);
584 rdata->etype = g_strdup (etype);
585 rdata->user = g_strdup (user);
586 rdata->ip = g_strdup (ip);
587 rdata->client_name = g_strdup(client_name);
588
589 publish_repo_event(rdata);
590 if (vinfo) {
591 g_free (vinfo->path);
592 g_free (vinfo);
593 }
594 free_repo_event_data (rdata);
595 return;
596 }
597
598 void
send_statistic_msg(const char * repo_id,char * user,char * operation,guint64 bytes)599 send_statistic_msg (const char *repo_id, char *user, char *operation, guint64 bytes)
600 {
601 StatsEventData *rdata = g_new0 (StatsEventData, 1);
602
603 memcpy (rdata->repo_id, repo_id, 36);
604 rdata->etype = g_strdup (operation);
605 rdata->user = g_strdup (user);
606 rdata->bytes = bytes;
607
608 publish_stats_event(rdata);
609
610 free_stats_event_data (rdata);
611 return;
612 }
613
614 char *
get_client_ip_addr(evhtp_request_t * req)615 get_client_ip_addr (evhtp_request_t *req)
616 {
617 const char *xff = evhtp_kv_find (req->headers_in, "X-Forwarded-For");
618 if (xff) {
619 struct in_addr addr;
620 const char *comma = strchr (xff, ',');
621 char *copy;
622 if (comma)
623 copy = g_strndup(xff, comma-xff);
624 else
625 copy = g_strdup(xff);
626 if (evutil_inet_pton (AF_INET, copy, &addr) == 1)
627 return copy;
628 g_free (copy);
629 }
630
631 evhtp_connection_t *conn = req->conn;
632 char ip_addr[17];
633 const char *ip = NULL;
634 struct sockaddr_in *addr_in = (struct sockaddr_in *)conn->saddr;
635
636 memset (ip_addr, '\0', 17);
637 ip = evutil_inet_ntop (AF_INET, &addr_in->sin_addr, ip_addr, 16);
638
639 return g_strdup (ip);
640 }
641
642 static int
validate_client_ver(const char * client_ver)643 validate_client_ver (const char *client_ver)
644 {
645 char **versions = NULL;
646 char *next_str = NULL;
647
648 versions = g_strsplit (client_ver, ".", 3);
649 if (g_strv_length (versions) != 3) {
650 g_strfreev (versions);
651 return EVHTP_RES_BADREQ;
652 }
653
654 strtoll (versions[0], &next_str, 10);
655 if (versions[0] == next_str) {
656 g_strfreev (versions);
657 return EVHTP_RES_BADREQ;
658 }
659
660 strtoll (versions[1], &next_str, 10);
661 if (versions[1] == next_str) {
662 g_strfreev (versions);
663 return EVHTP_RES_BADREQ;
664 }
665
666 strtoll (versions[2], &next_str, 10);
667 if (versions[2] == next_str) {
668 g_strfreev (versions);
669 return EVHTP_RES_BADREQ;
670 }
671
672 // todo: judge whether version is too old, then return 426
673
674 g_strfreev (versions);
675 return EVHTP_RES_OK;
676 }
677
678 static void
get_check_permission_cb(evhtp_request_t * req,void * arg)679 get_check_permission_cb (evhtp_request_t *req, void *arg)
680 {
681 const char *op = evhtp_kv_find (req->uri->query, "op");
682 if (op == NULL || (strcmp (op, "upload") != 0 && strcmp (op, "download") != 0)) {
683 evhtp_send_reply (req, EVHTP_RES_BADREQ);
684 return;
685 }
686
687 const char *client_id = evhtp_kv_find (req->uri->query, "client_id");
688 if (client_id && strlen(client_id) != 40) {
689 evhtp_send_reply (req, EVHTP_RES_BADREQ);
690 return;
691 }
692
693 const char *client_ver = evhtp_kv_find (req->uri->query, "client_ver");
694 if (client_ver) {
695 int status = validate_client_ver (client_ver);
696 if (status != EVHTP_RES_OK) {
697 evhtp_send_reply (req, status);
698 return;
699 }
700 }
701
702 char *client_name = NULL;
703 const char *client_name_in = evhtp_kv_find (req->uri->query, "client_name");
704 if (client_name_in)
705 client_name = g_uri_unescape_string (client_name_in, NULL);
706
707 char **parts = g_strsplit (req->uri->path->full + 1, "/", 0);
708 char *repo_id = parts[1];
709 HttpServer *htp_server = (HttpServer *)arg;
710 char *username = NULL;
711 char *ip = NULL;
712 const char *token;
713 SeafRepo *repo = NULL;
714
715 repo = seaf_repo_manager_get_repo_ex (seaf->repo_mgr, repo_id);
716 if (!repo) {
717 evhtp_send_reply (req, SEAF_HTTP_RES_REPO_DELETED);
718 goto out;
719 }
720 if (repo->is_corrupted || repo->repaired) {
721 evhtp_send_reply (req, SEAF_HTTP_RES_REPO_CORRUPTED);
722 goto out;
723 }
724
725 int token_status = validate_token (htp_server, req, repo_id, &username, TRUE);
726 if (token_status != EVHTP_RES_OK) {
727 evhtp_send_reply (req, token_status);
728 goto out;
729 }
730
731 /* We shall actually check the permission from database, don't rely on
732 * the cache here.
733 */
734 int perm_status = check_permission (htp_server, repo_id, username, op, TRUE);
735 if (perm_status == EVHTP_RES_FORBIDDEN) {
736 evhtp_send_reply (req, EVHTP_RES_FORBIDDEN);
737 goto out;
738 }
739
740 ip = get_client_ip_addr (req);
741 if (!ip) {
742 evhtp_send_reply (req, EVHTP_RES_SERVERR);
743 token = evhtp_kv_find (req->headers_in, "Seafile-Repo-Token");
744 seaf_warning ("[%s] Failed to get client ip.\n", token);
745 goto out;
746 }
747
748 if (strcmp (op, "download") == 0) {
749 on_repo_oper (htp_server, "repo-download-sync", repo_id, username, ip, client_name);
750 }
751 /* else if (strcmp (op, "upload") == 0) { */
752 /* on_repo_oper (htp_server, "repo-upload-sync", repo_id, username, ip, client_name); */
753 /* } */
754
755 if (client_id && client_name) {
756 token = evhtp_kv_find (req->headers_in, "Seafile-Repo-Token");
757
758 /* Record the (token, email, <peer info>) information, <peer info> may
759 * include peer_id, peer_ip, peer_name, etc.
760 */
761 if (!seaf_repo_manager_token_peer_info_exists (seaf->repo_mgr, token))
762 seaf_repo_manager_add_token_peer_info (seaf->repo_mgr,
763 token,
764 client_id,
765 ip,
766 client_name,
767 (gint64)time(NULL),
768 client_ver);
769 else
770 seaf_repo_manager_update_token_peer_info (seaf->repo_mgr,
771 token,
772 ip,
773 (gint64)time(NULL),
774 client_ver);
775 }
776
777 evhtp_send_reply (req, EVHTP_RES_OK);
778
779 out:
780 g_free (username);
781 g_strfreev (parts);
782 g_free (ip);
783 g_free (client_name);
784 if (repo) {
785 seaf_repo_unref (repo);
786 }
787 }
788
789 static void
get_protocol_cb(evhtp_request_t * req,void * arg)790 get_protocol_cb (evhtp_request_t *req, void *arg)
791 {
792 evbuffer_add (req->buffer_out, PROTO_VERSION, strlen (PROTO_VERSION));
793 evhtp_send_reply (req, EVHTP_RES_OK);
794 }
795
796 static void
get_check_quota_cb(evhtp_request_t * req,void * arg)797 get_check_quota_cb (evhtp_request_t *req, void *arg)
798 {
799 HttpServer *htp_server = arg;
800 char **parts = g_strsplit (req->uri->path->full + 1, "/", 0);
801 char *repo_id = parts[1];
802
803 int token_status = validate_token (htp_server, req, repo_id, NULL, FALSE);
804 if (token_status != EVHTP_RES_OK) {
805 evhtp_send_reply (req, token_status);
806 goto out;
807 }
808
809 const char *delta = evhtp_kv_find (req->uri->query, "delta");
810 if (delta == NULL) {
811 char *error = "Invalid delta parameter.\n";
812 seaf_warning ("%s", error);
813 evbuffer_add (req->buffer_out, error, strlen (error));
814 evhtp_send_reply (req, EVHTP_RES_BADREQ);
815 goto out;
816 }
817
818 char *next_ptr = NULL;
819 gint64 delta_num = strtoll(delta, &next_ptr, 10);
820 if (!(*delta != '\0' && *next_ptr == '\0')) {
821 char *error = "Invalid delta parameter.\n";
822 seaf_warning ("%s", error);
823 evbuffer_add (req->buffer_out, error, strlen (error));
824 evhtp_send_reply (req, EVHTP_RES_BADREQ);
825 goto out;
826 }
827
828 int ret = seaf_quota_manager_check_quota_with_delta (seaf->quota_mgr,
829 repo_id, delta_num);
830 if (ret < 0) {
831 evhtp_send_reply (req, EVHTP_RES_SERVERR);
832 } else if (ret == 0) {
833 evhtp_send_reply (req, EVHTP_RES_OK);
834 } else {
835 evhtp_send_reply (req, SEAF_HTTP_RES_NOQUOTA);
836 }
837
838 out:
839 g_strfreev (parts);
840 }
841
842 static gboolean
get_branch(SeafDBRow * row,void * vid)843 get_branch (SeafDBRow *row, void *vid)
844 {
845 char *ret = vid;
846 const char *commit_id;
847
848 commit_id = seaf_db_row_get_column_text (row, 0);
849 memcpy (ret, commit_id, 41);
850
851 return FALSE;
852 }
853
854 static void
get_head_commit_cb(evhtp_request_t * req,void * arg)855 get_head_commit_cb (evhtp_request_t *req, void *arg)
856 {
857 HttpServer *htp_server = arg;
858 char **parts = g_strsplit (req->uri->path->full + 1, "/", 0);
859 char *repo_id = parts[1];
860 gboolean db_err = FALSE, exists = TRUE;
861 int token_status;
862 char commit_id[41];
863 char *sql;
864
865 sql = "SELECT 1 FROM Repo WHERE repo_id=?";
866 exists = seaf_db_statement_exists (seaf->db, sql, &db_err, 1, "string", repo_id);
867 if (!exists) {
868 if (db_err) {
869 seaf_warning ("DB error when check repo existence.\n");
870 evbuffer_add_printf (req->buffer_out,
871 "{\"is_corrupted\": 1}");
872 evhtp_send_reply (req, EVHTP_RES_OK);
873 goto out;
874 }
875 evhtp_send_reply (req, SEAF_HTTP_RES_REPO_DELETED);
876 goto out;
877 }
878
879 token_status = validate_token (htp_server, req, repo_id, NULL, FALSE);
880 if (token_status != EVHTP_RES_OK) {
881 evhtp_send_reply (req, token_status);
882 goto out;
883 }
884
885 commit_id[0] = 0;
886
887 sql = "SELECT commit_id FROM Branch WHERE name='master' AND repo_id=?";
888 if (seaf_db_statement_foreach_row (seaf->db, sql,
889 get_branch, commit_id,
890 1, "string", repo_id) < 0) {
891 seaf_warning ("DB error when get branch master.\n");
892 evbuffer_add_printf (req->buffer_out,
893 "{\"is_corrupted\": 1}");
894 evhtp_send_reply (req, EVHTP_RES_OK);
895 goto out;
896 }
897
898 if (commit_id[0] == 0) {
899 evhtp_send_reply (req, SEAF_HTTP_RES_REPO_DELETED);
900 goto out;
901 }
902
903 evbuffer_add_printf (req->buffer_out,
904 "{\"is_corrupted\": 0, \"head_commit_id\": \"%s\"}",
905 commit_id);
906 evhtp_send_reply (req, EVHTP_RES_OK);
907
908 out:
909 g_strfreev (parts);
910 }
911
912 static char *
gen_merge_description(SeafRepo * repo,const char * merged_root,const char * p1_root,const char * p2_root)913 gen_merge_description (SeafRepo *repo,
914 const char *merged_root,
915 const char *p1_root,
916 const char *p2_root)
917 {
918 GList *p;
919 GList *results = NULL;
920 char *desc;
921
922 diff_merge_roots (repo->store_id, repo->version,
923 merged_root, p1_root, p2_root, &results, TRUE);
924
925 desc = diff_results_to_description (results);
926
927 for (p = results; p; p = p->next) {
928 DiffEntry *de = p->data;
929 diff_entry_free (de);
930 }
931 g_list_free (results);
932
933 return desc;
934 }
935
936 static int
fast_forward_or_merge(const char * repo_id,SeafCommit * base,SeafCommit * new_commit)937 fast_forward_or_merge (const char *repo_id,
938 SeafCommit *base,
939 SeafCommit *new_commit)
940 {
941 #define MAX_RETRY_COUNT 3
942
943 SeafRepo *repo = NULL;
944 SeafCommit *current_head = NULL, *merged_commit = NULL;
945 int retry_cnt = 0;
946 int ret = 0;
947
948 repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
949 if (!repo) {
950 seaf_warning ("Repo %s doesn't exist.\n", repo_id);
951 ret = -1;
952 goto out;
953 }
954
955 retry:
956 current_head = seaf_commit_manager_get_commit (seaf->commit_mgr,
957 repo->id, repo->version,
958 repo->head->commit_id);
959 if (!current_head) {
960 seaf_warning ("Failed to find head commit of %s.\n", repo_id);
961 ret = -1;
962 goto out;
963 }
964
965 /* Merge if base and head are not the same. */
966 if (strcmp (base->commit_id, current_head->commit_id) != 0) {
967 MergeOptions opt;
968 const char *roots[3];
969 char *desc = NULL;
970
971 memset (&opt, 0, sizeof(opt));
972 opt.n_ways = 3;
973 memcpy (opt.remote_repo_id, repo_id, 36);
974 memcpy (opt.remote_head, new_commit->commit_id, 40);
975 opt.do_merge = TRUE;
976
977 roots[0] = base->root_id; /* base */
978 roots[1] = current_head->root_id; /* head */
979 roots[2] = new_commit->root_id; /* remote */
980
981 if (seaf_merge_trees (repo->store_id, repo->version, 3, roots, &opt) < 0) {
982 seaf_warning ("Failed to merge.\n");
983 ret = -1;
984 goto out;
985 }
986
987 if (!opt.conflict)
988 desc = g_strdup("Auto merge by system");
989 else {
990 desc = gen_merge_description (repo,
991 opt.merged_tree_root,
992 current_head->root_id,
993 new_commit->root_id);
994 if (!desc)
995 desc = g_strdup("Auto merge by system");
996 }
997
998 merged_commit = seaf_commit_new(NULL, repo->id, opt.merged_tree_root,
999 new_commit->creator_name, EMPTY_SHA1,
1000 desc,
1001 0);
1002 g_free (desc);
1003
1004 merged_commit->parent_id = g_strdup (current_head->commit_id);
1005 merged_commit->second_parent_id = g_strdup (new_commit->commit_id);
1006 merged_commit->new_merge = TRUE;
1007 if (opt.conflict)
1008 merged_commit->conflict = TRUE;
1009 seaf_repo_to_commit (repo, merged_commit);
1010
1011 if (seaf_commit_manager_add_commit (seaf->commit_mgr, merged_commit) < 0) {
1012 seaf_warning ("Failed to add commit.\n");
1013 ret = -1;
1014 goto out;
1015 }
1016 } else {
1017 seaf_commit_ref (new_commit);
1018 merged_commit = new_commit;
1019 }
1020
1021 seaf_branch_set_commit(repo->head, merged_commit->commit_id);
1022
1023 if (seaf_branch_manager_test_and_update_branch(seaf->branch_mgr,
1024 repo->head,
1025 current_head->commit_id) < 0)
1026 {
1027 seaf_repo_unref (repo);
1028 repo = NULL;
1029 seaf_commit_unref (current_head);
1030 current_head = NULL;
1031 seaf_commit_unref (merged_commit);
1032 merged_commit = NULL;
1033
1034 if (++retry_cnt <= MAX_RETRY_COUNT) {
1035 /* Sleep random time between 100 and 1000 millisecs. */
1036 usleep (g_random_int_range(1, 11) * 100 * 1000);
1037
1038 repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
1039 if (!repo) {
1040 seaf_warning ("Repo %s doesn't exist.\n", repo_id);
1041 ret = -1;
1042 goto out;
1043 }
1044
1045 goto retry;
1046 } else {
1047 ret = -1;
1048 goto out;
1049 }
1050 }
1051
1052 out:
1053 seaf_commit_unref (current_head);
1054 seaf_commit_unref (merged_commit);
1055 seaf_repo_unref (repo);
1056 return ret;
1057 }
1058
1059 static void
put_update_branch_cb(evhtp_request_t * req,void * arg)1060 put_update_branch_cb (evhtp_request_t *req, void *arg)
1061 {
1062 HttpServer *htp_server = arg;
1063 char **parts;
1064 char *repo_id;
1065 char *username = NULL;
1066 SeafRepo *repo = NULL;
1067 SeafCommit *new_commit = NULL, *base = NULL;
1068
1069 const char *new_commit_id = evhtp_kv_find (req->uri->query, "head");
1070 if (new_commit_id == NULL || !is_object_id_valid (new_commit_id)) {
1071 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1072 return;
1073 }
1074
1075 parts = g_strsplit (req->uri->path->full + 1, "/", 0);
1076 repo_id = parts[1];
1077
1078 int token_status = validate_token (htp_server, req, repo_id, &username, FALSE);
1079 if (token_status != EVHTP_RES_OK) {
1080 evhtp_send_reply (req, token_status);
1081 goto out;
1082 }
1083
1084 int perm_status = check_permission (htp_server, repo_id, username,
1085 "upload", FALSE);
1086 if (perm_status == EVHTP_RES_FORBIDDEN) {
1087 evhtp_send_reply (req, EVHTP_RES_FORBIDDEN);
1088 goto out;
1089 }
1090
1091 repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
1092 if (!repo) {
1093 seaf_warning ("Repo %s is missing or corrupted.\n", repo_id);
1094 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1095 goto out;
1096 }
1097
1098 /* Since this is the last step of upload procedure, commit should exist. */
1099 new_commit = seaf_commit_manager_get_commit (seaf->commit_mgr,
1100 repo->id, repo->version,
1101 new_commit_id);
1102 if (!new_commit) {
1103 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1104 goto out;
1105 }
1106
1107 base = seaf_commit_manager_get_commit (seaf->commit_mgr,
1108 repo->id, repo->version,
1109 new_commit->parent_id);
1110 if (!base) {
1111 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1112 goto out;
1113 }
1114
1115 if (seaf_quota_manager_check_quota (seaf->quota_mgr, repo_id) < 0) {
1116 evhtp_send_reply (req, SEAF_HTTP_RES_NOQUOTA);
1117 goto out;
1118 }
1119
1120 if (fast_forward_or_merge (repo_id, base, new_commit) < 0) {
1121 seaf_warning ("Fast forward merge for repo %s is failed.\n", repo_id);
1122 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1123 goto out;
1124 }
1125
1126 seaf_repo_manager_merge_virtual_repo (seaf->repo_mgr, repo_id, NULL);
1127
1128 schedule_repo_size_computation (seaf->size_sched, repo_id);
1129
1130 evhtp_send_reply (req, EVHTP_RES_OK);
1131
1132 out:
1133 seaf_repo_unref (repo);
1134 seaf_commit_unref (new_commit);
1135 seaf_commit_unref (base);
1136 g_free (username);
1137 g_strfreev (parts);
1138 }
1139
1140 static void
head_commit_oper_cb(evhtp_request_t * req,void * arg)1141 head_commit_oper_cb (evhtp_request_t *req, void *arg)
1142 {
1143 htp_method req_method = evhtp_request_get_method (req);
1144
1145 if (req_method == htp_method_GET) {
1146 get_head_commit_cb (req, arg);
1147 } else if (req_method == htp_method_PUT) {
1148 put_update_branch_cb (req, arg);
1149 }
1150 }
1151
1152 static gboolean
collect_head_commit_ids(SeafDBRow * row,void * data)1153 collect_head_commit_ids (SeafDBRow *row, void *data)
1154 {
1155 json_t *map = (json_t *)data;
1156 const char *repo_id = seaf_db_row_get_column_text (row, 0);
1157 const char *commit_id = seaf_db_row_get_column_text (row, 1);
1158
1159 json_object_set_new (map, repo_id, json_string(commit_id));
1160
1161 return TRUE;
1162 }
1163
1164 static void
head_commits_multi_cb(evhtp_request_t * req,void * arg)1165 head_commits_multi_cb (evhtp_request_t *req, void *arg)
1166 {
1167 size_t list_len;
1168 json_t *repo_id_array = NULL;
1169 size_t n, i;
1170 GString *id_list_str = NULL;
1171 char *sql = NULL;
1172 json_t *commit_id_map = NULL;
1173 char *data = NULL;
1174
1175 list_len = evbuffer_get_length (req->buffer_in);
1176 if (list_len == 0) {
1177 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1178 goto out;
1179 }
1180
1181 char *repo_id_list_con = g_new0 (char, list_len);
1182 if (!repo_id_list_con) {
1183 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1184 seaf_warning ("Failed to allocate %lu bytes memory.\n", list_len);
1185 goto out;
1186 }
1187
1188 json_error_t jerror;
1189 evbuffer_remove (req->buffer_in, repo_id_list_con, list_len);
1190 repo_id_array = json_loadb (repo_id_list_con, list_len, 0, &jerror);
1191 g_free (repo_id_list_con);
1192
1193 if (!repo_id_array) {
1194 seaf_warning ("load repo_id_list to json failed, error: %s\n", jerror.text);
1195 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1196 goto out;
1197 }
1198
1199 n = json_array_size (repo_id_array);
1200 if (n == 0) {
1201 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1202 goto out;
1203 }
1204
1205 json_t *id;
1206 id_list_str = g_string_new ("");
1207 for (i = 0; i < n; ++i) {
1208 id = json_array_get (repo_id_array, i);
1209 if (json_typeof(id) != JSON_STRING) {
1210 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1211 goto out;
1212 }
1213 /* Make sure ids are in UUID format. */
1214 if (!is_uuid_valid (json_string_value (id))) {
1215 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1216 goto out;
1217 }
1218 if (i == 0)
1219 g_string_append_printf (id_list_str, "'%s'", json_string_value(id));
1220 else
1221 g_string_append_printf (id_list_str, ",'%s'", json_string_value(id));
1222 }
1223
1224 if (seaf_db_type (seaf->db) == SEAF_DB_TYPE_MYSQL)
1225 sql = g_strdup_printf ("SELECT repo_id, commit_id FROM Branch WHERE name='master' AND repo_id IN (%s) LOCK IN SHARE MODE",
1226 id_list_str->str);
1227 else
1228 sql = g_strdup_printf ("SELECT repo_id, commit_id FROM Branch WHERE name='master' AND repo_id IN (%s)",
1229 id_list_str->str);
1230 commit_id_map = json_object();
1231 if (seaf_db_statement_foreach_row (seaf->db, sql,
1232 collect_head_commit_ids, commit_id_map, 0) < 0) {
1233 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1234 goto out;
1235 }
1236
1237 data = json_dumps (commit_id_map, JSON_COMPACT);
1238 if (!data) {
1239 seaf_warning ("failed to dump json.\n");
1240 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1241 goto out;
1242 }
1243
1244 evbuffer_add (req->buffer_out, data, strlen(data));
1245 evhtp_send_reply (req, EVHTP_RES_OK);
1246
1247 out:
1248 if (repo_id_array)
1249 json_decref (repo_id_array);
1250 if (id_list_str)
1251 g_string_free (id_list_str, TRUE);
1252 g_free (sql);
1253 if (commit_id_map)
1254 json_decref (commit_id_map);
1255 if (data)
1256 free (data);
1257 }
1258
1259 static void
get_commit_info_cb(evhtp_request_t * req,void * arg)1260 get_commit_info_cb (evhtp_request_t *req, void *arg)
1261 {
1262 HttpServer *htp_server = arg;
1263 char **parts = g_strsplit (req->uri->path->full + 1, "/", 0);
1264 char *repo_id = parts[1];
1265 char *commit_id = parts[3];
1266
1267 int token_status = validate_token (htp_server, req, repo_id, NULL, FALSE);
1268 if (token_status != EVHTP_RES_OK) {
1269 evhtp_send_reply (req, token_status);
1270 goto out;
1271 }
1272
1273 char *data = NULL;
1274 int len;
1275
1276 int ret = seaf_obj_store_read_obj (seaf->commit_mgr->obj_store, repo_id, 1,
1277 commit_id, (void **)&data, &len);
1278 if (ret < 0) {
1279 seaf_warning ("Get commit info failed: commit %s is missing.\n", commit_id);
1280 evhtp_send_reply (req, EVHTP_RES_NOTFOUND);
1281 goto out;
1282 }
1283
1284 evbuffer_add (req->buffer_out, data, len);
1285 evhtp_send_reply (req, EVHTP_RES_OK);
1286 g_free (data);
1287
1288 out:
1289 g_strfreev (parts);
1290 }
1291
1292 static void
put_commit_cb(evhtp_request_t * req,void * arg)1293 put_commit_cb (evhtp_request_t *req, void *arg)
1294 {
1295 HttpServer *htp_server = arg;
1296 char **parts = g_strsplit (req->uri->path->full + 1, "/", 0);
1297 char *repo_id = parts[1];
1298 char *commit_id = parts[3];
1299 char *username = NULL;
1300 void *data = NULL;
1301
1302 int token_status = validate_token (htp_server, req, repo_id, &username, FALSE);
1303 if (token_status != EVHTP_RES_OK) {
1304 evhtp_send_reply (req, token_status);
1305 goto out;
1306 }
1307
1308 int perm_status = check_permission (htp_server, repo_id, username,
1309 "upload", FALSE);
1310 if (perm_status == EVHTP_RES_FORBIDDEN) {
1311 evhtp_send_reply (req, EVHTP_RES_FORBIDDEN);
1312 goto out;
1313 }
1314
1315 int con_len = evbuffer_get_length (req->buffer_in);
1316 if(con_len == 0) {
1317 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1318 goto out;
1319 }
1320
1321 data = g_new0 (char, con_len);
1322 if (!data) {
1323 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1324 seaf_warning ("Failed to allocate %d bytes memory.\n", con_len);
1325 goto out;
1326 }
1327
1328 evbuffer_remove (req->buffer_in, data, con_len);
1329 SeafCommit *commit = seaf_commit_from_data (commit_id, (char *)data, con_len);
1330 if (!commit) {
1331 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1332 goto out;
1333 }
1334
1335 if (strcmp (commit->repo_id, repo_id) != 0) {
1336 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1337 goto out;
1338 }
1339
1340 if (seaf_commit_manager_add_commit (seaf->commit_mgr, commit) < 0) {
1341 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1342 } else {
1343 evhtp_send_reply (req, EVHTP_RES_OK);
1344 }
1345 seaf_commit_unref (commit);
1346
1347 out:
1348 g_free (username);
1349 g_free (data);
1350 g_strfreev (parts);
1351 }
1352
1353 static void
commit_oper_cb(evhtp_request_t * req,void * arg)1354 commit_oper_cb (evhtp_request_t *req, void *arg)
1355 {
1356 htp_method req_method = evhtp_request_get_method (req);
1357
1358 if (req_method == htp_method_PUT) {
1359 put_commit_cb (req, arg);
1360 } else if (req_method == htp_method_GET) {
1361 get_commit_info_cb (req, arg);
1362 }
1363 }
1364
1365 static int
collect_file_ids(int n,const char * basedir,SeafDirent * files[],void * data)1366 collect_file_ids (int n, const char *basedir, SeafDirent *files[], void *data)
1367 {
1368 SeafDirent *file1 = files[0];
1369 SeafDirent *file2 = files[1];
1370 GList **pret = data;
1371
1372 if (file1 && (!file2 || strcmp(file1->id, file2->id) != 0) &&
1373 strcmp (file1->id, EMPTY_SHA1) != 0)
1374 *pret = g_list_prepend (*pret, g_strdup(file1->id));
1375
1376 return 0;
1377 }
1378
1379 static int
collect_file_ids_nop(int n,const char * basedir,SeafDirent * files[],void * data)1380 collect_file_ids_nop (int n, const char *basedir, SeafDirent *files[], void *data)
1381 {
1382 return 0;
1383 }
1384
1385 static int
collect_dir_ids(int n,const char * basedir,SeafDirent * dirs[],void * data,gboolean * recurse)1386 collect_dir_ids (int n, const char *basedir, SeafDirent *dirs[], void *data,
1387 gboolean *recurse)
1388 {
1389 SeafDirent *dir1 = dirs[0];
1390 SeafDirent *dir2 = dirs[1];
1391 GList **pret = data;
1392
1393 if (dir1 && (!dir2 || strcmp(dir1->id, dir2->id) != 0) &&
1394 strcmp (dir1->id, EMPTY_SHA1) != 0)
1395 *pret = g_list_prepend (*pret, g_strdup(dir1->id));
1396
1397 return 0;
1398 }
1399
1400 static int
calculate_send_object_list(SeafRepo * repo,const char * server_head,const char * client_head,gboolean dir_only,GList ** results)1401 calculate_send_object_list (SeafRepo *repo,
1402 const char *server_head,
1403 const char *client_head,
1404 gboolean dir_only,
1405 GList **results)
1406 {
1407 SeafCommit *remote_head = NULL, *master_head = NULL;
1408 char *remote_head_root;
1409 int ret = 0;
1410
1411 *results = NULL;
1412
1413 master_head = seaf_commit_manager_get_commit (seaf->commit_mgr,
1414 repo->id, repo->version,
1415 server_head);
1416 if (!master_head) {
1417 seaf_warning ("Server head commit %s:%s not found.\n", repo->id, server_head);
1418 return -1;
1419 }
1420
1421 if (client_head) {
1422 remote_head = seaf_commit_manager_get_commit (seaf->commit_mgr,
1423 repo->id, repo->version,
1424 client_head);
1425 if (!remote_head) {
1426 ret = -1;
1427 goto out;
1428 }
1429 remote_head_root = remote_head->root_id;
1430 } else
1431 remote_head_root = EMPTY_SHA1;
1432
1433 /* Diff won't traverse the root object itself. */
1434 if (strcmp (remote_head_root, master_head->root_id) != 0 &&
1435 strcmp (master_head->root_id, EMPTY_SHA1) != 0)
1436 *results = g_list_prepend (*results, g_strdup(master_head->root_id));
1437
1438 DiffOptions opts;
1439 memset (&opts, 0, sizeof(opts));
1440 memcpy (opts.store_id, repo->store_id, 36);
1441 opts.version = repo->version;
1442 if (!dir_only)
1443 opts.file_cb = collect_file_ids;
1444 else
1445 opts.file_cb = collect_file_ids_nop;
1446 opts.dir_cb = collect_dir_ids;
1447 opts.data = results;
1448
1449 const char *trees[2];
1450 trees[0] = master_head->root_id;
1451 trees[1] = remote_head_root;
1452 if (diff_trees (2, trees, &opts) < 0) {
1453 seaf_warning ("Failed to diff remote and master head for repo %.8s.\n",
1454 repo->id);
1455 string_list_free (*results);
1456 ret = -1;
1457 }
1458
1459 out:
1460 seaf_commit_unref (remote_head);
1461 seaf_commit_unref (master_head);
1462 return ret;
1463 }
1464
1465 static void
get_fs_obj_id_cb(evhtp_request_t * req,void * arg)1466 get_fs_obj_id_cb (evhtp_request_t *req, void *arg)
1467 {
1468 HttpServer *htp_server = arg;
1469 char **parts;
1470 char *repo_id;
1471 SeafRepo *repo = NULL;
1472 gboolean dir_only = FALSE;
1473
1474 const char *server_head = evhtp_kv_find (req->uri->query, "server-head");
1475 if (server_head == NULL || !is_object_id_valid (server_head)) {
1476 char *error = "Invalid server-head parameter.\n";
1477 seaf_warning ("%s", error);
1478 evbuffer_add (req->buffer_out, error, strlen (error));
1479 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1480 return;
1481 }
1482
1483 const char *client_head = evhtp_kv_find (req->uri->query, "client-head");
1484 if (client_head && !is_object_id_valid (client_head)) {
1485 char *error = "Invalid client-head parameter.\n";
1486 seaf_warning ("%s", error);
1487 evbuffer_add (req->buffer_out, error, strlen (error));
1488 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1489 return;
1490 }
1491
1492 const char *dir_only_arg = evhtp_kv_find (req->uri->query, "dir-only");
1493 if (dir_only_arg)
1494 dir_only = TRUE;
1495
1496 parts = g_strsplit (req->uri->path->full + 1, "/", 0);
1497 repo_id = parts[1];
1498
1499 int token_status = validate_token (htp_server, req, repo_id, NULL, FALSE);
1500 if (token_status != EVHTP_RES_OK) {
1501 evhtp_send_reply (req, token_status);
1502 goto out;
1503 }
1504
1505 GList *list = NULL, *ptr;
1506
1507 repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
1508 if (!repo) {
1509 seaf_warning ("Failed to find repo %.8s.\n", repo_id);
1510 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1511 goto out;
1512 }
1513
1514 if (calculate_send_object_list (repo, server_head, client_head, dir_only, &list) < 0) {
1515 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1516 goto out;
1517 }
1518
1519 json_t *obj_array = json_array ();
1520
1521 for (ptr = list; ptr; ptr = ptr->next) {
1522 json_array_append_new (obj_array, json_string (ptr->data));
1523 g_free (ptr->data);
1524 }
1525 g_list_free (list);
1526
1527 char *obj_list = json_dumps (obj_array, JSON_COMPACT);
1528 evbuffer_add (req->buffer_out, obj_list, strlen (obj_list));
1529 evhtp_send_reply (req, EVHTP_RES_OK);
1530
1531 g_free (obj_list);
1532 json_decref (obj_array);
1533
1534 out:
1535 g_strfreev (parts);
1536 seaf_repo_unref (repo);
1537 }
1538
1539 typedef struct ComputeObjTask {
1540 HttpServer *htp_server;
1541 char *token;
1542 char *repo_id;
1543 char *client_head;
1544 char *server_head;
1545 gboolean dir_only;
1546 } ComputeObjTask;
1547
1548 typedef struct CalObjResult {
1549 GList *list;
1550 gboolean done;
1551 } CalObjResult;
1552
1553 static void
free_compute_obj_task(ComputeObjTask * task)1554 free_compute_obj_task(ComputeObjTask *task)
1555 {
1556 if (!task)
1557 return;
1558
1559 if (task->token)
1560 g_free(task->token);
1561 if (task->repo_id)
1562 g_free(task->repo_id);
1563 if (task->client_head)
1564 g_free(task->client_head);
1565 if (task->server_head)
1566 g_free(task->server_head);
1567 g_free(task);
1568 }
1569
1570 static void
free_obj_cal_result(gpointer data)1571 free_obj_cal_result (gpointer data)
1572 {
1573 CalObjResult *result = (CalObjResult *)data;
1574 if (!result)
1575 return;
1576
1577 if (result->list)
1578 g_list_free (result->list);
1579
1580 g_free(result);
1581 }
1582
1583 static void
compute_fs_obj_id(gpointer ptask,gpointer ppara)1584 compute_fs_obj_id (gpointer ptask, gpointer ppara)
1585 {
1586 SeafRepo *repo = NULL;
1587 ComputeObjTask *task = ptask;
1588 const char *client_head = task->client_head;
1589 const char *server_head = task->server_head;
1590 char *repo_id = task->repo_id;
1591 gboolean dir_only = task->dir_only;
1592 HttpServer *htp_server = task->htp_server;
1593 CalObjResult *result = NULL;
1594
1595 pthread_mutex_lock (&htp_server->fs_obj_ids_lock);
1596 result = g_hash_table_lookup (htp_server->fs_obj_ids, task->token);
1597 pthread_mutex_unlock (&htp_server->fs_obj_ids_lock);
1598 if (!result) {
1599 goto out;
1600 }
1601
1602 repo = seaf_repo_manager_get_repo (seaf->repo_mgr, repo_id);
1603 if (!repo) {
1604 seaf_warning ("Failed to find repo %.8s.\n", repo_id);
1605 goto out;
1606 }
1607
1608 if (calculate_send_object_list (repo, server_head, client_head, dir_only, &result->list) < 0) {
1609 pthread_mutex_lock (&htp_server->fs_obj_ids_lock);
1610 g_hash_table_remove (htp_server->fs_obj_ids, task->token);
1611 pthread_mutex_unlock (&htp_server->fs_obj_ids_lock);
1612 goto out;
1613 }
1614
1615 result->done = TRUE;
1616 out:
1617 seaf_repo_unref (repo);
1618 free_compute_obj_task(task);
1619 }
1620
1621 static void
start_fs_obj_id_cb(evhtp_request_t * req,void * arg)1622 start_fs_obj_id_cb (evhtp_request_t *req, void *arg)
1623 {
1624 HttpServer *htp_server = arg;
1625 char **parts;
1626 char *repo_id;
1627 gboolean dir_only = FALSE;
1628 json_t *obj;
1629
1630 const char *server_head = evhtp_kv_find (req->uri->query, "server-head");
1631 if (server_head == NULL || !is_object_id_valid (server_head)) {
1632 char *error = "Invalid server-head parameter.\n";
1633 evbuffer_add (req->buffer_out, error, strlen (error));
1634 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1635 return;
1636 }
1637
1638 const char *client_head = evhtp_kv_find (req->uri->query, "client-head");
1639 if (client_head && !is_object_id_valid (client_head)) {
1640 char *error = "Invalid client-head parameter.\n";
1641 evbuffer_add (req->buffer_out, error, strlen (error));
1642 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1643 return;
1644 }
1645
1646 const char *dir_only_arg = evhtp_kv_find (req->uri->query, "dir-only");
1647 if (dir_only_arg)
1648 dir_only = TRUE;
1649
1650 parts = g_strsplit (req->uri->path->full + 1, "/", 0);
1651 repo_id = parts[1];
1652
1653 int token_status = validate_token (htp_server, req, repo_id, NULL, FALSE);
1654 if (token_status != EVHTP_RES_OK) {
1655 evhtp_send_reply (req, token_status);
1656 goto out;
1657 }
1658
1659 char uuid[37];
1660 char *new_token;
1661 gen_uuid_inplace (uuid);
1662 new_token = g_strndup(uuid, FS_ID_LIST_TOKEN_LEN);
1663
1664 CalObjResult *result = g_new0(CalObjResult, 1);
1665 if (!result) {
1666 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1667 goto out;
1668 }
1669 result->done = FALSE;
1670
1671 ComputeObjTask *task = g_new0 (ComputeObjTask, 1);
1672 if (!task) {
1673 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1674 goto out;
1675 }
1676
1677 task->token = new_token;
1678 task->dir_only = dir_only;
1679 task->htp_server = htp_server;
1680 task->repo_id = g_strdup(repo_id);
1681 task->client_head = g_strdup(client_head);
1682 task->server_head = g_strdup(server_head);
1683
1684 pthread_mutex_lock (&htp_server->fs_obj_ids_lock);
1685 g_hash_table_insert (htp_server->fs_obj_ids, g_strdup(task->token), result);
1686 pthread_mutex_unlock (&htp_server->fs_obj_ids_lock);
1687 g_thread_pool_push (htp_server->compute_fs_obj_id_pool, task, NULL);
1688 obj = json_object ();
1689 json_object_set_new (obj, "token", json_string (new_token));
1690
1691 char *json_str = json_dumps (obj, JSON_COMPACT);
1692 evbuffer_add (req->buffer_out, json_str, strlen(json_str));
1693 evhtp_send_reply (req, EVHTP_RES_OK);
1694
1695 g_free (json_str);
1696 json_decref (obj);
1697 out:
1698 g_strfreev (parts);
1699 }
1700
1701 static void
query_fs_obj_id_cb(evhtp_request_t * req,void * arg)1702 query_fs_obj_id_cb (evhtp_request_t *req, void *arg)
1703 {
1704 json_t *obj;
1705 const char *token = NULL;
1706 CalObjResult *result = NULL;
1707 char **parts;
1708 char *repo_id = NULL;
1709 HttpServer *htp_server = (HttpServer *)arg;
1710
1711 parts = g_strsplit (req->uri->path->full + 1, "/", 0);
1712 repo_id = parts[1];
1713
1714 int token_status = validate_token (htp_server, req, repo_id, NULL, FALSE);
1715 if (token_status != EVHTP_RES_OK) {
1716 evhtp_send_reply (req, token_status);
1717 goto out;
1718 }
1719
1720 token = evhtp_kv_find (req->uri->query, "token");
1721 if (!token || strlen(token)!=FS_ID_LIST_TOKEN_LEN) {
1722 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1723 goto out;
1724 }
1725
1726 obj = json_object ();
1727
1728 pthread_mutex_lock (&htp_server->fs_obj_ids_lock);
1729 result = g_hash_table_lookup (htp_server->fs_obj_ids, token);
1730 if (!result) {
1731 pthread_mutex_unlock (&htp_server->fs_obj_ids_lock);
1732 evhtp_send_reply (req, EVHTP_RES_NOTFOUND);
1733 goto out;
1734 } else {
1735 if (!result->done) {
1736 json_object_set_new (obj, "success", json_false());
1737 } else {
1738 json_object_set_new (obj, "success", json_true());
1739 }
1740 }
1741 pthread_mutex_unlock (&htp_server->fs_obj_ids_lock);
1742
1743 json_object_set_new (obj, "token", json_string (token));
1744
1745 char *json_str = json_dumps (obj, JSON_COMPACT);
1746 evbuffer_add (req->buffer_out, json_str, strlen(json_str));
1747 evhtp_send_reply (req, EVHTP_RES_OK);
1748
1749 g_free (json_str);
1750
1751 out:
1752 if (obj)
1753 json_decref (obj);
1754 g_strfreev (parts);
1755 return;
1756 }
1757
1758 static void
retrieve_fs_obj_id_cb(evhtp_request_t * req,void * arg)1759 retrieve_fs_obj_id_cb (evhtp_request_t *req, void *arg)
1760 {
1761 char **parts;
1762 const char *token = NULL;
1763 char *repo_id = NULL;
1764 GList *list = NULL;
1765 CalObjResult *result = NULL;
1766 HttpServer *htp_server = (HttpServer *)arg;
1767
1768 parts = g_strsplit (req->uri->path->full + 1, "/", 0);
1769 repo_id = parts[1];
1770
1771 int token_status = validate_token (htp_server, req, repo_id, NULL, FALSE);
1772 if (token_status != EVHTP_RES_OK) {
1773 evhtp_send_reply (req, token_status);
1774 goto out;
1775 }
1776
1777 token = evhtp_kv_find (req->uri->query, "token");
1778 if (!token || strlen(token)!=FS_ID_LIST_TOKEN_LEN) {
1779 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1780 goto out;
1781 }
1782
1783 pthread_mutex_lock (&htp_server->fs_obj_ids_lock);
1784 result = g_hash_table_lookup (htp_server->fs_obj_ids, token);
1785 if (!result) {
1786 pthread_mutex_unlock (&htp_server->fs_obj_ids_lock);
1787 evhtp_send_reply (req, EVHTP_RES_NOTFOUND);
1788
1789 return;
1790 }
1791 if (!result->done) {
1792 pthread_mutex_unlock (&htp_server->fs_obj_ids_lock);
1793
1794 char *error = "The cauculation task is not completed.\n";
1795 evbuffer_add (req->buffer_out, error, strlen(error));
1796 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1797 return;
1798 }
1799 list = result->list;
1800 pthread_mutex_unlock (&htp_server->fs_obj_ids_lock);
1801
1802 GList *ptr;
1803 json_t *obj_array = json_array ();
1804
1805 for (ptr = list; ptr; ptr = ptr->next) {
1806 json_array_append_new (obj_array, json_string (ptr->data));
1807 g_free (ptr->data);
1808 }
1809
1810 pthread_mutex_lock (&htp_server->fs_obj_ids_lock);
1811 g_hash_table_remove (htp_server->fs_obj_ids, token);
1812 pthread_mutex_unlock (&htp_server->fs_obj_ids_lock);
1813
1814 char *obj_list = json_dumps (obj_array, JSON_COMPACT);
1815 evbuffer_add (req->buffer_out, obj_list, strlen (obj_list));
1816 evhtp_send_reply (req, EVHTP_RES_OK);
1817
1818 g_free (obj_list);
1819 json_decref (obj_array);
1820
1821 out:
1822 g_strfreev (parts);
1823 return;
1824 }
1825
1826 static void
get_block_cb(evhtp_request_t * req,void * arg)1827 get_block_cb (evhtp_request_t *req, void *arg)
1828 {
1829 const char *repo_id = NULL;
1830 char *block_id = NULL;
1831 char *store_id = NULL;
1832 HttpServer *htp_server = arg;
1833 BlockMetadata *blk_meta = NULL;
1834 char *username = NULL;
1835
1836 char **parts = g_strsplit (req->uri->path->full + 1, "/", 0);
1837 repo_id = parts[1];
1838 block_id = parts[3];
1839
1840 int token_status = validate_token (htp_server, req, repo_id, &username, FALSE);
1841 if (token_status != EVHTP_RES_OK) {
1842 evhtp_send_reply (req, token_status);
1843 goto out;
1844 }
1845
1846 store_id = get_repo_store_id (htp_server, repo_id);
1847 if (!store_id) {
1848 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1849 goto out;
1850 }
1851
1852 blk_meta = seaf_block_manager_stat_block (seaf->block_mgr,
1853 store_id, 1, block_id);
1854 if (blk_meta == NULL || blk_meta->size <= 0) {
1855 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1856 goto out;
1857 }
1858
1859 BlockHandle *blk_handle = NULL;
1860 blk_handle = seaf_block_manager_open_block(seaf->block_mgr,
1861 store_id, 1, block_id, BLOCK_READ);
1862 if (!blk_handle) {
1863 seaf_warning ("Failed to open block %.8s:%s.\n", store_id, block_id);
1864 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1865 goto out;
1866 }
1867
1868 void *block_con = g_new0 (char, blk_meta->size);
1869 if (!block_con) {
1870 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1871 seaf_warning ("Failed to allocate %d bytes memeory.\n", blk_meta->size);
1872 goto free_handle;
1873 }
1874
1875 int rsize = seaf_block_manager_read_block (seaf->block_mgr,
1876 blk_handle, block_con,
1877 blk_meta->size);
1878 if (rsize != blk_meta->size) {
1879 seaf_warning ("Failed to read block %.8s:%s.\n", store_id, block_id);
1880 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1881 } else {
1882 evbuffer_add (req->buffer_out, block_con, blk_meta->size);
1883 evhtp_send_reply (req, EVHTP_RES_OK);
1884 }
1885 g_free (block_con);
1886 send_statistic_msg (store_id, username, "sync-file-download", (guint64)rsize);
1887
1888 free_handle:
1889 seaf_block_manager_close_block (seaf->block_mgr, blk_handle);
1890 seaf_block_manager_block_handle_free (seaf->block_mgr, blk_handle);
1891
1892 out:
1893 g_free (username);
1894 g_free (blk_meta);
1895 g_free (store_id);
1896 g_strfreev (parts);
1897 }
1898
1899 static void
put_send_block_cb(evhtp_request_t * req,void * arg)1900 put_send_block_cb (evhtp_request_t *req, void *arg)
1901 {
1902 const char *repo_id = NULL;
1903 char *block_id = NULL;
1904 char *store_id = NULL;
1905 char *username = NULL;
1906 HttpServer *htp_server = arg;
1907 char **parts = NULL;
1908 void *blk_con = NULL;
1909
1910 parts = g_strsplit (req->uri->path->full + 1, "/", 0);
1911 repo_id = parts[1];
1912 block_id = parts[3];
1913
1914 int token_status = validate_token (htp_server, req, repo_id, &username, FALSE);
1915 if (token_status != EVHTP_RES_OK) {
1916 evhtp_send_reply (req, token_status);
1917 goto out;
1918 }
1919
1920 int perm_status = check_permission (htp_server, repo_id, username,
1921 "upload", FALSE);
1922 if (perm_status == EVHTP_RES_FORBIDDEN) {
1923 evhtp_send_reply (req, EVHTP_RES_FORBIDDEN);
1924 goto out;
1925 }
1926
1927 store_id = get_repo_store_id (htp_server, repo_id);
1928 if (!store_id) {
1929 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1930 goto out;
1931 }
1932
1933 int blk_len = evbuffer_get_length (req->buffer_in);
1934 if (blk_len == 0) {
1935 evhtp_send_reply (req, EVHTP_RES_BADREQ);
1936 goto out;
1937 }
1938
1939 blk_con = g_new0 (char, blk_len);
1940 if (!blk_con) {
1941 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1942 seaf_warning ("Failed to allocate %d bytes memory.\n", blk_len);
1943 goto out;
1944 }
1945
1946 evbuffer_remove (req->buffer_in, blk_con, blk_len);
1947
1948 BlockHandle *blk_handle = NULL;
1949 blk_handle = seaf_block_manager_open_block (seaf->block_mgr,
1950 store_id, 1, block_id, BLOCK_WRITE);
1951 if (blk_handle == NULL) {
1952 seaf_warning ("Failed to open block %.8s:%s.\n", store_id, block_id);
1953 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1954 goto out;
1955 }
1956
1957 if (seaf_block_manager_write_block (seaf->block_mgr, blk_handle,
1958 blk_con, blk_len) != blk_len) {
1959 seaf_warning ("Failed to write block %.8s:%s.\n", store_id, block_id);
1960 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1961 seaf_block_manager_close_block (seaf->block_mgr, blk_handle);
1962 seaf_block_manager_block_handle_free (seaf->block_mgr, blk_handle);
1963 goto out;
1964 }
1965
1966 if (seaf_block_manager_close_block (seaf->block_mgr, blk_handle) < 0) {
1967 seaf_warning ("Failed to close block %.8s:%s.\n", store_id, block_id);
1968 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1969 seaf_block_manager_block_handle_free (seaf->block_mgr, blk_handle);
1970 goto out;
1971 }
1972
1973 if (seaf_block_manager_commit_block (seaf->block_mgr,
1974 blk_handle) < 0) {
1975 seaf_warning ("Failed to commit block %.8s:%s.\n", store_id, block_id);
1976 evhtp_send_reply (req, EVHTP_RES_SERVERR);
1977 seaf_block_manager_block_handle_free (seaf->block_mgr, blk_handle);
1978 goto out;
1979 }
1980
1981 seaf_block_manager_block_handle_free (seaf->block_mgr, blk_handle);
1982
1983 evhtp_send_reply (req, EVHTP_RES_OK);
1984
1985 send_statistic_msg (store_id, username, "sync-file-upload", (guint64)blk_len);
1986
1987 out:
1988 g_free (username);
1989 g_free (store_id);
1990 g_strfreev (parts);
1991 g_free (blk_con);
1992 }
1993
1994 static void
block_oper_cb(evhtp_request_t * req,void * arg)1995 block_oper_cb (evhtp_request_t *req, void *arg)
1996 {
1997 htp_method req_method = evhtp_request_get_method (req);
1998
1999 if (req_method == htp_method_GET) {
2000 get_block_cb (req, arg);
2001 } else if (req_method == htp_method_PUT) {
2002 put_send_block_cb (req, arg);
2003 }
2004 }
2005
2006 static void
post_check_exist_cb(evhtp_request_t * req,void * arg,CheckExistType type)2007 post_check_exist_cb (evhtp_request_t *req, void *arg, CheckExistType type)
2008 {
2009 HttpServer *htp_server = arg;
2010 char **parts = g_strsplit (req->uri->path->full + 1, "/", 0);
2011 char *repo_id = parts[1];
2012 char *store_id = NULL;
2013
2014 int token_status = validate_token (htp_server, req, repo_id, NULL, FALSE);
2015 if (token_status != EVHTP_RES_OK) {
2016 evhtp_send_reply (req, token_status);
2017 goto out;
2018 }
2019
2020 store_id = get_repo_store_id (htp_server, repo_id);
2021 if (!store_id) {
2022 evhtp_send_reply (req, EVHTP_RES_SERVERR);
2023 goto out;
2024 }
2025
2026 size_t list_len = evbuffer_get_length (req->buffer_in);
2027 if (list_len == 0) {
2028 evhtp_send_reply (req, EVHTP_RES_BADREQ);
2029 goto out;
2030 }
2031
2032 char *obj_list_con = g_new0 (char, list_len);
2033 if (!obj_list_con) {
2034 evhtp_send_reply (req, EVHTP_RES_SERVERR);
2035 seaf_warning ("Failed to allocate %zu bytes memory.\n", list_len);
2036 goto out;
2037 }
2038
2039 json_error_t jerror;
2040 evbuffer_remove (req->buffer_in, obj_list_con, list_len);
2041 json_t *obj_array = json_loadb (obj_list_con, list_len, 0, &jerror);
2042 g_free (obj_list_con);
2043
2044 if (!obj_array) {
2045 seaf_warning ("dump obj_id to json failed, error: %s\n", jerror.text);
2046 evhtp_send_reply (req, EVHTP_RES_BADREQ);
2047 return;
2048 }
2049
2050 json_t *obj = NULL;
2051 gboolean ret = TRUE;
2052 const char *obj_id = NULL;
2053 int index = 0;
2054
2055 int array_size = json_array_size (obj_array);
2056 json_t *needed_objs = json_array();
2057
2058 for (; index < array_size; ++index) {
2059 obj = json_array_get (obj_array, index);
2060 obj_id = json_string_value (obj);
2061 if (!is_object_id_valid (obj_id))
2062 continue;
2063
2064 if (type == CHECK_FS_EXIST) {
2065 ret = seaf_fs_manager_object_exists (seaf->fs_mgr, store_id, 1,
2066 obj_id);
2067 } else if (type == CHECK_BLOCK_EXIST) {
2068 ret = seaf_block_manager_block_exists (seaf->block_mgr, store_id, 1,
2069 obj_id);
2070 }
2071
2072 if (!ret) {
2073 json_array_append (needed_objs, obj);
2074 }
2075 }
2076
2077 char *ret_array = json_dumps (needed_objs, JSON_COMPACT);
2078 evbuffer_add (req->buffer_out, ret_array, strlen (ret_array));
2079 evhtp_send_reply (req, EVHTP_RES_OK);
2080
2081 g_free (ret_array);
2082 json_decref (needed_objs);
2083 json_decref (obj_array);
2084
2085 out:
2086 g_free (store_id);
2087 g_strfreev (parts);
2088 }
2089
2090 static void
post_check_fs_cb(evhtp_request_t * req,void * arg)2091 post_check_fs_cb (evhtp_request_t *req, void *arg)
2092 {
2093 post_check_exist_cb (req, arg, CHECK_FS_EXIST);
2094 }
2095
2096 static void
post_check_block_cb(evhtp_request_t * req,void * arg)2097 post_check_block_cb (evhtp_request_t *req, void *arg)
2098 {
2099 post_check_exist_cb (req, arg, CHECK_BLOCK_EXIST);
2100 }
2101
2102 static void
post_recv_fs_cb(evhtp_request_t * req,void * arg)2103 post_recv_fs_cb (evhtp_request_t *req, void *arg)
2104 {
2105 HttpServer *htp_server = arg;
2106 char **parts = g_strsplit (req->uri->path->full + 1, "/", 0);
2107 const char *repo_id = parts[1];
2108 char *store_id = NULL;
2109 char *username = NULL;
2110 FsHdr *hdr = NULL;
2111
2112 int token_status = validate_token (htp_server, req, repo_id, &username, FALSE);
2113 if (token_status != EVHTP_RES_OK) {
2114 evhtp_send_reply (req, token_status);
2115 goto out;
2116 }
2117
2118 int perm_status = check_permission (htp_server, repo_id, username,
2119 "upload", FALSE);
2120 if (perm_status == EVHTP_RES_FORBIDDEN) {
2121 evhtp_send_reply (req, EVHTP_RES_FORBIDDEN);
2122 goto out;
2123 }
2124
2125 store_id = get_repo_store_id (htp_server, repo_id);
2126 if (!store_id) {
2127 evhtp_send_reply (req, EVHTP_RES_SERVERR);
2128 goto out;
2129 }
2130
2131 int fs_con_len = evbuffer_get_length (req->buffer_in);
2132 if (fs_con_len < sizeof(FsHdr)) {
2133 evhtp_send_reply (req, EVHTP_RES_BADREQ);
2134 goto out;
2135 }
2136
2137 hdr = g_new0 (FsHdr, 1);
2138 if (!hdr) {
2139 evhtp_send_reply (req, EVHTP_RES_SERVERR);
2140 goto out;
2141 }
2142
2143 char obj_id[41];
2144 void *obj_con = NULL;
2145 int con_len;
2146
2147 while (fs_con_len > 0) {
2148 if (fs_con_len < sizeof(FsHdr)) {
2149 seaf_warning ("Bad fs object content format from %.8s:%s.\n",
2150 repo_id, username);
2151 evhtp_send_reply (req, EVHTP_RES_BADREQ);
2152 break;
2153 }
2154
2155 evbuffer_remove (req->buffer_in, hdr, sizeof(FsHdr));
2156 con_len = ntohl (hdr->obj_size);
2157 memcpy (obj_id, hdr->obj_id, 40);
2158 obj_id[40] = 0;
2159
2160 if (!is_object_id_valid (obj_id)) {
2161 evhtp_send_reply (req, EVHTP_RES_BADREQ);
2162 break;
2163 }
2164
2165 obj_con = g_new0 (char, con_len);
2166 if (!obj_con) {
2167 evhtp_send_reply (req, EVHTP_RES_SERVERR);
2168 break;
2169 }
2170 evbuffer_remove (req->buffer_in, obj_con, con_len);
2171
2172 if (seaf_obj_store_write_obj (seaf->fs_mgr->obj_store,
2173 store_id, 1, obj_id, obj_con,
2174 con_len, FALSE) < 0) {
2175 seaf_warning ("Failed to write fs object %.8s to disk.\n",
2176 obj_id);
2177 g_free (obj_con);
2178 evhtp_send_reply (req, EVHTP_RES_SERVERR);
2179 break;
2180 }
2181
2182 fs_con_len -= (con_len + sizeof(FsHdr));
2183 g_free (obj_con);
2184 }
2185
2186 if (fs_con_len == 0) {
2187 evhtp_send_reply (req, EVHTP_RES_OK);
2188 }
2189
2190 out:
2191 g_free (store_id);
2192 g_free (hdr);
2193 g_free (username);
2194 g_strfreev (parts);
2195 }
2196
2197 #define MAX_OBJECT_PACK_SIZE (1 << 20) /* 1MB */
2198
2199 static void
post_pack_fs_cb(evhtp_request_t * req,void * arg)2200 post_pack_fs_cb (evhtp_request_t *req, void *arg)
2201 {
2202 HttpServer *htp_server = arg;
2203 char **parts = g_strsplit (req->uri->path->full + 1, "/", 0);
2204 const char *repo_id = parts[1];
2205 char *store_id = NULL;
2206
2207 int token_status = validate_token (htp_server, req, repo_id, NULL, FALSE);
2208 if (token_status != EVHTP_RES_OK) {
2209 evhtp_send_reply (req, token_status);
2210 goto out;
2211 }
2212
2213 store_id = get_repo_store_id (htp_server, repo_id);
2214 if (!store_id) {
2215 evhtp_send_reply (req, EVHTP_RES_SERVERR);
2216 goto out;
2217 }
2218
2219 int fs_id_list_len = evbuffer_get_length (req->buffer_in);
2220 if (fs_id_list_len == 0) {
2221 evhtp_send_reply (req, EVHTP_RES_BADREQ);
2222 goto out;
2223 }
2224
2225 char *fs_id_list = g_new0 (char, fs_id_list_len);
2226 if (!fs_id_list) {
2227 evhtp_send_reply (req, EVHTP_RES_SERVERR);
2228 seaf_warning ("Failed to allocate %d bytes memory.\n", fs_id_list_len);
2229 goto out;
2230 }
2231
2232 json_error_t jerror;
2233 evbuffer_remove (req->buffer_in, fs_id_list, fs_id_list_len);
2234 json_t *fs_id_array = json_loadb (fs_id_list, fs_id_list_len, 0, &jerror);
2235
2236 g_free (fs_id_list);
2237
2238 if (!fs_id_array) {
2239 seaf_warning ("dump fs obj_id from json failed, error: %s\n", jerror.text);
2240 evhtp_send_reply (req, EVHTP_RES_BADREQ);
2241 goto out;
2242 }
2243
2244 json_t *obj = NULL;
2245 const char *obj_id = NULL;
2246 int index = 0;
2247 void *fs_data = NULL;
2248 int data_len;
2249 int data_len_net;
2250 int total_size = 0;
2251
2252 int array_size = json_array_size (fs_id_array);
2253
2254 for (; index < array_size; ++index) {
2255 obj = json_array_get (fs_id_array, index);
2256 obj_id = json_string_value (obj);
2257
2258 if (!is_object_id_valid (obj_id)) {
2259 seaf_warning ("Invalid fs id %s.\n", obj_id);
2260 evhtp_send_reply (req, EVHTP_RES_BADREQ);
2261 json_decref (fs_id_array);
2262 goto out;
2263 }
2264 if (seaf_obj_store_read_obj (seaf->fs_mgr->obj_store, store_id, 1,
2265 obj_id, &fs_data, &data_len) < 0) {
2266 seaf_warning ("Failed to read seafile object %s:%s.\n", store_id, obj_id);
2267 evhtp_send_reply (req, EVHTP_RES_SERVERR);
2268 json_decref (fs_id_array);
2269 goto out;
2270 }
2271
2272 evbuffer_add (req->buffer_out, obj_id, 40);
2273 data_len_net = htonl (data_len);
2274 evbuffer_add (req->buffer_out, &data_len_net, 4);
2275 evbuffer_add (req->buffer_out, fs_data, data_len);
2276
2277 total_size += data_len;
2278 g_free (fs_data);
2279
2280 if (total_size >= MAX_OBJECT_PACK_SIZE)
2281 break;
2282 }
2283
2284 evhtp_send_reply (req, EVHTP_RES_OK);
2285
2286 json_decref (fs_id_array);
2287 out:
2288 g_free (store_id);
2289 g_strfreev (parts);
2290 }
2291
2292 static void
get_block_map_cb(evhtp_request_t * req,void * arg)2293 get_block_map_cb (evhtp_request_t *req, void *arg)
2294 {
2295 const char *repo_id = NULL;
2296 char *file_id = NULL;
2297 char *store_id = NULL;
2298 HttpServer *htp_server = arg;
2299 Seafile *file = NULL;
2300 char *block_id;
2301 BlockMetadata *blk_meta = NULL;
2302 json_t *array = NULL;
2303 char *data = NULL;
2304
2305 char **parts = g_strsplit (req->uri->path->full + 1, "/", 0);
2306 repo_id = parts[1];
2307 file_id = parts[3];
2308
2309 int token_status = validate_token (htp_server, req, repo_id, NULL, FALSE);
2310 if (token_status != EVHTP_RES_OK) {
2311 evhtp_send_reply (req, token_status);
2312 goto out;
2313 }
2314
2315 store_id = get_repo_store_id (htp_server, repo_id);
2316 if (!store_id) {
2317 evhtp_send_reply (req, EVHTP_RES_SERVERR);
2318 goto out;
2319 }
2320
2321 file = seaf_fs_manager_get_seafile (seaf->fs_mgr, store_id, 1, file_id);
2322 if (!file) {
2323 evhtp_send_reply (req, EVHTP_RES_NOTFOUND);
2324 goto out;
2325 }
2326
2327 array = json_array ();
2328
2329 int i;
2330 for (i = 0; i < file->n_blocks; ++i) {
2331 block_id = file->blk_sha1s[i];
2332 blk_meta = seaf_block_manager_stat_block (seaf->block_mgr,
2333 store_id, 1, block_id);
2334 if (blk_meta == NULL) {
2335 seaf_warning ("Failed to find block %s/%s\n", store_id, block_id);
2336 evhtp_send_reply (req, EVHTP_RES_SERVERR);
2337 g_free (blk_meta);
2338 goto out;
2339 }
2340 json_array_append_new (array, json_integer(blk_meta->size));
2341 g_free (blk_meta);
2342 }
2343
2344 data = json_dumps (array, JSON_COMPACT);
2345 evbuffer_add (req->buffer_out, data, strlen (data));
2346 evhtp_send_reply (req, EVHTP_RES_OK);
2347
2348 out:
2349 g_free (store_id);
2350 seafile_unref (file);
2351 if (array)
2352 json_decref (array);
2353 if (data)
2354 free (data);
2355 g_strfreev (parts);
2356 }
2357
2358 static json_t *
fill_obj_from_seafilerepo(SeafileRepo * srepo,GHashTable * table)2359 fill_obj_from_seafilerepo (SeafileRepo *srepo, GHashTable *table)
2360 {
2361 int version = 0;
2362 char *repo_id = NULL;
2363 char *commit_id = NULL;
2364 char *repo_name = NULL;
2365 char *permission = NULL;
2366 char *owner = NULL;
2367 gint64 last_modify = 0;
2368 json_t *obj = NULL;
2369
2370 g_object_get (srepo, "version", &version,
2371 "id", &repo_id,
2372 "head_cmmt_id", &commit_id,
2373 "name", &repo_name,
2374 "last_modify", &last_modify,
2375 "permission", &permission,
2376 "user", &owner,
2377 NULL);
2378
2379 if (!repo_id)
2380 goto out;
2381 //the repo_id will be free when the table is destroyed.
2382 if (g_hash_table_lookup (table, repo_id)) {
2383 g_free (repo_id);
2384 goto out;
2385 }
2386 g_hash_table_insert (table, repo_id, repo_id);
2387 obj = json_object ();
2388 json_object_set_new (obj, "version", json_integer (version));
2389 json_object_set_new (obj, "id", json_string (repo_id));
2390 json_object_set_new (obj, "head_commit_id", json_string (commit_id));
2391 json_object_set_new (obj, "name", json_string (repo_name));
2392 json_object_set_new (obj, "mtime", json_integer (last_modify));
2393 json_object_set_new (obj, "permission", json_string (permission));
2394 json_object_set_new (obj, "owner", json_string (owner));
2395
2396 out:
2397 g_free (commit_id);
2398 g_free (repo_name);
2399 g_free (permission);
2400 g_free (owner);
2401 return obj;
2402 }
2403
2404 static GHashTable *
filter_group_repos(GList * repos)2405 filter_group_repos (GList *repos)
2406 {
2407 if (!repos)
2408 return NULL;
2409
2410 SeafileRepo *srepo = NULL;
2411 SeafileRepo *srepo_tmp = NULL;
2412 GList *iter;
2413 GHashTable *table = NULL;
2414 char *permission = NULL;
2415 char *permission_prev = NULL;
2416 char *repo_id = NULL;
2417
2418 table = g_hash_table_new_full (g_str_hash, g_str_equal,
2419 g_free,
2420 NULL);
2421
2422 for (iter = repos; iter; iter = iter->next) {
2423 srepo = iter->data;
2424 g_object_get (srepo, "id", &repo_id,
2425 "permission", &permission,
2426 NULL);
2427 srepo_tmp = g_hash_table_lookup (table, repo_id);
2428 if (srepo_tmp) {
2429 g_object_get (srepo_tmp, "permission", &permission_prev,
2430 NULL);
2431 if (g_strcmp0 (permission, "rw") == 0 && g_strcmp0 (permission_prev, "r") == 0) {
2432 g_object_unref (srepo_tmp);
2433 g_hash_table_remove (table, repo_id);
2434 g_hash_table_insert (table, g_strdup (repo_id), srepo);
2435 } else {
2436 g_object_unref (srepo);
2437 }
2438 g_free (permission_prev);
2439 } else {
2440 g_hash_table_insert (table, g_strdup (repo_id), srepo);
2441 }
2442 }
2443
2444 g_free (repo_id);
2445 g_free (permission);
2446 return table;
2447 }
2448
2449 static void
group_repos_to_json(json_t * repo_array,GHashTable * group_repos,GHashTable * obtained_repos)2450 group_repos_to_json (json_t *repo_array, GHashTable *group_repos,
2451 GHashTable *obtained_repos)
2452 {
2453 GHashTableIter iter;
2454 gpointer key, value;
2455 SeafileRepo *srepo = NULL;
2456 json_t *obj;
2457
2458 g_hash_table_iter_init (&iter, group_repos);
2459 while (g_hash_table_iter_next (&iter, &key, &value)) {
2460 srepo = value;
2461 obj = fill_obj_from_seafilerepo (srepo, obtained_repos);
2462 if (!obj) {
2463 g_object_unref (srepo);
2464 continue;
2465 }
2466 json_object_set_new (obj, "type", json_string ("grepo"));
2467
2468 json_array_append_new (repo_array, obj);
2469 g_object_unref (srepo);
2470 }
2471 }
2472
2473 static void
get_accessible_repo_list_cb(evhtp_request_t * req,void * arg)2474 get_accessible_repo_list_cb (evhtp_request_t *req, void *arg)
2475 {
2476 GList *iter;
2477 HttpServer *htp_server = (HttpServer *)arg;
2478 SeafRepo *repo = NULL;
2479 char *user = NULL;
2480 GList *repos = NULL;
2481 int org_id = -1;
2482 const char *repo_id = evhtp_kv_find (req->uri->query, "repo_id");
2483
2484 if (!repo_id || !is_uuid_valid (repo_id)) {
2485 evhtp_send_reply (req, EVHTP_RES_BADREQ);
2486 seaf_warning ("Invalid repo id.\n");
2487 return;
2488 }
2489
2490 int token_status = validate_token (htp_server, req, repo_id, &user, FALSE);
2491 if (token_status != EVHTP_RES_OK) {
2492 evhtp_send_reply (req, token_status);
2493 return;
2494 }
2495
2496 json_t *obj;
2497 json_t *repo_array = json_array ();
2498
2499 gboolean db_err = FALSE;
2500 GHashTable *obtained_repos = NULL;
2501 char *repo_id_tmp = NULL;
2502 obtained_repos = g_hash_table_new_full (g_str_hash, g_str_equal,
2503 g_free,
2504 NULL);
2505 //get personal repo list
2506 repos = seaf_repo_manager_get_repos_by_owner (seaf->repo_mgr, user, 0, -1, -1, &db_err);
2507 if (db_err)
2508 goto out;
2509
2510 for (iter = repos; iter; iter = iter->next) {
2511 repo = iter->data;
2512
2513 if (!repo->is_corrupted) {
2514 if (!g_hash_table_lookup (obtained_repos, repo->id)) {
2515 repo_id_tmp = g_strdup (repo->id);
2516 g_hash_table_insert (obtained_repos, repo_id_tmp, repo_id_tmp);
2517 }
2518 obj = json_object ();
2519 json_object_set_new (obj, "version", json_integer (repo->version));
2520 json_object_set_new (obj, "id", json_string (repo->id));
2521 json_object_set_new (obj, "head_commit_id", json_string (repo->head->commit_id));
2522 json_object_set_new (obj, "name", json_string (repo->name));
2523 json_object_set_new (obj, "mtime", json_integer (repo->last_modify));
2524 json_object_set_new (obj, "permission", json_string ("rw"));
2525 json_object_set_new (obj, "type", json_string ("repo"));
2526 json_object_set_new (obj, "owner", json_string (user));
2527
2528 json_array_append_new (repo_array, obj);
2529 }
2530 seaf_repo_unref (repo);
2531 }
2532 g_list_free (repos);
2533
2534 GError *error = NULL;
2535 SeafileRepo *srepo = NULL;
2536 //get shared repo list
2537 repos = seaf_share_manager_list_share_repos (seaf->share_mgr, user, "to_email", -1, -1, &db_err);
2538 if (db_err)
2539 goto out;
2540
2541 for (iter = repos; iter; iter = iter->next) {
2542 srepo = iter->data;
2543 obj = fill_obj_from_seafilerepo (srepo, obtained_repos);
2544 if (!obj) {
2545 g_object_unref (srepo);
2546 continue;
2547 }
2548 json_object_set_new (obj, "type", json_string ("srepo"));
2549
2550 json_array_append_new (repo_array, obj);
2551 g_object_unref (srepo);
2552 }
2553 g_list_free (repos);
2554
2555 //get group repo list
2556 GHashTable *group_repos = NULL;
2557 repos = seaf_get_group_repos_by_user (seaf->repo_mgr, user, org_id, &error);
2558 if (error) {
2559 g_clear_error (&error);
2560 goto out;
2561 }
2562
2563 if (repos) {
2564 group_repos = filter_group_repos (repos);
2565 group_repos_to_json (repo_array, group_repos, obtained_repos);
2566 g_hash_table_destroy (group_repos);
2567 g_list_free (repos);
2568 }
2569
2570 //get inner public repo list
2571 repos = seaf_repo_manager_list_inner_pub_repos (seaf->repo_mgr, &db_err);
2572 if (db_err)
2573 goto out;
2574
2575 for (iter = repos; iter; iter = iter->next) {
2576 srepo = iter->data;
2577 obj = fill_obj_from_seafilerepo (srepo, obtained_repos);
2578 if (!obj) {
2579 g_object_unref (srepo);
2580 continue;
2581 }
2582 json_object_set_new (obj, "type", json_string ("grepo"));
2583 json_object_set (obj, "owner", json_string ("Organization"));
2584
2585 json_array_append_new (repo_array, obj);
2586 g_object_unref (srepo);
2587 }
2588 g_list_free (repos);
2589
2590 out:
2591 g_hash_table_destroy (obtained_repos);
2592
2593 if (db_err) {
2594 json_decref (repo_array);
2595 seaf_warning ("DB error when get accessible repo list.\n");
2596 evhtp_send_reply (req, EVHTP_RES_SERVERR);
2597 return;
2598 }
2599
2600 char *json_str = json_dumps (repo_array, JSON_COMPACT);
2601 evbuffer_add (req->buffer_out, json_str, strlen(json_str));
2602 evhtp_send_reply (req, EVHTP_RES_OK);
2603
2604 g_free (json_str);
2605 json_decref (repo_array);
2606 }
2607
2608 static void
http_request_init(HttpServerStruct * server)2609 http_request_init (HttpServerStruct *server)
2610 {
2611 HttpServer *priv = server->priv;
2612
2613 evhtp_set_cb (priv->evhtp,
2614 GET_PROTO_PATH, get_protocol_cb,
2615 NULL);
2616
2617 evhtp_set_regex_cb (priv->evhtp,
2618 GET_CHECK_QUOTA_REGEX, get_check_quota_cb,
2619 priv);
2620
2621 evhtp_set_regex_cb (priv->evhtp,
2622 OP_PERM_CHECK_REGEX, get_check_permission_cb,
2623 priv);
2624
2625 evhtp_set_regex_cb (priv->evhtp,
2626 HEAD_COMMIT_OPER_REGEX, head_commit_oper_cb,
2627 priv);
2628
2629 evhtp_set_regex_cb (priv->evhtp,
2630 GET_HEAD_COMMITS_MULTI_REGEX, head_commits_multi_cb,
2631 priv);
2632
2633 evhtp_set_regex_cb (priv->evhtp,
2634 COMMIT_OPER_REGEX, commit_oper_cb,
2635 priv);
2636
2637 evhtp_set_regex_cb (priv->evhtp,
2638 GET_FS_OBJ_ID_REGEX, get_fs_obj_id_cb,
2639 priv);
2640
2641 // evhtp_set_regex_cb (priv->evhtp,
2642 // START_FS_OBJ_ID_REGEX, start_fs_obj_id_cb,
2643 // priv);
2644
2645 // evhtp_set_regex_cb (priv->evhtp,
2646 // QUERY_FS_OBJ_ID_REGEX, query_fs_obj_id_cb,
2647 // priv);
2648
2649 // evhtp_set_regex_cb (priv->evhtp,
2650 // RETRIEVE_FS_OBJ_ID_REGEX, retrieve_fs_obj_id_cb,
2651 // priv);
2652
2653 evhtp_set_regex_cb (priv->evhtp,
2654 BLOCK_OPER_REGEX, block_oper_cb,
2655 priv);
2656
2657 evhtp_set_regex_cb (priv->evhtp,
2658 POST_CHECK_FS_REGEX, post_check_fs_cb,
2659 priv);
2660
2661 evhtp_set_regex_cb (priv->evhtp,
2662 POST_CHECK_BLOCK_REGEX, post_check_block_cb,
2663 priv);
2664
2665 evhtp_set_regex_cb (priv->evhtp,
2666 POST_RECV_FS_REGEX, post_recv_fs_cb,
2667 priv);
2668
2669 evhtp_set_regex_cb (priv->evhtp,
2670 POST_PACK_FS_REGEX, post_pack_fs_cb,
2671 priv);
2672
2673 evhtp_set_regex_cb (priv->evhtp,
2674 GET_BLOCK_MAP_REGEX, get_block_map_cb,
2675 priv);
2676
2677 evhtp_set_regex_cb (priv->evhtp,
2678 GET_ACCESSIBLE_REPO_LIST_REGEX, get_accessible_repo_list_cb,
2679 priv);
2680
2681 /* Web access file */
2682 access_file_init (priv->evhtp);
2683
2684 /* Web upload file */
2685 if (upload_file_init (priv->evhtp, server->http_temp_dir) < 0)
2686 exit(-1);
2687 }
2688
2689 static void
token_cache_value_free(gpointer data)2690 token_cache_value_free (gpointer data)
2691 {
2692 TokenInfo *token_info = (TokenInfo *)data;
2693 if (token_info != NULL) {
2694 g_free (token_info->repo_id);
2695 g_free (token_info->email);
2696 g_free (token_info);
2697 }
2698 }
2699
2700 static gboolean
is_token_expire(gpointer key,gpointer value,gpointer arg)2701 is_token_expire (gpointer key, gpointer value, gpointer arg)
2702 {
2703 TokenInfo *token_info = (TokenInfo *)value;
2704
2705 if(token_info && token_info->expire_time <= (gint64)time(NULL)) {
2706 return TRUE;
2707 }
2708
2709 return FALSE;
2710 }
2711
2712 static void
perm_cache_value_free(gpointer data)2713 perm_cache_value_free (gpointer data)
2714 {
2715 PermInfo *perm_info = data;
2716 g_free (perm_info->perm);
2717 g_free (perm_info);
2718 }
2719
2720 static gboolean
is_perm_expire(gpointer key,gpointer value,gpointer arg)2721 is_perm_expire (gpointer key, gpointer value, gpointer arg)
2722 {
2723 PermInfo *perm_info = (PermInfo *)value;
2724
2725 if(perm_info && perm_info->expire_time <= (gint64)time(NULL)) {
2726 return TRUE;
2727 }
2728
2729 return FALSE;
2730 }
2731
2732 static gboolean
is_vir_repo_info_expire(gpointer key,gpointer value,gpointer arg)2733 is_vir_repo_info_expire (gpointer key, gpointer value, gpointer arg)
2734 {
2735 VirRepoInfo *vinfo = (VirRepoInfo *)value;
2736
2737 if(vinfo && vinfo->expire_time <= (gint64)time(NULL)) {
2738 return TRUE;
2739 }
2740
2741 return FALSE;
2742 }
2743
2744 static void
free_vir_repo_info(gpointer data)2745 free_vir_repo_info (gpointer data)
2746 {
2747 if (!data)
2748 return;
2749
2750 VirRepoInfo *vinfo = data;
2751
2752 if (vinfo->store_id)
2753 g_free (vinfo->store_id);
2754
2755 g_free (vinfo);
2756 }
2757
2758 static void
remove_expire_cache_cb(evutil_socket_t sock,short type,void * data)2759 remove_expire_cache_cb (evutil_socket_t sock, short type, void *data)
2760 {
2761 HttpServer *htp_server = data;
2762
2763 pthread_mutex_lock (&htp_server->token_cache_lock);
2764 g_hash_table_foreach_remove (htp_server->token_cache, is_token_expire, NULL);
2765 pthread_mutex_unlock (&htp_server->token_cache_lock);
2766
2767 pthread_mutex_lock (&htp_server->perm_cache_lock);
2768 g_hash_table_foreach_remove (htp_server->perm_cache, is_perm_expire, NULL);
2769 pthread_mutex_unlock (&htp_server->perm_cache_lock);
2770
2771 pthread_mutex_lock (&htp_server->vir_repo_info_cache_lock);
2772 g_hash_table_foreach_remove (htp_server->vir_repo_info_cache,
2773 is_vir_repo_info_expire, NULL);
2774 pthread_mutex_unlock (&htp_server->vir_repo_info_cache_lock);
2775 }
2776
2777 static void *
http_server_run(void * arg)2778 http_server_run (void *arg)
2779 {
2780 HttpServerStruct *server = arg;
2781 HttpServer *priv = server->priv;
2782
2783 priv->evbase = event_base_new();
2784 priv->evhtp = evhtp_new(priv->evbase, NULL);
2785
2786 if (evhtp_bind_socket(priv->evhtp,
2787 server->bind_addr,
2788 server->bind_port, 128) < 0) {
2789 seaf_warning ("Could not bind socket: %s\n", strerror (errno));
2790 exit(-1);
2791 }
2792
2793 http_request_init (server);
2794
2795 evhtp_use_threads_wexit (priv->evhtp, NULL, NULL, server->worker_threads, NULL);
2796
2797 struct timeval tv;
2798 tv.tv_sec = CLEANING_INTERVAL_SEC;
2799 tv.tv_usec = 0;
2800 priv->reap_timer = event_new (priv->evbase,
2801 -1,
2802 EV_PERSIST,
2803 remove_expire_cache_cb,
2804 priv);
2805 evtimer_add (priv->reap_timer, &tv);
2806
2807 event_base_loop (priv->evbase, 0);
2808
2809 return NULL;
2810 }
2811
2812 HttpServerStruct *
seaf_http_server_new(struct _SeafileSession * session)2813 seaf_http_server_new (struct _SeafileSession *session)
2814 {
2815 HttpServerStruct *server = g_new0 (HttpServerStruct, 1);
2816 HttpServer *priv = g_new0 (HttpServer, 1);
2817
2818 priv->evbase = NULL;
2819 priv->evhtp = NULL;
2820
2821 load_http_config (server, session);
2822
2823 priv->token_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2824 g_free, token_cache_value_free);
2825 pthread_mutex_init (&priv->token_cache_lock, NULL);
2826
2827 priv->perm_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2828 g_free, perm_cache_value_free);
2829 pthread_mutex_init (&priv->perm_cache_lock, NULL);
2830
2831 priv->vir_repo_info_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2832 g_free, free_vir_repo_info);
2833 pthread_mutex_init (&priv->vir_repo_info_cache_lock, NULL);
2834
2835 server->http_temp_dir = g_build_filename (session->seaf_dir, "httptemp", NULL);
2836
2837 // priv->compute_fs_obj_id_pool = g_thread_pool_new (compute_fs_obj_id, NULL,
2838 // FS_ID_LIST_MAX_WORKERS, FALSE, NULL);
2839
2840 // priv->fs_obj_ids = g_hash_table_new_full (g_str_hash, g_str_equal,
2841 // g_free, free_obj_cal_result);
2842 // pthread_mutex_init (&priv->fs_obj_ids_lock, NULL);
2843
2844 server->seaf_session = session;
2845 server->priv = priv;
2846
2847 return server;
2848 }
2849
2850 gint64
get_last_modify_time(const char * path)2851 get_last_modify_time (const char *path)
2852 {
2853 struct stat st;
2854 if (stat (path, &st) < 0) {
2855 return -1;
2856 }
2857
2858 return st.st_mtime;
2859 }
2860
2861 static gint64
check_httptemp_dir_recursive(const char * parent_dir,gint64 expired_time)2862 check_httptemp_dir_recursive (const char *parent_dir, gint64 expired_time)
2863 {
2864 char *full_path;
2865 const char *dname;
2866 gint64 cur_time;
2867 gint64 last_modify = -1;
2868 GDir *dir = NULL;
2869 gint64 file_num = 0;
2870
2871 dir = g_dir_open (parent_dir, 0, NULL);
2872
2873 while ((dname = g_dir_read_name(dir)) != NULL) {
2874 full_path = g_build_path ("/", parent_dir, dname, NULL);
2875
2876 if (g_file_test (full_path, G_FILE_TEST_IS_DIR)) {
2877 file_num += check_httptemp_dir_recursive (full_path, expired_time);
2878 } else {
2879 cur_time = time (NULL);
2880 last_modify = get_last_modify_time (full_path);
2881 if (last_modify == -1) {
2882 g_free (full_path);
2883 continue;
2884 }
2885 /*remove blokc cache from local*/
2886 if (last_modify + expired_time <= cur_time) {
2887 g_unlink (full_path);
2888 file_num ++;
2889 }
2890 }
2891 g_free (full_path);
2892 }
2893
2894 g_dir_close (dir);
2895
2896 return file_num;
2897 }
2898
2899 static int
scan_httptemp_dir(const char * httptemp_dir,gint64 expired_time)2900 scan_httptemp_dir (const char *httptemp_dir, gint64 expired_time)
2901 {
2902 return check_httptemp_dir_recursive (httptemp_dir, expired_time);
2903 }
2904
2905 static void *
cleanup_expired_httptemp_file(void * arg)2906 cleanup_expired_httptemp_file (void *arg)
2907 {
2908 GError *error = NULL;
2909 HttpServerStruct *server = arg;
2910 SeafileSession *session = server->seaf_session;
2911 gint64 ttl = 0;
2912 gint64 scan_interval = 0;
2913 gint64 file_num = 0;
2914
2915 ttl = fileserver_config_get_int64 (session->config, HTTP_TEMP_FILE_TTL, &error);
2916 if (error) {
2917 ttl = HTTP_TEMP_FILE_DEFAULT_TTL;
2918 g_clear_error (&error);
2919 }
2920
2921 scan_interval = fileserver_config_get_int64 (session->config, HTTP_SCAN_INTERVAL, &error);
2922 if (error) {
2923 scan_interval = HTTP_TEMP_FILE_SCAN_INTERVAL;
2924 g_clear_error (&error);
2925 }
2926
2927 while (TRUE) {
2928 sleep (scan_interval);
2929 file_num = scan_httptemp_dir (server->http_temp_dir, ttl);
2930 if (file_num) {
2931 seaf_message ("Clean up %ld http temp files\n", file_num);
2932 file_num = 0;
2933 }
2934 }
2935
2936 return NULL;
2937 }
2938
2939 int
seaf_http_server_start(HttpServerStruct * server)2940 seaf_http_server_start (HttpServerStruct *server)
2941 {
2942 int ret = pthread_create (&server->priv->thread_id, NULL, http_server_run, server);
2943 if (ret != 0)
2944 return -1;
2945
2946 pthread_detach (server->priv->thread_id);
2947
2948 pthread_t tid;
2949 ret = pthread_create (&tid, NULL, cleanup_expired_httptemp_file, server);
2950 if (ret != 0)
2951 return -1;
2952
2953 pthread_detach (tid);
2954 return 0;
2955 }
2956
2957 int
seaf_http_server_invalidate_tokens(HttpServerStruct * htp_server,const GList * tokens)2958 seaf_http_server_invalidate_tokens (HttpServerStruct *htp_server,
2959 const GList *tokens)
2960 {
2961 const GList *p;
2962
2963 pthread_mutex_lock (&htp_server->priv->token_cache_lock);
2964 for (p = tokens; p; p = p->next) {
2965 const char *token = (char *)p->data;
2966 g_hash_table_remove (htp_server->priv->token_cache, token);
2967 }
2968 pthread_mutex_unlock (&htp_server->priv->token_cache_lock);
2969 return 0;
2970 }
2971