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