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-