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