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