js: Move /view to C.
This commit is contained in:
		
							
								
								
									
										61
									
								
								core/core.js
									
									
									
									
									
								
							
							
						
						
									
										61
									
								
								core/core.js
									
									
									
									
									
								
							| @@ -962,66 +962,7 @@ async function blobHandler(request, response, blobId, uri) { | ||||
| 	} | ||||
|  | ||||
| 	let process; | ||||
| 	if (uri == '/view') { | ||||
| 		let data; | ||||
| 		let match; | ||||
| 		let query = form.decodeForm(request.query); | ||||
| 		let headers = { | ||||
| 			'Content-Security-Policy': k_content_security_policy, | ||||
| 		}; | ||||
| 		if (query.filename && query.filename.match(/^[A-Za-z0-9\.-]*$/)) { | ||||
| 			headers['Content-Disposition'] = `attachment; filename=${query.filename}`; | ||||
| 		} | ||||
| 		if ((match = /^\/\~(\w+)\/(\w+)$/.exec(blobId))) { | ||||
| 			let id = await new Database(match[1]).get('path:' + match[2]); | ||||
| 			if (id) { | ||||
| 				if (request.headers['if-none-match'] === '"' + id + '"') { | ||||
| 					headers['Content-Length'] = '0'; | ||||
| 					response.writeHead(304, headers); | ||||
| 					response.end(); | ||||
| 				} else { | ||||
| 					data = await ssb.blobGet(id); | ||||
| 					if (match[3]) { | ||||
| 						let appObject = JSON.parse(data); | ||||
| 						data = appObject.files[match[3]]; | ||||
| 					} | ||||
| 					sendData( | ||||
| 						response, | ||||
| 						data, | ||||
| 						undefined, | ||||
| 						Object.assign({etag: '"' + id + '"'}, headers) | ||||
| 					); | ||||
| 				} | ||||
| 			} else { | ||||
| 				if (request.headers['if-none-match'] === '"' + blobId + '"') { | ||||
| 					headers['Content-Length'] = '0'; | ||||
| 					response.writeHead(304, headers); | ||||
| 					response.end(); | ||||
| 				} else { | ||||
| 					sendData( | ||||
| 						response, | ||||
| 						data, | ||||
| 						undefined, | ||||
| 						Object.assign({etag: '"' + blobId + '"'}, headers) | ||||
| 					); | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			if (request.headers['if-none-match'] === '"' + blobId + '"') { | ||||
| 				headers['Content-Length'] = '0'; | ||||
| 				response.writeHead(304, headers); | ||||
| 				response.end(); | ||||
| 			} else { | ||||
| 				data = await ssb.blobGet(blobId); | ||||
| 				sendData( | ||||
| 					response, | ||||
| 					data, | ||||
| 					undefined, | ||||
| 					Object.assign({etag: '"' + blobId + '"'}, headers) | ||||
| 				); | ||||
| 			} | ||||
| 		} | ||||
| 	} else if (uri == '/save') { | ||||
| 	if (uri == '/save') { | ||||
| 		let match; | ||||
| 		if ((match = /^\/\~(\w+)\/(\w+)$/.exec(blobId))) { | ||||
| 			let user = match[1]; | ||||
|   | ||||
							
								
								
									
										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