commit ab03866

uint  ·  2026-04-28 20:06:07 +0000 UTC
parent 097a8d7
add doxygen comments to static functions
7 files changed,  +339, -1
+30, -0
 1@@ -23,6 +23,13 @@ int http_io_timeout = 5;
 2 int max_clients = 64;
 3 int auth_delay = 250;
 4 
 5+/**
 6+ * @brief Load configuration from a file path
 7+ *
 8+ * @param path Path to config file
 9+ *
10+ * @return 0=Success, -1=Failure
11+ */
12 static int load_path(const char* path)
13 {
14 	FILE* f = fopen(path, "r");
15@@ -62,6 +69,14 @@ static int load_path(const char* path)
16 	return 0;
17 }
18 
19+/**
20+ * @brief Parse bool string value
21+ *
22+ * @param s Input string
23+ * @param out Output bool
24+ *
25+ * @return 0=Success, -1=Failure
26+ */
27 static int parse_bool(const char* s, bool* out)
28 {
29 	if (!s || !out)
30@@ -77,6 +92,14 @@ static int parse_bool(const char* s, bool* out)
31 	return 0;
32 }
33 
34+/**
35+ * @brief Apply a config key/value pair
36+ *
37+ * @param k Config key
38+ * @param v Config value
39+ *
40+ * @return 0=Success, -1=Failure
41+ */
42 static int set_kv(const char* k, const char* v)
43 {
44 	/* regular */
45@@ -201,6 +224,13 @@ static int set_kv(const char* k, const char* v)
46 	return 0;
47 }
48 
49+/**
50+ * @brief Trim leading and trailing whitespace
51+ *
52+ * @param s Input string
53+ *
54+ * @return Ptr to trimmed string
55+ */
56 static char* trim(char* s)
57 {
58 	while (*s && (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n'))
+138, -0
  1@@ -40,6 +40,11 @@ static int stream_file(int c, const struct item* it, const char* hdr, int head_o
  2 extern struct library lib;
  3 extern mtx_t lib_lock;
  4 
  5+/**
  6+ * @brief Sleep for configured auth delay
  7+ *
  8+ * @note Retries on EINTR.
  9+ */
 10 static void auth_delay_sleep(void)
 11 {
 12 	if (auth_delay <= 0)
 13@@ -59,6 +64,13 @@ static void auth_delay_sleep(void)
 14 	}
 15 }
 16 
 17+/**
 18+ * @brief Check whether origin is allowed by CORS config
 19+ *
 20+ * @param origin Request Origin header value
 21+ *
 22+ * @return 1=Allowed, 0=Not allowed
 23+ */
 24 static int cors_origin_allowed(const char* origin)
 25 {
 26 	if (cors_origin[0] == '\0')
 27@@ -92,6 +104,16 @@ static int cors_origin_allowed(const char* origin)
 28 	return 0;
 29 }
 30 
 31+/**
 32+ * @brief Build CORS response headers
 33+ *
 34+ * @param out Output buffer
 35+ * @param outsz Output buffer size
 36+ * @param hdr Request header block
 37+ * @param preflight Whether this is a preflight response
 38+ *
 39+ * @return 1=Built, 0=Not emitted
 40+ */
 41 static int cors_build(char* out, size_t outsz, const char* hdr, int preflight)
 42 {
 43 	char origin[512];
 44@@ -144,6 +166,13 @@ static int cors_build(char* out, size_t outsz, const char* hdr, int preflight)
 45 	return 1;
 46 }
 47 
 48+/**
 49+ * @brief Find library item by id
 50+ *
 51+ * @param id Item id
 52+ *
 53+ * @return Item Ptr=Found, NULL=Not found
 54+ */
 55 static const struct item* find_item(uint64_t id)
 56 {
 57 	for (size_t i = 0; i < lib.len; i++)
 58@@ -153,6 +182,15 @@ static const struct item* find_item(uint64_t id)
 59 	return NULL;
 60 }
 61 
 62+/**
 63+ * @brief Resolve relative item path for a given id
 64+ *
 65+ * @param out Output path buffer
 66+ * @param id Item id
 67+ * @param u Authenticated user filter
 68+ *
 69+ * @return 0=Success, 1=Not found, -1=Failure
 70+ */
 71 static int item_path_for_id(char out[4096], uint64_t id, const struct user* u)
 72 {
 73 	int ret = -1;
 74@@ -187,6 +225,13 @@ out:
 75 	return ret; /* 0 ok, 1 not found, -1 error */
 76 }
 77 
 78+/**
 79+ * @brief Infer MIME type from file path extension
 80+ *
 81+ * @param path File path
 82+ *
 83+ * @return MIME type string
 84+ */
 85 static const char* mime_from_path(const char* path)
 86 {
 87 	const char* dot = strrchr(path, '.');
 88@@ -240,6 +285,14 @@ static const char* mime_from_path(const char* path)
 89 	return "application/octet-stream";
 90 }
 91 
 92+/**
 93+ * @brief Parse 16-digit hex string to uint64
 94+ *
 95+ * @param s Input string
 96+ * @param out Output value
 97+ *
 98+ * @return 0=Success, -1=Failure
 99+ */
100 static int parse_hex64(const char* s, uint64_t* out)
101 {
102 	if (!s || !out)
103@@ -264,6 +317,16 @@ static int parse_hex64(const char* s, uint64_t* out)
104 	return 0;
105 }
106 
107+/**
108+ * @brief Parse HTTP Range header
109+ *
110+ * @param hdr Request header block
111+ * @param total Total file size in bytes
112+ * @param start Output start offset
113+ * @param end Output end offset
114+ *
115+ * @return RANGE_NONE=No header, RANGE_OK=Valid range, RANGE_BAD=Malformed, RANGE_UNSAT=Unsatisfiable
116+ */
117 static int parse_range(const char* hdr, size_t total, size_t* start, size_t* end)
118 {
119 	const char* p = cistrstr(hdr, "\nrange:");
120@@ -349,6 +412,19 @@ static int parse_range(const char* hdr, size_t total, size_t* start, size_t* end
121 	return RANGE_OK;
122 }
123 
124+/**
125+ * @brief Read and parse a single HTTP request
126+ *
127+ * @param c Connected client socket file descriptor
128+ * @param method Output method buffer
129+ * @param msz Method buffer size
130+ * @param path Output path buffer
131+ * @param psz Path buffer size
132+ * @param hdr Output raw header buffer
133+ * @param hsz Header buffer size
134+ *
135+ * @return 0=Success, -1=Failure
136+ */
137 static int read_request(int c, char* method, size_t msz, char* path, size_t psz, char* hdr, size_t hsz)
138 {
139 	size_t used = 0;
140@@ -389,6 +465,19 @@ static int read_request(int c, char* method, size_t msz, char* path, size_t psz,
141 	return 0;
142 }
143 
144+/**
145+ * @brief Send HTTP response header and optional body
146+ *
147+ * @param c Connected client socket file descriptor
148+ * @param hdr Request header block
149+ * @param status HTTP status line
150+ * @param ctype Content-Type header string
151+ * @param extra Extra response headers
152+ * @param body Response body
153+ * @param len Response body length
154+ * @param send_body Whether to send body bytes
155+ * @param preflight Whether this is a preflight response
156+ */
157 static void reply(int c, const char* hdr, const char* status, const char* ctype, const char* extra, const void* body, size_t len, int send_body, int preflight)
158 {
159 	char resp[HTTP_RESP_MAX];
160@@ -429,22 +518,53 @@ static void reply(int c, const char* hdr, const char* status, const char* ctype,
161 		(void)write_all(c, body, len);
162 }
163 
164+/**
165+ * @brief Send JSON HTTP response
166+ *
167+ * @param c Connected client socket file descriptor
168+ * @param hdr Request header block
169+ * @param status HTTP status line
170+ * @param body Response body
171+ * @param len Response body length
172+ * @param send_body Whether to send body bytes
173+ */
174 static void reply_json(int c, const char* hdr, const char* status, const char* body, size_t len, int send_body)
175 {
176 	reply(c, hdr, status, HTTP_JSON, NULL, body, len, send_body, 0);
177 }
178 
179+/**
180+ * @brief Send CORS preflight response
181+ *
182+ * @param c Connected client socket file descriptor
183+ * @param hdr Request header block
184+ */
185 static void reply_preflight(int c, const char* hdr)
186 {
187 	reply(c, hdr, HTTP_204, NULL, NULL, NULL, (size_t)0, 0, 1);
188 }
189 
190+/**
191+ * @brief Send text/plain HTTP response
192+ *
193+ * @param c Connected client socket file descriptor
194+ * @param hdr Request header block
195+ * @param status HTTP status line
196+ * @param body Response body
197+ */
198 static void reply_text(int c, const char* hdr, const char* status, const char* body)
199 {
200 	size_t len = strlen(body);
201 	reply(c, hdr, status, HTTP_TEXT, NULL, body, len, 1, 0);
202 }
203 
204+/**
205+ * @brief Send 401 Unauthorized response
206+ *
207+ * @param c Connected client socket file descriptor
208+ * @param hdr Request header block
209+ * @param send_body Whether to send body bytes
210+ */
211 static void reply_unauth(int c, const char* hdr, int send_body)
212 {
213 	const char* body = "unauthorized\n";
214@@ -464,6 +584,14 @@ static void reply_unauth(int c, const char* hdr, int send_body)
215 	);
216 }
217 
218+/**
219+ * @brief Stat media item on disk
220+ *
221+ * @param it Library item
222+ * @param st Output stat buffer
223+ *
224+ * @return 0=Success, -1=Failure
225+ */
226 static int stat_item(const struct item* it, struct stat* st)
227 {
228 	char full[4096];
229@@ -480,6 +608,16 @@ static int stat_item(const struct item* it, struct stat* st)
230 	return 0;
231 }
232 
233+/**
234+ * @brief Stream item file to HTTP client
235+ *
236+ * @param c Connected client socket file descriptor
237+ * @param it Library item
238+ * @param hdr Request header block
239+ * @param head_only Whether request method is HEAD
240+ *
241+ * @return 0=Handled, -1=Failure
242+ */
243 static int stream_file(int c, const struct item* it, const char* hdr, int head_only)
244 {
245 	char full[4096];
+63, -0
  1@@ -16,6 +16,13 @@ static int json_putn(struct json* j, const char* s, size_t n);
  2 static int json_puts(struct json* j, const char* s);
  3 static int json_string(struct json* j, const char* s);
  4 
  5+/**
  6+ * @brief Extract basename from path
  7+ *
  8+ * @param path Input path
  9+ *
 10+ * @return Ptr to basename
 11+ */
 12 static const char* json_basename(const char* path)
 13 {
 14 	if (!path)
 15@@ -25,6 +32,14 @@ static const char* json_basename(const char* path)
 16 	return p ? (p + 1) : path;
 17 }
 18 
 19+/**
 20+ * @brief Grow JSON buffer capacity
 21+ *
 22+ * @param j JSON buffer
 23+ * @param need Additional bytes needed
 24+ *
 25+ * @return 0=Success, -1=Failure
 26+ */
 27 static int json_grow(struct json* j, size_t need)
 28 {
 29 	if (j->len + need <= j->cap)
 30@@ -46,6 +61,14 @@ static int json_grow(struct json* j, size_t need)
 31 	return 0;
 32 }
 33 
 34+/**
 35+ * @brief Encode uint64 as 16-digit hex string
 36+ *
 37+ * @param j Output JSON buffer
 38+ * @param v Input value
 39+ *
 40+ * @return 0=Success, -1=Failure
 41+ */
 42 static int json_hex64(struct json* j, uint64_t v)
 43 {
 44 	char hex[17];
 45@@ -60,6 +83,13 @@ static int json_hex64(struct json* j, uint64_t v)
 46 	return json_string(j, hex);
 47 }
 48 
 49+/**
 50+ * @brief Map MIME type to item kind
 51+ *
 52+ * @param type MIME type string
 53+ *
 54+ * @return Item kind string
 55+ */
 56 static const char* json_kind_from_type(const char* type)
 57 {
 58 	if (!type || type[0] == '\0')
 59@@ -75,11 +105,28 @@ static const char* json_kind_from_type(const char* type)
 60 	return "other";
 61 }
 62 
 63+/**
 64+ * @brief Append single byte to JSON buffer
 65+ *
 66+ * @param j Output JSON buffer
 67+ * @param c Byte to append
 68+ *
 69+ * @return 0=Success, -1=Failure
 70+ */
 71 static int json_putc(struct json* j, char c)
 72 {
 73 	return json_putn(j, &c, 1);
 74 }
 75 
 76+/**
 77+ * @brief Append byte range to JSON buffer
 78+ *
 79+ * @param j Output JSON buffer
 80+ * @param s Input bytes
 81+ * @param n Number of bytes to append
 82+ *
 83+ * @return 0=Success, -1=Failure
 84+ */
 85 static int json_putn(struct json* j, const char* s, size_t n)
 86 {
 87 	if (json_grow(j, n + 1) < 0)
 88@@ -92,11 +139,27 @@ static int json_putn(struct json* j, const char* s, size_t n)
 89 	return 0;
 90 }
 91 
 92+/**
 93+ * @brief Append string to JSON buffer
 94+ *
 95+ * @param j Output JSON buffer
 96+ * @param s Input string
 97+ *
 98+ * @return 0=Success, -1=Failure
 99+ */
100 static int json_puts(struct json* j, const char* s)
101 {
102 	return json_putn(j, s, strlen(s));
103 }
104 
105+/**
106+ * @brief Append JSON string with escaping
107+ *
108+ * @param j Output JSON buffer
109+ * @param s Input string
110+ *
111+ * @return 0=Success, -1=Failure
112+ */
113 static int json_string(struct json* j, const char* s)
114 {
115 	/* opening quote */
+28, -0
 1@@ -55,6 +55,9 @@ mtx_t lib_lock;
 2 static mtx_t slots_lock;
 3 static int slots_available;
 4 
 5+/**
 6+ * @brief Apply process resource limits
 7+ */
 8 static void apply_rlimits(void)
 9 {
10 	struct rlimit rl;
11@@ -68,6 +71,13 @@ static void apply_rlimits(void)
12 	(void)setrlimit(RLIMIT_NOFILE, &rl);
13 }
14 
15+/**
16+ * @brief Handle a single accepted client connection
17+ *
18+ * @param arg Client socket cast through void*
19+ *
20+ * @return 0=Success
21+ */
22 static int client_thread(void* arg)
23 {
24 	int c = (int)(intptr_t)arg;
25@@ -81,6 +91,11 @@ static int client_thread(void* arg)
26 	return 0;
27 }
28 
29+/**
30+ * @brief Set close on exec on file descriptor
31+ *
32+ * @param fd File descriptor
33+ */
34 static void fd_set_cloexec(int fd)
35 {
36 	int f = fcntl(fd, F_GETFD);
37@@ -88,6 +103,11 @@ static void fd_set_cloexec(int fd)
38 		(void)fcntl(fd, F_SETFD, f | FD_CLOEXEC);
39 }
40 
41+/**
42+ * @brief Apply configured socket IO timeouts
43+ *
44+ * @param fd File descriptor
45+ */
46 static void sock_set_timeouts(int fd)
47 {
48 	struct timeval tv;
49@@ -98,6 +118,9 @@ static void sock_set_timeouts(int fd)
50 	(void)setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
51 }
52 
53+/**
54+ * @brief Release one client slot back to the pool
55+ */
56 static void release_slot(void)
57 {
58 	if (mtx_lock(&slots_lock) != thrd_success)
59@@ -109,6 +132,11 @@ static void release_slot(void)
60 	(void)mtx_unlock(&slots_lock);
61 }
62 
63+/**
64+ * @brief Try to reserve one client slot
65+ *
66+ * @return 1=Acquired, 0=Full, -1=Failure
67+ */
68 static int try_acquire_slot(void)
69 {
70 	int ok = 0;
+24, -0
 1@@ -17,6 +17,13 @@ static uint64_t encode_fnv1a64(const char* s);
 2 static int library_push(struct library* l, const char* rel);
 3 static int scan_dir(struct library* l, const char* root, const char* rel);
 4 
 5+/**
 6+ * @brief Encode string using FNV1a64
 7+ *
 8+ * @param s Input string
 9+ *
10+ * @return 64bit hash value
11+ */
12 static uint64_t encode_fnv1a64(const char* s)
13 {
14 	/* standard fnv1a64 encoding */
15@@ -30,6 +37,14 @@ static uint64_t encode_fnv1a64(const char* s)
16 	return h;
17 }
18 
19+/**
20+ * @brief Append item path to library
21+ *
22+ * @param l Output library
23+ * @param rel Relative item path
24+ *
25+ * @return 0=Success, -1=Failure
26+ */
27 static int library_push(struct library* l, const char* rel)
28 {
29 	if (l->len == l->cap) {
30@@ -54,6 +69,15 @@ static int library_push(struct library* l, const char* rel)
31 	return 0;
32 }
33 
34+/**
35+ * @brief Recursively scan one directory subtree
36+ *
37+ * @param l Output library
38+ * @param root Media root directory
39+ * @param rel Relative path within root
40+ *
41+ * @return 0=Success, -1=Failure
42+ */
43 static int scan_dir(struct library* l, const char* root, const char* rel)
44 {
45 	char full[4096];
+47, -0
 1@@ -15,6 +15,14 @@ static int b64_val(int c);
 2 static int ct_equal(const char* a, const char* b);
 3 static const struct user* find_user(const char* name);
 4 
 5+/**
 6+ * @brief Append allow prefix to user entry
 7+ *
 8+ * @param u User entry
 9+ * @param s Allow prefix string
10+ *
11+ * @return 0=Success, -1=Failure
12+ */
13 static int allow_push(struct user* u, const char* s)
14 {
15 	if (u->allow_len == u->allow_cap) {
16@@ -35,6 +43,15 @@ static int allow_push(struct user* u, const char* s)
17 	return 0;
18 }
19 
20+/**
21+ * @brief Decode base64 string
22+ *
23+ * @param out Output buffer
24+ * @param outsz Output buffer size
25+ * @param in Input base64 string
26+ *
27+ * @return 0=Success, -1=Failure
28+ */
29 static int b64_decode(unsigned char* out, size_t outsz, const char* in)
30 {
31 	size_t olen = 0;
32@@ -68,6 +85,13 @@ static int b64_decode(unsigned char* out, size_t outsz, const char* in)
33 	return 0;
34 }
35 
36+/**
37+ * @brief Map base64 byte to sextet value
38+ *
39+ * @param c Input byte
40+ *
41+ * @return 0..63=Value, -2=Padding, -1=Invalid
42+ */
43 static int b64_val(int c)
44 {
45 	if (c >= 'A' && c <= 'Z')
46@@ -85,6 +109,14 @@ static int b64_val(int c)
47 
48 	return -1;
49 }
50+/**
51+ * @brief Compare two strings in constant time
52+ *
53+ * @param a Left string
54+ * @param b Right string
55+ *
56+ * @return 1=Equal, 0=Not equal
57+ */
58 static int ct_equal(const char* a, const char* b)
59 {
60 	size_t la = strlen(a);
61@@ -102,6 +134,13 @@ static int ct_equal(const char* a, const char* b)
62 	return (diff == 0) && (la == lb);
63 }
64 
65+/**
66+ * @brief Find user by name
67+ *
68+ * @param name Username
69+ *
70+ * @return User Ptr=Found, NULL=Not found
71+ */
72 static const struct user* find_user(const char* name)
73 {
74 	for (size_t i = 0; i < users.len; i++)
75@@ -111,6 +150,14 @@ static const struct user* find_user(const char* name)
76 	return NULL;
77 }
78 
79+/**
80+ * @brief Check whether path matches allow prefix
81+ *
82+ * @param path Relative media path
83+ * @param pre Allow prefix
84+ *
85+ * @return 1=Match, 0=No match
86+ */
87 static int prefix_ok(const char* path, const char* pre)
88 {
89 	size_t n = strlen(pre);
+9, -1
 1@@ -8,6 +8,15 @@
 2 
 3 static int ci_key_match(const char* a, const char* b, size_t n);
 4 
 5+/**
 6+ * @brief Compare two header keys case-insensitively
 7+ *
 8+ * @param a Left string
 9+ * @param b Right string
10+ * @param n Number of bytes to compare
11+ *
12+ * @return 1=Match, 0=No match
13+ */
14 static int ci_key_match(const char* a, const char* b, size_t n)
15 {
16 	for (size_t i = 0; i < n; i++) {
17@@ -160,4 +169,3 @@ int write_all(int fd, const void* buf, size_t n)
18 
19 	return 0;
20 }
21-