js: Move /view to C.
This commit is contained in:
		
							
								
								
									
										120
									
								
								src/httpd.js.c
									
									
									
									
									
								
							
							
						
						
									
										120
									
								
								src/httpd.js.c
									
									
									
									
									
								
							| @@ -41,6 +41,8 @@ static JSValue _authenticate_jwt(tf_ssb_t* ssb, JSContext* context, const char* | ||||
| static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); | ||||
| static const char* _make_session_jwt(JSContext* context, tf_ssb_t* ssb, const char* name); | ||||
| static const char* _make_set_session_cookie_header(tf_http_request_t* request, const char* session_cookie); | ||||
| const char** _form_data_decode(const char* data, int length); | ||||
| const char* _form_data_get(const char** form_data, const char* key); | ||||
|  | ||||
| static JSClassID _httpd_class_id; | ||||
| static JSClassID _httpd_request_class_id; | ||||
| @@ -959,6 +961,123 @@ static void _httpd_endpoint_static(tf_http_request_t* request) | ||||
| 	tf_file_stat(task, path, _httpd_endpoint_static_stat, request); | ||||
| } | ||||
|  | ||||
| typedef struct _view_t | ||||
| { | ||||
| 	tf_http_request_t* request; | ||||
| 	const char** form_data; | ||||
| 	void* data; | ||||
| 	size_t size; | ||||
| 	bool not_modified; | ||||
| } view_t; | ||||
|  | ||||
| static bool _is_filename_safe(const char* filename) | ||||
| { | ||||
| 	if (!filename) | ||||
| 	{ | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	for (const char* p = filename; *p; p++) | ||||
| 	{ | ||||
| 		if ((*p <= 'a' && *p >= 'z') && | ||||
| 			(*p <= 'A' && *p >= 'Z') && | ||||
| 			(*p <= '0' && *p >= '9') && | ||||
| 			*p != '.' && | ||||
| 			*p != '-' && | ||||
| 			*p != '_') | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	return strlen(filename) < 256; | ||||
| } | ||||
|  | ||||
| static void _httpd_endpoint_view_work(tf_ssb_t* ssb, void* user_data) | ||||
| { | ||||
| 	view_t* view = user_data; | ||||
| 	tf_http_request_t* request = view->request; | ||||
| 	char blob_id[256] = ""; | ||||
| 	if (request->path[0] == '/' && request->path[1] == '~') | ||||
| 	{ | ||||
| 		char user[256] = ""; | ||||
| 		char path[1024] = ""; | ||||
| 		const char* slash = strchr(request->path + 2, '/'); | ||||
| 		if (slash) | ||||
| 		{ | ||||
| 			snprintf(user, sizeof(user), "%.*s", (int)(slash - (request->path + 2)), request->path + 2); | ||||
| 			snprintf(path, sizeof(path), "path:%.*s", (int)(strlen(slash + 1) - strlen("/view")), slash + 1); | ||||
| 			const char* value = tf_ssb_db_get_property(ssb, user, path); | ||||
| 			snprintf(blob_id, sizeof(blob_id), "%s", value); | ||||
| 			tf_free((void*)value); | ||||
| 		} | ||||
| 	} | ||||
| 	else if (request->path[0] == '/' && request->path[1] == '&') | ||||
| 	{ | ||||
| 		snprintf(blob_id, sizeof(blob_id), "%.*s", (int)(strlen(request->path) - strlen("/view") - 1), request->path + 1); | ||||
| 	} | ||||
|  | ||||
| 	if (*blob_id) | ||||
| 	{ | ||||
| 		const char* if_none_match = tf_http_request_get_header(request, "if-none-match"); | ||||
| 		char match[258]; | ||||
| 		snprintf(match, sizeof(match), "\"%s\"", blob_id); | ||||
| 		if (if_none_match && strcmp(if_none_match, match)) | ||||
| 		{ | ||||
| 			view->not_modified = true; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			tf_ssb_db_blob_get(ssb, blob_id, (uint8_t**)&view->data, &view->size); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void _httpd_endpoint_view_after_work(tf_ssb_t* ssb, int status, void* user_data) | ||||
| { | ||||
| 	view_t* view = user_data; | ||||
| 	const char* filename = _form_data_get(view->form_data, "filename"); | ||||
| 	if (!_is_filename_safe(filename)) | ||||
| 	{ | ||||
| 		filename = NULL; | ||||
| 	} | ||||
| 	char content_disposition[512] = ""; | ||||
| 	if (filename) | ||||
| 	{ | ||||
| 		snprintf(content_disposition, sizeof(content_disposition), "attachment; filename=%s", filename); | ||||
| 	} | ||||
| 	const char* headers[] = { | ||||
| 		"Content-Security-Policy", "sandbox allow-downloads allow-top-navigation-by-user-activation", | ||||
| 		filename ? "Content-Disposition" : NULL, filename ? content_disposition : NULL, | ||||
| 	}; | ||||
| 	int count = filename ? tf_countof(headers) / 2 : (tf_countof(headers) / 2 - 1); | ||||
| 	if (view->not_modified) | ||||
| 	{ | ||||
| 		tf_http_respond(view->request, 304, headers, count, NULL, 0); | ||||
| 	} | ||||
| 	else if (view->data) | ||||
| 	{ | ||||
| 		tf_http_respond(view->request, 200, headers, count, view->data, view->size); | ||||
| 		tf_free(view->data); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		const char* k_payload = tf_http_status_text(404); | ||||
| 		tf_http_respond(view->request, 404, NULL, 0, k_payload, strlen(k_payload)); | ||||
| 	} | ||||
| 	tf_free(view->form_data); | ||||
| 	tf_http_request_unref(view->request); | ||||
| 	tf_free(view); | ||||
| } | ||||
|  | ||||
| static void _httpd_endpoint_view(tf_http_request_t* request) | ||||
| { | ||||
| 	tf_http_request_ref(request); | ||||
| 	tf_task_t* task = request->user_data; | ||||
| 	tf_ssb_t* ssb = tf_task_get_ssb(task); | ||||
| 	view_t* view = tf_malloc(sizeof(view_t)); | ||||
| 	*view = (view_t) { .request = request, .form_data = _form_data_decode(request->query, request->query ? strlen(request->query) : 0) }; | ||||
| 	tf_ssb_run_work(ssb, _httpd_endpoint_view_work, _httpd_endpoint_view_after_work, view); | ||||
| } | ||||
|  | ||||
| static void _httpd_endpoint_root_callback(const char* path, void* user_data) | ||||
| { | ||||
| 	tf_http_request_t* request = user_data; | ||||
| @@ -1690,6 +1809,7 @@ void tf_httpd_register(JSContext* context) | ||||
| 	tf_http_add_handler(http, "/.well-known/*", _httpd_endpoint_static, NULL, task); | ||||
| 	tf_http_add_handler(http, "/~*/*/", _httpd_endpoint_static, NULL, task); | ||||
| 	tf_http_add_handler(http, "/&*.sha256/", _httpd_endpoint_static, NULL, task); | ||||
| 	tf_http_add_handler(http, "/*/view", _httpd_endpoint_view, NULL, task); | ||||
|  | ||||
| 	tf_http_add_handler(http, "/robots.txt", _httpd_endpoint_robots_txt, NULL, NULL); | ||||
| 	tf_http_add_handler(http, "/debug", _httpd_endpoint_debug, NULL, task); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user