Blame


1 50794f47 2023-04-04 op /*
2 50794f47 2023-04-04 op * This file is in the public domain.
3 50794f47 2023-04-04 op */
4 50794f47 2023-04-04 op
5 50794f47 2023-04-04 op #include <sys/tree.h>
6 50794f47 2023-04-04 op
7 50794f47 2023-04-04 op #include <ctype.h>
8 50794f47 2023-04-04 op #include <event.h>
9 50794f47 2023-04-04 op #include <libgen.h>
10 50794f47 2023-04-04 op #include <limits.h>
11 50794f47 2023-04-04 op #include <signal.h>
12 50794f47 2023-04-04 op #include <stdlib.h>
13 50794f47 2023-04-04 op #include <string.h>
14 50794f47 2023-04-04 op #include <unistd.h>
15 50794f47 2023-04-04 op
16 50794f47 2023-04-04 op #include <sqlite3.h>
17 50794f47 2023-04-04 op
18 50794f47 2023-04-04 op #include "msearchd.h"
19 50794f47 2023-04-04 op
20 50794f47 2023-04-04 op char dbpath[PATH_MAX];
21 50794f47 2023-04-04 op
22 50794f47 2023-04-04 op void server_sig_handler(int, short, void *);
23 50794f47 2023-04-04 op void server_open_db(struct env *);
24 50794f47 2023-04-04 op void server_close_db(struct env *);
25 50794f47 2023-04-04 op __dead void server_shutdown(struct env *);
26 50794f47 2023-04-04 op int server_reply(struct client *, int, const char *);
27 ffb578a0 2023-04-04 op int server_urldecode(char *);
28 50794f47 2023-04-04 op char *server_getquery(struct client *);
29 50794f47 2023-04-04 op
30 50794f47 2023-04-04 op void
31 50794f47 2023-04-04 op server_sig_handler(int sig, short ev, void *arg)
32 50794f47 2023-04-04 op {
33 50794f47 2023-04-04 op struct env *env = arg;
34 50794f47 2023-04-04 op
35 50794f47 2023-04-04 op /*
36 50794f47 2023-04-04 op * Normal signal handler rules don't apply here because libevent
37 50794f47 2023-04-04 op * decouples for us.
38 50794f47 2023-04-04 op */
39 50794f47 2023-04-04 op
40 50794f47 2023-04-04 op switch (sig) {
41 50794f47 2023-04-04 op case SIGHUP:
42 50794f47 2023-04-04 op log_info("re-opening the db");
43 50794f47 2023-04-04 op server_close_db(env);
44 50794f47 2023-04-04 op server_open_db(env);
45 50794f47 2023-04-04 op break;
46 50794f47 2023-04-04 op case SIGTERM:
47 50794f47 2023-04-04 op case SIGINT:
48 50794f47 2023-04-04 op server_shutdown(env);
49 50794f47 2023-04-04 op break;
50 50794f47 2023-04-04 op default:
51 50794f47 2023-04-04 op fatalx("unexpected signal %d", sig);
52 50794f47 2023-04-04 op }
53 50794f47 2023-04-04 op }
54 50794f47 2023-04-04 op
55 50794f47 2023-04-04 op static inline void
56 50794f47 2023-04-04 op loadstmt(sqlite3 *db, sqlite3_stmt **stmt, const char *sql)
57 50794f47 2023-04-04 op {
58 50794f47 2023-04-04 op int err;
59 50794f47 2023-04-04 op
60 50794f47 2023-04-04 op err = sqlite3_prepare_v2(db, sql, -1, stmt, NULL);
61 50794f47 2023-04-04 op if (err != SQLITE_OK)
62 50794f47 2023-04-04 op fatalx("failed to prepare statement \"%s\": %s",
63 50794f47 2023-04-04 op sql, sqlite3_errstr(err));
64 50794f47 2023-04-04 op }
65 50794f47 2023-04-04 op
66 50794f47 2023-04-04 op void
67 50794f47 2023-04-04 op server_open_db(struct env *env)
68 50794f47 2023-04-04 op {
69 50794f47 2023-04-04 op int err;
70 50794f47 2023-04-04 op
71 50794f47 2023-04-04 op err = sqlite3_open_v2(dbpath, &env->env_db,
72 50794f47 2023-04-04 op SQLITE_OPEN_READONLY, NULL);
73 50794f47 2023-04-04 op if (err != SQLITE_OK)
74 50794f47 2023-04-04 op fatalx("can't open database %s: %s", dbpath,
75 50794f47 2023-04-04 op sqlite3_errmsg(env->env_db));
76 50794f47 2023-04-04 op
77 50794f47 2023-04-04 op loadstmt(env->env_db, &env->env_query,
78 61acbd55 2023-09-03 op "select mid, \"from\", date, subj,"
79 61acbd55 2023-09-03 op " snippet(email, 4, '<strong>', '</strong>', '...', 32)"
80 50794f47 2023-04-04 op " from email"
81 50794f47 2023-04-04 op " where email match ?"
82 50794f47 2023-04-04 op " order by rank, date"
83 50794f47 2023-04-04 op " limit 100");
84 50794f47 2023-04-04 op }
85 50794f47 2023-04-04 op
86 50794f47 2023-04-04 op void
87 50794f47 2023-04-04 op server_close_db(struct env *env)
88 50794f47 2023-04-04 op {
89 50794f47 2023-04-04 op int err;
90 50794f47 2023-04-04 op
91 50794f47 2023-04-04 op sqlite3_finalize(env->env_query);
92 50794f47 2023-04-04 op
93 50794f47 2023-04-04 op if ((err = sqlite3_close(env->env_db)) != SQLITE_OK)
94 50794f47 2023-04-04 op log_warnx("sqlite3_close %s", sqlite3_errstr(err));
95 50794f47 2023-04-04 op }
96 50794f47 2023-04-04 op
97 50794f47 2023-04-04 op int
98 50794f47 2023-04-04 op server_main(const char *db)
99 50794f47 2023-04-04 op {
100 50794f47 2023-04-04 op char path[PATH_MAX], *parent;
101 50794f47 2023-04-04 op struct env env;
102 50794f47 2023-04-04 op struct event sighup;
103 50794f47 2023-04-04 op struct event sigint;
104 50794f47 2023-04-04 op struct event sigterm;
105 50794f47 2023-04-04 op
106 50794f47 2023-04-04 op signal(SIGPIPE, SIG_IGN);
107 50794f47 2023-04-04 op
108 50794f47 2023-04-04 op memset(&env, 0, sizeof(env));
109 50794f47 2023-04-04 op
110 50794f47 2023-04-04 op if (realpath(db, dbpath) == NULL)
111 50794f47 2023-04-04 op fatal("realpath %s", db);
112 50794f47 2023-04-04 op
113 50794f47 2023-04-04 op strlcpy(path, dbpath, sizeof(path));
114 50794f47 2023-04-04 op parent = dirname(path);
115 50794f47 2023-04-04 op if (unveil(parent, "r") == -1)
116 50794f47 2023-04-04 op fatal("unveil(%s, r)", parent);
117 50794f47 2023-04-04 op
118 50794f47 2023-04-04 op /*
119 50794f47 2023-04-04 op * rpath flock: sqlite3
120 50794f47 2023-04-04 op * unix: accept(2)
121 50794f47 2023-04-04 op */
122 50794f47 2023-04-04 op if (pledge("stdio rpath flock unix", NULL) == -1)
123 50794f47 2023-04-04 op fatal("pledge");
124 50794f47 2023-04-04 op
125 50794f47 2023-04-04 op server_open_db(&env);
126 50794f47 2023-04-04 op
127 50794f47 2023-04-04 op event_init();
128 50794f47 2023-04-04 op
129 50794f47 2023-04-04 op env.env_sockfd = 3;
130 50794f47 2023-04-04 op
131 50794f47 2023-04-04 op event_set(&env.env_sockev, env.env_sockfd, EV_READ|EV_PERSIST,
132 50794f47 2023-04-04 op fcgi_accept, &env);
133 50794f47 2023-04-04 op event_add(&env.env_sockev, NULL);
134 50794f47 2023-04-04 op
135 50794f47 2023-04-04 op evtimer_set(&env.env_pausev, fcgi_accept, &env);
136 50794f47 2023-04-04 op
137 50794f47 2023-04-04 op signal_set(&sighup, SIGHUP, server_sig_handler, &env);
138 50794f47 2023-04-04 op signal_set(&sigint, SIGINT, server_sig_handler, &env);
139 50794f47 2023-04-04 op signal_set(&sigterm, SIGTERM, server_sig_handler, &env);
140 50794f47 2023-04-04 op
141 50794f47 2023-04-04 op signal_add(&sighup, NULL);
142 50794f47 2023-04-04 op signal_add(&sigint, NULL);
143 50794f47 2023-04-04 op signal_add(&sigterm, NULL);
144 50794f47 2023-04-04 op
145 50794f47 2023-04-04 op log_info("ready");
146 50794f47 2023-04-04 op event_dispatch();
147 50794f47 2023-04-04 op
148 50794f47 2023-04-04 op server_shutdown(&env);
149 50794f47 2023-04-04 op }
150 50794f47 2023-04-04 op
151 50794f47 2023-04-04 op void __dead
152 50794f47 2023-04-04 op server_shutdown(struct env *env)
153 50794f47 2023-04-04 op {
154 50794f47 2023-04-04 op log_info("shutting down");
155 50794f47 2023-04-04 op server_close_db(env);
156 50794f47 2023-04-04 op exit(0);
157 50794f47 2023-04-04 op }
158 50794f47 2023-04-04 op
159 50794f47 2023-04-04 op int
160 50794f47 2023-04-04 op server_reply(struct client *clt, int status, const char *arg)
161 50794f47 2023-04-04 op {
162 a3bb069c 2023-09-14 op const char *cps;
163 a3bb069c 2023-09-14 op
164 50794f47 2023-04-04 op if (status != 200 &&
165 50794f47 2023-04-04 op clt_printf(clt, "Status: %d\r\n", status) == -1)
166 50794f47 2023-04-04 op return (-1);
167 50794f47 2023-04-04 op
168 a3bb069c 2023-09-14 op cps = "Content-Security-Policy: default-src 'self'; "
169 a3bb069c 2023-09-14 op "script-src 'none'; object-src 'none';\r\n";
170 a3bb069c 2023-09-14 op if (clt_puts(clt, cps) == -1)
171 a3bb069c 2023-09-14 op return (-1);
172 a3bb069c 2023-09-14 op
173 50794f47 2023-04-04 op if (status == 302) {
174 50794f47 2023-04-04 op if (clt_printf(clt, "Location: %s\r\n", arg) == -1)
175 50794f47 2023-04-04 op return (-1);
176 50794f47 2023-04-04 op arg = NULL;
177 50794f47 2023-04-04 op }
178 50794f47 2023-04-04 op
179 50794f47 2023-04-04 op if (arg != NULL &&
180 50794f47 2023-04-04 op clt_printf(clt, "Content-Type: %s\r\n", arg) == -1)
181 50794f47 2023-04-04 op return (-1);
182 50794f47 2023-04-04 op
183 50794f47 2023-04-04 op return (clt_puts(clt, "\r\n"));
184 50794f47 2023-04-04 op }
185 50794f47 2023-04-04 op
186 50794f47 2023-04-04 op int
187 50794f47 2023-04-04 op server_urldecode(char *s)
188 50794f47 2023-04-04 op {
189 50794f47 2023-04-04 op unsigned int x;
190 50794f47 2023-04-04 op char *q, code[3] = {0};
191 50794f47 2023-04-04 op
192 50794f47 2023-04-04 op q = s;
193 50794f47 2023-04-04 op for (;;) {
194 50794f47 2023-04-04 op if (*s == '\0')
195 50794f47 2023-04-04 op break;
196 50794f47 2023-04-04 op
197 50794f47 2023-04-04 op if (*s == '+') {
198 50794f47 2023-04-04 op *q++ = ' ';
199 50794f47 2023-04-04 op s++;
200 50794f47 2023-04-04 op continue;
201 50794f47 2023-04-04 op }
202 50794f47 2023-04-04 op
203 50794f47 2023-04-04 op if (*s != '%') {
204 50794f47 2023-04-04 op *q++ = *s++;
205 50794f47 2023-04-04 op continue;
206 50794f47 2023-04-04 op }
207 50794f47 2023-04-04 op
208 50794f47 2023-04-04 op if (!isxdigit((unsigned char)s[1]) ||
209 50794f47 2023-04-04 op !isxdigit((unsigned char)s[2]))
210 50794f47 2023-04-04 op return (-1);
211 50794f47 2023-04-04 op code[0] = s[1];
212 50794f47 2023-04-04 op code[1] = s[2];
213 50794f47 2023-04-04 op x = strtoul(code, NULL, 16);
214 50794f47 2023-04-04 op *q++ = (char)x;
215 50794f47 2023-04-04 op s += 3;
216 50794f47 2023-04-04 op }
217 50794f47 2023-04-04 op *q = '\0';
218 50794f47 2023-04-04 op return (0);
219 50794f47 2023-04-04 op }
220 50794f47 2023-04-04 op
221 50794f47 2023-04-04 op char *
222 50794f47 2023-04-04 op server_getquery(struct client *clt)
223 50794f47 2023-04-04 op {
224 50794f47 2023-04-04 op char *tmp, *field;
225 50794f47 2023-04-04 op
226 50794f47 2023-04-04 op tmp = clt->clt_query;
227 50794f47 2023-04-04 op while ((field = strsep(&tmp, "&")) != NULL) {
228 50794f47 2023-04-04 op if (server_urldecode(field) == -1)
229 50794f47 2023-04-04 op continue;
230 50794f47 2023-04-04 op
231 50794f47 2023-04-04 op if (!strncmp(field, "q=", 2))
232 50794f47 2023-04-04 op return (field + 2);
233 50794f47 2023-04-04 op log_info("unknown query param %s", field);
234 50794f47 2023-04-04 op }
235 50794f47 2023-04-04 op
236 50794f47 2023-04-04 op return (NULL);
237 50794f47 2023-04-04 op }
238 50794f47 2023-04-04 op
239 50794f47 2023-04-04 op static inline int
240 50794f47 2023-04-04 op fts_escape(const char *p, char *buf, size_t bufsize)
241 50794f47 2023-04-04 op {
242 50794f47 2023-04-04 op char *q;
243 50794f47 2023-04-04 op
244 50794f47 2023-04-04 op /*
245 50794f47 2023-04-04 op * split p into words and quote them into buf.
246 50794f47 2023-04-04 op * quoting means wrapping each word into "..." and
247 50794f47 2023-04-04 op * replace every " with "".
248 50794f47 2023-04-04 op * i.e. 'C++ "framework"' -> '"C++" """framework"""'
249 50794f47 2023-04-04 op * flatting all the whitespaces seems fine too.
250 50794f47 2023-04-04 op */
251 50794f47 2023-04-04 op
252 50794f47 2023-04-04 op q = buf;
253 50794f47 2023-04-04 op while (bufsize != 0) {
254 50794f47 2023-04-04 op p += strspn(p, " \f\n\r\t\v");
255 50794f47 2023-04-04 op if (*p == '\0')
256 50794f47 2023-04-04 op break;
257 50794f47 2023-04-04 op
258 50794f47 2023-04-04 op *q++ = '"';
259 50794f47 2023-04-04 op bufsize--;
260 50794f47 2023-04-04 op while (*p && !isspace((unsigned char)*p) && bufsize != 0) {
261 50794f47 2023-04-04 op if (*p == '"') { /* double the quote character */
262 50794f47 2023-04-04 op *q++ = '"';
263 50794f47 2023-04-04 op if (--bufsize == 0)
264 50794f47 2023-04-04 op break;
265 50794f47 2023-04-04 op }
266 50794f47 2023-04-04 op *q++ = *p++;
267 50794f47 2023-04-04 op bufsize--;
268 50794f47 2023-04-04 op }
269 50794f47 2023-04-04 op
270 50794f47 2023-04-04 op if (bufsize < 2)
271 50794f47 2023-04-04 op break;
272 50794f47 2023-04-04 op *q++ = '"';
273 50794f47 2023-04-04 op *q++ = ' ';
274 50794f47 2023-04-04 op bufsize -= 2;
275 50794f47 2023-04-04 op }
276 50794f47 2023-04-04 op if ((*p == '\0') && bufsize != 0) {
277 50794f47 2023-04-04 op *q = '\0';
278 50794f47 2023-04-04 op return (0);
279 50794f47 2023-04-04 op }
280 50794f47 2023-04-04 op return (-1);
281 50794f47 2023-04-04 op }
282 50794f47 2023-04-04 op
283 8e5ae9ac 2023-05-05 op static int
284 8e5ae9ac 2023-05-05 op render_tmpl(struct client *clt, const char *tmpl,
285 8e5ae9ac 2023-05-05 op const char *var, const char *val)
286 8e5ae9ac 2023-05-05 op {
287 8e5ae9ac 2023-05-05 op const char *t;
288 8e5ae9ac 2023-05-05 op size_t vlen;
289 8e5ae9ac 2023-05-05 op
290 8e5ae9ac 2023-05-05 op if (var == NULL)
291 8e5ae9ac 2023-05-05 op return (clt_puts(clt, tmpl));
292 8e5ae9ac 2023-05-05 op
293 8e5ae9ac 2023-05-05 op vlen = strlen(var);
294 8e5ae9ac 2023-05-05 op while ((t = strstr(tmpl, var)) != NULL) {
295 8e5ae9ac 2023-05-05 op if (clt_write(clt, tmpl, t - tmpl) == -1 ||
296 8e5ae9ac 2023-05-05 op clt_putsan(clt, val) == -1)
297 8e5ae9ac 2023-05-05 op return (-1);
298 8e5ae9ac 2023-05-05 op tmpl = t + vlen;
299 8e5ae9ac 2023-05-05 op }
300 8e5ae9ac 2023-05-05 op
301 8e5ae9ac 2023-05-05 op return (clt_puts(clt, tmpl));
302 8e5ae9ac 2023-05-05 op }
303 8e5ae9ac 2023-05-05 op
304 50794f47 2023-04-04 op int
305 50794f47 2023-04-04 op server_handle(struct env *env, struct client *clt)
306 50794f47 2023-04-04 op {
307 50794f47 2023-04-04 op char dbuf[64];
308 50794f47 2023-04-04 op char esc[QUERY_MAXLEN];
309 50794f47 2023-04-04 op char *query;
310 61acbd55 2023-09-03 op const char *mid, *from, *subj, *snip;
311 50794f47 2023-04-04 op uint64_t date;
312 50794f47 2023-04-04 op time_t d;
313 50794f47 2023-04-04 op struct tm *tm;
314 181eab11 2023-04-04 op int err, have_results = 0;
315 50794f47 2023-04-04 op
316 50794f47 2023-04-04 op if ((query = server_getquery(clt)) != NULL &&
317 e6af6a01 2023-04-04 op fts_escape(query, esc, sizeof(esc)) != -1 &&
318 e6af6a01 2023-04-04 op *esc != '\0') {
319 50794f47 2023-04-04 op log_debug("searching for %s", esc);
320 50794f47 2023-04-04 op
321 50794f47 2023-04-04 op err = sqlite3_bind_text(env->env_query, 1, esc, -1, NULL);
322 50794f47 2023-04-04 op if (err != SQLITE_OK) {
323 50794f47 2023-04-04 op sqlite3_reset(env->env_query);
324 50794f47 2023-04-04 op if (server_reply(clt, 500, "text/plain") == -1)
325 50794f47 2023-04-04 op return (-1);
326 50794f47 2023-04-04 op if (clt_puts(clt, "Internal server error\n") == -1)
327 50794f47 2023-04-04 op return (-1);
328 50794f47 2023-04-04 op return (fcgi_end_request(clt, 1));
329 50794f47 2023-04-04 op }
330 e6af6a01 2023-04-04 op } else
331 e6af6a01 2023-04-04 op query = NULL;
332 50794f47 2023-04-04 op
333 50794f47 2023-04-04 op if (server_reply(clt, 200, "text/html") == -1)
334 50794f47 2023-04-04 op goto err;
335 50794f47 2023-04-04 op
336 8e5ae9ac 2023-05-05 op if (render_tmpl(clt, tmpl_head, "TITLE", "Search") == -1 ||
337 8e5ae9ac 2023-05-05 op render_tmpl(clt, tmpl_search_header, NULL, NULL) == -1 ||
338 8e5ae9ac 2023-05-05 op render_tmpl(clt, tmpl_search, "QUERY", query) == -1)
339 50794f47 2023-04-04 op goto err;
340 50794f47 2023-04-04 op
341 50794f47 2023-04-04 op if (query == NULL)
342 50794f47 2023-04-04 op goto done;
343 50794f47 2023-04-04 op
344 50794f47 2023-04-04 op if (clt_puts(clt, "<div class='thread'><ul>") == -1)
345 50794f47 2023-04-04 op goto err;
346 50794f47 2023-04-04 op
347 50794f47 2023-04-04 op for (;;) {
348 50794f47 2023-04-04 op err = sqlite3_step(env->env_query);
349 50794f47 2023-04-04 op if (err == SQLITE_DONE)
350 50794f47 2023-04-04 op break;
351 50794f47 2023-04-04 op if (err != SQLITE_ROW) {
352 50794f47 2023-04-04 op log_warnx("%s: sqlite3_step %s", __func__,
353 50794f47 2023-04-04 op sqlite3_errstr(err));
354 50794f47 2023-04-04 op break;
355 50794f47 2023-04-04 op }
356 181eab11 2023-04-04 op
357 181eab11 2023-04-04 op have_results = 1;
358 50794f47 2023-04-04 op
359 50794f47 2023-04-04 op mid = sqlite3_column_text(env->env_query, 0);
360 50794f47 2023-04-04 op from = sqlite3_column_text(env->env_query, 1);
361 50794f47 2023-04-04 op date = sqlite3_column_int64(env->env_query, 2);
362 50794f47 2023-04-04 op subj = sqlite3_column_text(env->env_query, 3);
363 61acbd55 2023-09-03 op snip = sqlite3_column_text(env->env_query, 4);
364 50794f47 2023-04-04 op
365 50794f47 2023-04-04 op if ((sizeof(d) == 4) && date > UINT32_MAX) {
366 50794f47 2023-04-04 op log_warnx("overflow of 32bit time value");
367 50794f47 2023-04-04 op date = 0;
368 50794f47 2023-04-04 op }
369 50794f47 2023-04-04 op
370 50794f47 2023-04-04 op d = date;
371 50794f47 2023-04-04 op if ((tm = gmtime(&d)) == NULL) {
372 50794f47 2023-04-04 op log_warnx("gmtime failure");
373 50794f47 2023-04-04 op continue;
374 50794f47 2023-04-04 op }
375 50794f47 2023-04-04 op
376 50794f47 2023-04-04 op if (strftime(dbuf, sizeof(dbuf), "%F %R", tm) == 0) {
377 50794f47 2023-04-04 op log_warnx("strftime failure");
378 50794f47 2023-04-04 op continue;
379 50794f47 2023-04-04 op }
380 50794f47 2023-04-04 op
381 50794f47 2023-04-04 op if (clt_puts(clt, "<li class='mail'>"
382 50794f47 2023-04-04 op "<p class='mail-meta'><time>") == -1 ||
383 50794f47 2023-04-04 op clt_putsan(clt, dbuf) == -1 ||
384 50794f47 2023-04-04 op clt_puts(clt, "</time> <span class='from'>") == -1 ||
385 50794f47 2023-04-04 op clt_putsan(clt, from) == -1 ||
386 50794f47 2023-04-04 op clt_puts(clt, "</span><span class=colon>:</span>") == -1 ||
387 50794f47 2023-04-04 op clt_puts(clt, "</p>"
388 50794f47 2023-04-04 op "<p class='subject'>"
389 50794f47 2023-04-04 op "<a href='/mail/") == -1 ||
390 50794f47 2023-04-04 op clt_putsan(clt, mid) == -1 ||
391 50794f47 2023-04-04 op clt_puts(clt, ".html'>") == -1 ||
392 50794f47 2023-04-04 op clt_putsan(clt, subj) == -1 ||
393 61acbd55 2023-09-03 op clt_puts(clt, "</a></p><p class=excerpt>") == -1 ||
394 61acbd55 2023-09-03 op clt_putmatch(clt, snip) == -1 ||
395 61acbd55 2023-09-03 op clt_puts(clt, "</p></li>") == -1)
396 50794f47 2023-04-04 op goto err;
397 50794f47 2023-04-04 op }
398 50794f47 2023-04-04 op
399 50794f47 2023-04-04 op if (clt_puts(clt, "</ul></div>") == -1)
400 50794f47 2023-04-04 op goto err;
401 50794f47 2023-04-04 op
402 181eab11 2023-04-04 op if (!have_results &&
403 181eab11 2023-04-04 op clt_puts(clt, "<p class='notice'>No mail found.</p>") == -1)
404 181eab11 2023-04-04 op goto err;
405 181eab11 2023-04-04 op
406 50794f47 2023-04-04 op done:
407 8e5ae9ac 2023-05-05 op if (render_tmpl(clt, tmpl_foot, NULL, NULL) == -1)
408 50794f47 2023-04-04 op goto err;
409 50794f47 2023-04-04 op
410 50794f47 2023-04-04 op sqlite3_reset(env->env_query);
411 50794f47 2023-04-04 op return (fcgi_end_request(clt, 0));
412 50794f47 2023-04-04 op err:
413 50794f47 2023-04-04 op sqlite3_reset(env->env_query);
414 50794f47 2023-04-04 op return (-1);
415 50794f47 2023-04-04 op }
416 50794f47 2023-04-04 op
417 50794f47 2023-04-04 op void
418 50794f47 2023-04-04 op server_client_free(struct client *clt)
419 50794f47 2023-04-04 op {
420 50794f47 2023-04-04 op free(clt->clt_server_name);
421 50794f47 2023-04-04 op free(clt->clt_script_name);
422 50794f47 2023-04-04 op free(clt->clt_path_info);
423 50794f47 2023-04-04 op free(clt->clt_query);
424 50794f47 2023-04-04 op free(clt);
425 50794f47 2023-04-04 op }