27 "/route-manifest.json",
28 "/app-graph-schema.json",
30 "/component-graph.json",
31 "/server-actions.json",
32 "/dispatch-table.json",
33 "/permission-matrix.json",
35 "/tau-state-catalog.json",
39 "/route-explain-project-detail.json",
40 "/impact-project-card.json",
41 "/dead-surface-report.json",
42 "/consistency-report.json",
43 "/policy-cache-report.json",
44 "/performance-budget.json",
45 "/generated-docs.json",
46 "/generated-tests.json",
48 "/release-evidence.json",
50 "/api/graph/export/native",
51 "/api/graph/export/radius",
56 "/api/projects/sample",
72 if (path == NULL || path[0] ==
'\0') {
95 if (artifact_dir == NULL || path == NULL) {
99 path_len = strlen(path);
102 if (
streq(path,
"/manifest.json")) {
103 return path_join(artifact_dir,
"pharos.app.json");
105 if (
streq(path,
"/dispatch-table.json")) {
106 return path_join(artifact_dir,
"dispatch-table.json");
108 if (
streq(path,
"/api/manifest")) {
109 return path_join(artifact_dir,
"pharos.app.json");
113 if (path_len == 0 || (path_len == 1 && path[0] ==
'/')) {
114 return path_join(artifact_dir,
"index.html");
120 return path_join(artifact_dir, relative);
124 if (path[path_len - 1] ==
'/') {
127 char *dir_path =
path_join(artifact_dir, relative);
129 if (dir_path != NULL) {
130 result =
path_join(dir_path,
"index.html");
139 return path_join(artifact_dir, relative);
144 const char *artifact_dir,
148 char *file_path = NULL;
149 char *content_type = NULL;
152 long long content_length = 0;
156 if (connection == NULL || artifact_dir == NULL || path == NULL) {
162 if (strstr(path,
"..") != NULL) {
168 if (file_path == NULL) {
180 char *dir_index =
path_join(file_path,
"index.html");
182 file_path = dir_index;
183 if (file_path == NULL || !
path_exists(file_path)) {
190 file = fopen(file_path,
"rb");
197 if (fseek(file, 0, SEEK_END) != 0) {
202 content_length = (
long long)ftell(file);
203 if (content_length < 0 || fseek(file, 0, SEEK_SET) != 0) {
210 if (content_type == NULL) {
217 if (
starts_with(path,
"/api/") &&
streq(content_type,
"application/octet-stream")) {
219 content_type = strdup(
"application/json");
220 if (content_type == NULL) {
229 "HTTP/1.1 200 OK\r\n"
230 "Content-Type: %s\r\n"
231 "Content-Length: %lld\r\n"
232 "Connection: close\r\n"
244 while ((bytes_read = fread(chunk, 1,
sizeof(chunk), file)) > 0) {
267 const char *base_name = raw_name;
268 char *safe_name = NULL;
270 size_t write_index = 0;
272 if (base_name == NULL) {
276 const char *slash = strrchr(base_name,
'/');
277 const char *backslash = strrchr(base_name,
'\\');
278 if (slash != NULL && (backslash == NULL || slash > backslash)) {
279 base_name = slash + 1;
280 }
else if (backslash != NULL) {
281 base_name = backslash + 1;
284 needed = strlen(base_name) + 1;
285 safe_name = (
char *)malloc(needed);
286 if (safe_name == NULL) {
289 for (index = 0; base_name[index] !=
'\0'; index++) {
290 unsigned char ch = (
unsigned char)base_name[index];
292 if (isalnum(ch) || ch ==
'.' || ch ==
'_' || ch ==
'-') {
294 }
else if (ch ==
' ') {
297 safe_name[write_index++] = out;
299 safe_name[write_index] =
'\0';
300 while (safe_name[0] ==
'_' || safe_name[0] ==
'.' || safe_name[0] ==
'-') {
301 memmove(safe_name, safe_name + 1, strlen(safe_name));
303 while (write_index > 0) {
304 char tail = safe_name[write_index - 1];
305 if (tail !=
'_' && tail !=
'.' && tail !=
'-') {
308 safe_name[--write_index] =
'\0';
314 if (artifact_dir == NULL || artifact_dir[0] ==
'\0' || media_url == NULL || !
starts_with(media_url,
"/media/")) {
317 return path_join(artifact_dir, media_url + 1);
321 char *fixture_path = NULL;
329 const char *entry_type = NULL;
330 char *base_template = NULL;
332 char *base_path = NULL;
335 if (artifact_dir == NULL || entity_type == NULL || entity_slug == NULL ||
336 artifact_dir[0] ==
'\0' || entity_type[0] ==
'\0' || entity_slug[0] ==
'\0') {
341 fixture_path =
path_join(artifact_dir,
"data/framework/media-policy-v1.json");
342 if (fixture_path == NULL) {
361 if (subtrees == NULL) {
369 for (idx = 0; idx < count; idx++) {
379 if (
streq(entry_type, entity_type)) {
392 if (base_template == NULL || base_template[0] ==
'\0') {
400 const char *placeholder =
"{slug}";
401 char *slug_pos = strstr(base_template, placeholder);
402 if (slug_pos != NULL) {
403 size_t prefix_len = (size_t)(slug_pos - base_template);
404 size_t suffix_len = strlen(slug_pos + strlen(placeholder));
405 size_t slug_len = strlen(entity_slug);
406 base_path = (
char *)malloc(prefix_len + slug_len + suffix_len + 1);
407 if (base_path == NULL) {
412 memcpy(base_path, base_template, prefix_len);
413 memcpy(base_path + prefix_len, entity_slug, slug_len);
414 memmove(base_path + prefix_len + slug_len, slug_pos + strlen(placeholder), suffix_len + 1);
416 base_path = strdup(base_template);
417 if (base_path == NULL) {
428 char *full_dir =
path_join(artifact_dir, base_path);
429 if (full_dir == NULL) {
445 if (subdirs != NULL) {
447 for (idx = 0; idx < sub_count; idx++) {
454 char *sub_path =
path_join(base_path, sub_name);
455 char *full_sub_dir = NULL;
456 if (sub_path == NULL) {
461 full_sub_dir =
path_join(artifact_dir, sub_path);
463 if (full_sub_dir == NULL) {
489 char *disk_path = NULL;
490 if (artifact_dir == NULL || artifact_dir[0] ==
'\0' || media_url == NULL || !
starts_with(media_url,
"/media/businesses/")) {
502 const char *artifact_dir,
503 const char *content_type,
504 const char *body_path,
505 const char *field_name,
506 const char *target_url_dir,
507 const char *fallback_filename
509 unsigned char *body_bytes = NULL;
511 const unsigned char *content_start = NULL;
512 size_t content_len = 0;
513 char *raw_filename = NULL;
514 char *safe_filename = NULL;
515 char *target_dir = NULL;
516 char *target_path = NULL;
517 char *media_url = NULL;
518 size_t media_url_len = 0;
520 if (artifact_dir == NULL || artifact_dir[0] ==
'\0' || content_type == NULL || body_path == NULL || body_path[0] ==
'\0' || field_name == NULL || field_name[0] ==
'\0' || target_url_dir == NULL || target_url_dir[0] ==
'\0') {
524 if (body_bytes == NULL) {
531 safe_filename =
safe_upload_filename_dup(raw_filename != NULL && raw_filename[0] !=
'\0' ? raw_filename : fallback_filename);
532 if ((safe_filename == NULL || safe_filename[0] ==
'\0') && fallback_filename != NULL && fallback_filename[0] !=
'\0') {
536 if (safe_filename == NULL || safe_filename[0] ==
'\0') {
540 target_dir =
path_join(artifact_dir, target_url_dir[0] ==
'/' ? target_url_dir + 1 : target_url_dir);
541 target_path = target_dir != NULL ?
path_join(target_dir, safe_filename) : NULL;
542 if (target_dir == NULL || target_path == NULL ||
ensure_directory(target_dir) != 0) {
550 media_url_len = strlen(target_url_dir) + 1 + strlen(safe_filename) + 1;
551 media_url = (
char *)malloc(media_url_len);
552 if (media_url == NULL) {
556 snprintf(media_url, media_url_len,
"%s/%s", target_url_dir, safe_filename);
int native_connection_write(NativeConnection *connection, const void *buffer, size_t len)
Writes bytes to a native connection.
int native_dynamic_multipart_find_part(const unsigned char *body, size_t body_len, const char *content_type, const char *field_name, char **out_text, char **out_filename, const unsigned char **out_content_start, size_t *out_content_len)
Finds a named part in a multipart request body.
Pharos-owned multipart form-data parsing for in-process dynamic handling.
int ensure_directory(const char *path)
Creates a directory path and any missing parent directories.
Declares native filesystem helper routines.
unsigned char * read_file_bytes_dup_runtime(const char *path, size_t *out_size)
Reads the full content of a binary file into a heap-allocated buffer.
int write_binary_file_runtime(const char *path, const unsigned char *data, size_t length)
Writes a binary byte range to a file, creating parent directories as needed.
char * mime_type_for_path(const char *path)
Resolves the MIME type that should be used for a file path.
Declares HTTP logging and response formatting helpers.
void native_send_text_response(NativeConnection *connection, int status, const char *status_text, const char *body)
Sends a plain-text HTTP response with explicit content length.
int native_connection_printf(NativeConnection *connection, const char *format,...)
Writes formatted bytes to a native connection.
Declares native HTTP response writing helpers.
char * path_join(const char *left, const char *right)
Joins two path segments using the native path separator.
int streq(const char *a, const char *b)
Tests whether two strings are exactly equal.
int path_is_directory(const char *path)
Tests whether a filesystem path refers to a directory.
int starts_with(const char *value, const char *prefix)
Tests whether one string starts with another string.
int path_exists(const char *path)
Tests whether a filesystem path currently exists.
Declares shared low-level support helpers used across the Pharos native runtime.
yyjson_doc * native_json_doc_load_file(const char *path)
Parses a JSON file into an owned yyjson document.
void native_json_doc_free(yyjson_doc *doc)
Releases a document returned by the native yyjson helpers.
char * native_json_obj_get_string_dup(yyjson_val *obj, const char *key)
Duplicates a string-valued key from a JSON object.
yyjson_val * native_json_obj_get_array(yyjson_val *obj, const char *key)
Looks up an array-valued key from a JSON object value.
yyjson_val * native_json_obj_get(yyjson_val *obj, const char *key)
Looks up a key from a JSON object value.
Declares shared helper wrappers around vendored yyjson parsing and serialization APIs.
Parsed HTTP request fields extracted from a native connection.
Accepted client connection and its resolved transport metadata.
yyjson_api_inline bool yyjson_is_str(const yyjson_val *val)
yyjson_api_inline bool yyjson_is_obj(const yyjson_val *val)
yyjson_api_inline yyjson_val * yyjson_arr_get(const yyjson_val *arr, size_t idx)
yyjson_api_inline yyjson_val * yyjson_doc_get_root(const yyjson_doc *doc)
yyjson_api_inline size_t yyjson_arr_size(const yyjson_val *arr)
yyjson_api_inline const char * yyjson_get_str(const yyjson_val *val)