1 |
#include "http_error.h" |
2 |
#include "html/default_layout.h" |
3 |
#include "log.h" |
4 |
#include "response.h" |
5 |
|
6 |
#include <stddef.h> |
7 |
#include <stdlib.h> |
8 |
#include <string.h> |
9 |
|
10 |
#define SERVER_SIGNATURE \ |
11 |
"FreeHTTPD/1.0.0 (Ubuntu 24.04 LTS) Server at localhost" |
12 |
|
13 |
/* clang-format off */ |
14 |
static unsigned int valid_statuses[] = { |
15 |
200, 201, 202, 204, 206, |
16 |
300, 301, 302, 303, 304, 307, 308, |
17 |
400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, |
18 |
414, 415, 416, 417, 418, 421, 422, 423, 424, 425, 426, 428, 429, 431, 451, |
19 |
500, 501, 502, 503, 504, 505, 506, 507, 508, 510, 511, |
20 |
}; |
21 |
/* clang-format on */ |
22 |
|
23 |
static unsigned int valid_statuses_count |
24 |
= sizeof (valid_statuses) / sizeof (valid_statuses[0]); |
25 |
|
26 |
freehttpd_errdoc_tbl_t * |
27 |
freehttpd_error_document_tbl_init (void) |
28 |
{ |
29 |
freehttpd_errdoc_tbl_t *errdoc_tbl |
30 |
= calloc (1, sizeof (freehttpd_errdoc_tbl_t)); |
31 |
|
32 |
if (errdoc_tbl == NULL) |
33 |
return NULL; |
34 |
|
35 |
errdoc_tbl->errdocs |
36 |
= calloc (valid_statuses_count, sizeof (freehttpd_errdoc_t)); |
37 |
|
38 |
if (errdoc_tbl->errdocs == NULL) |
39 |
return NULL; |
40 |
|
41 |
errdoc_tbl->errdocs_cap = valid_statuses_count; |
42 |
return errdoc_tbl; |
43 |
} |
44 |
|
45 |
void |
46 |
freehttpd_error_document_tbl_free (freehttpd_errdoc_tbl_t *errdoc_tbl) |
47 |
{ |
48 |
if (errdoc_tbl == NULL) |
49 |
return; |
50 |
|
51 |
if (errdoc_tbl->errdocs != NULL) |
52 |
{ |
53 |
for (size_t i = 0; i < errdoc_tbl->errdocs_count; i++) |
54 |
{ |
55 |
freehttpd_errdoc_t *errdoc = &errdoc_tbl->errdocs[i]; |
56 |
|
57 |
if (errdoc->auto_free == true && errdoc->document != NULL) |
58 |
free ((void *) errdoc->document); |
59 |
} |
60 |
|
61 |
free (errdoc_tbl->errdocs); |
62 |
} |
63 |
|
64 |
free (errdoc_tbl); |
65 |
} |
66 |
|
67 |
static unsigned int |
68 |
hash_function (freehttpd_status_t status) |
69 |
{ |
70 |
return status % valid_statuses_count; |
71 |
} |
72 |
|
73 |
const freehttpd_errdoc_t * |
74 |
freehttpd_error_document_get (freehttpd_errdoc_tbl_t *errdoc_tbl, |
75 |
freehttpd_status_t status) |
76 |
{ |
77 |
unsigned int hash = hash_function (status); |
78 |
|
79 |
if (hash >= errdoc_tbl->errdocs_cap) |
80 |
{ |
81 |
log_err (LOG_ERR "Invalid status code: %d: Hash overflow", status); |
82 |
return NULL; |
83 |
} |
84 |
|
85 |
freehttpd_errdoc_t *errdoc = &errdoc_tbl->errdocs[hash]; |
86 |
bool second_round = false; |
87 |
unsigned int index = hash; |
88 |
|
89 |
while (errdoc_tbl->errdocs[index].status != status) |
90 |
{ |
91 |
if (errdoc_tbl->errdocs[index].status == 0) |
92 |
return NULL; |
93 |
|
94 |
index++; |
95 |
|
96 |
if (index >= errdoc_tbl->errdocs_cap) |
97 |
{ |
98 |
second_round = true; |
99 |
index = 0; |
100 |
} |
101 |
|
102 |
if (second_round && index >= hash) |
103 |
return NULL; |
104 |
} |
105 |
|
106 |
return errdoc; |
107 |
} |
108 |
|
109 |
freehttpd_errdoc_t * |
110 |
freehttpd_error_document_set (freehttpd_errdoc_tbl_t *errdoc_tbl, |
111 |
freehttpd_status_t status, const char *document, |
112 |
size_t document_length) |
113 |
{ |
114 |
unsigned int hash = hash_function (status); |
115 |
freehttpd_errdoc_t *errdoc = NULL; |
116 |
|
117 |
if (hash >= errdoc_tbl->errdocs_cap) |
118 |
{ |
119 |
log_err (LOG_ERR "Invalid status code: %d: Hash overflow: %u\n", |
120 |
status, hash); |
121 |
return NULL; |
122 |
} |
123 |
|
124 |
if (errdoc_tbl->errdocs_count >= errdoc_tbl->errdocs_cap) |
125 |
{ |
126 |
log_err (LOG_ERR "Error documents table is full\n"); |
127 |
return NULL; |
128 |
} |
129 |
|
130 |
errdoc = &errdoc_tbl->errdocs[hash]; |
131 |
|
132 |
if (errdoc->document != NULL && errdoc->auto_free == true) |
133 |
free ((void *) errdoc->document); |
134 |
|
135 |
errdoc->status = status; |
136 |
errdoc->document = document; |
137 |
errdoc->document_length = document_length; |
138 |
errdoc_tbl->errdocs_count++; |
139 |
errdoc->auto_free = true; |
140 |
|
141 |
return errdoc; |
142 |
} |
143 |
|
144 |
void |
145 |
freehttpd_error_document_load_defaults (freehttpd_errdoc_tbl_t *errdoc_tbl) |
146 |
{ |
147 |
const char *placeholders[] = { |
148 |
"{FREEHTTPD_STATUS}", |
149 |
"{FREEHTTPD_STATUS_TEXT}", |
150 |
"{FREEHTTPD_SIGNATURE}", |
151 |
"{FREEHTTPD_STATUS_DESCRIPTION}", |
152 |
}; |
153 |
|
154 |
for (unsigned int i = 0; |
155 |
i < sizeof (valid_statuses) / sizeof (valid_statuses[0]); i++) |
156 |
{ |
157 |
freehttpd_errdoc_t *doc = freehttpd_error_document_set ( |
158 |
errdoc_tbl, valid_statuses[i], |
159 |
(const char *) default_layout_html, default_layout_html_len); |
160 |
|
161 |
char status_code[4]; |
162 |
const char *values[] = { |
163 |
status_code, |
164 |
freehttpd_response_status_text (valid_statuses[i]), |
165 |
SERVER_SIGNATURE, |
166 |
freehttpd_response_status_description (valid_statuses[i]), |
167 |
}; |
168 |
|
169 |
snprintf (status_code, sizeof (status_code), "%d", |
170 |
valid_statuses[i]); |
171 |
|
172 |
doc->auto_free = false; |
173 |
|
174 |
for (unsigned int j = 0; |
175 |
j < sizeof (placeholders) / sizeof (placeholders[0]); j++) |
176 |
{ |
177 |
char *placeholder |
178 |
= strstr ((char *) doc->document, placeholders[j]); |
179 |
|
180 |
while (placeholder != NULL) |
181 |
{ |
182 |
size_t placeholder_len = strlen (placeholders[j]); |
183 |
size_t value_len = strlen (values[j]); |
184 |
size_t new_len = doc->document_length + value_len |
185 |
- placeholder_len; |
186 |
char buffer[new_len + 1]; |
187 |
bzero (buffer, sizeof (buffer)); |
188 |
|
189 |
memcpy (buffer, doc->document, |
190 |
placeholder - doc->document); |
191 |
memcpy (buffer + (placeholder - doc->document), |
192 |
values[j], value_len); |
193 |
memcpy (buffer + (placeholder - doc->document) |
194 |
+ value_len, |
195 |
placeholder + placeholder_len, |
196 |
doc->document_length |
197 |
- (placeholder - doc->document) |
198 |
- placeholder_len); |
199 |
|
200 |
buffer[new_len] = 0; |
201 |
|
202 |
if (doc->auto_free == true) |
203 |
free ((void *) doc->document); |
204 |
else |
205 |
doc->auto_free = true; |
206 |
|
207 |
doc->document_length = new_len; |
208 |
doc->document = strdup (buffer); |
209 |
placeholder = strstr ((char *) doc->document, |
210 |
placeholders[j]); |
211 |
} |
212 |
} |
213 |
} |
214 |
} |