the option has been changed to --outform
[strongswan.git] / src / libfast / request.c
1 /*
2 * Copyright (C) 2007 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #define _GNU_SOURCE
17
18 #include "request.h"
19
20 #include <library.h>
21 #include <debug.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <pthread.h>
25 #include <ClearSilver/ClearSilver.h>
26
27 typedef struct private_request_t private_request_t;
28
29 /**
30 * private data of the task manager
31 */
32 struct private_request_t {
33
34 /**
35 * public functions
36 */
37 request_t public;
38
39 /**
40 * FastCGI request object
41 */
42 FCGX_Request req;
43
44 /**
45 * length of the req.envp array
46 */
47 int req_env_len;
48
49 /**
50 * ClearSilver CGI Kit context
51 */
52 CGI *cgi;
53
54 /**
55 * ClearSilver HDF dataset for this request
56 */
57 HDF *hdf;
58
59 /**
60 * close the session?
61 */
62 bool closed;
63
64 /**
65 * reference count
66 */
67 refcount_t ref;
68 };
69
70 /**
71 * key to a the threads "this" request, used for ClearSilver cgiwrap callbacks.
72 * ClearSilver cgiwrap is not threadsave, so we use a private
73 * context for each thread.
74 */
75 static pthread_key_t this_key;
76
77 /**
78 * control variable for pthread_once
79 */
80 pthread_once_t once = PTHREAD_ONCE_INIT;
81
82 /**
83 * fcgiwrap read callback
84 */
85 static int read_cb(void *null, char *buf, int size)
86 {
87 private_request_t *this = (private_request_t*)pthread_getspecific(this_key);
88
89 return FCGX_GetStr(buf, size, this->req.in);
90 }
91
92 /**
93 * fcgiwrap writef callback
94 */
95 static int writef_cb(void *null, const char *format, va_list args)
96 {
97 private_request_t *this = (private_request_t*)pthread_getspecific(this_key);
98
99 FCGX_VFPrintF(this->req.out, format, args);
100 return 0;
101 }
102 /**
103 * fcgiwrap write callback
104 */
105 static int write_cb(void *null, const char *buf, int size)
106 {
107 private_request_t *this = (private_request_t*)pthread_getspecific(this_key);
108
109 return FCGX_PutStr(buf, size, this->req.out);
110 }
111
112 /**
113 * fcgiwrap getenv callback
114 */
115 static char *getenv_cb(void *null, const char *key)
116 {
117 char *value;
118 private_request_t *this = (private_request_t*)pthread_getspecific(this_key);
119
120 value = FCGX_GetParam(key, this->req.envp);
121 return value ? strdup(value) : NULL;
122 }
123
124 /**
125 * fcgiwrap getenv callback
126 */
127 static int putenv_cb(void *null, const char *key, const char *value)
128 {
129 /* not supported */
130 return 1;
131 }
132
133 /**
134 * fcgiwrap iterenv callback
135 */
136 static int iterenv_cb(void *null, int num, char **key, char **value)
137 {
138 *key = NULL;
139 *value = NULL;
140 private_request_t *this = (private_request_t*)pthread_getspecific(this_key);
141 if (num < this->req_env_len)
142 {
143 char *eq;
144
145 eq = strchr(this->req.envp[num], '=');
146 if (eq)
147 {
148 *key = strndup(this->req.envp[num], eq - this->req.envp[num]);
149 *value = strdup(eq + 1);
150 }
151 if (*key == NULL || *value == NULL)
152 {
153 free(*key);
154 free(*value);
155 return 1;
156 }
157 }
158 return 0;
159 }
160
161 /**
162 * Implementation of request_t.get_cookie.
163 */
164 static char* get_cookie(private_request_t *this, char *name)
165 {
166 return hdf_get_valuef(this->hdf, "Cookie.%s", name);
167 }
168
169 /**
170 * Implementation of request_t.get_path.
171 */
172 static char* get_path(private_request_t *this)
173 {
174 char * path = FCGX_GetParam("PATH_INFO", this->req.envp);
175 return path ? path : "";
176 }
177
178 /**
179 * Implementation of request_t.get_host.
180 */
181 static char* get_host(private_request_t *this)
182 {
183 char *addr = FCGX_GetParam("REMOTE_ADDR", this->req.envp);
184 return addr ? addr : "";
185 }
186
187 /**
188 * Implementation of request_t.get_user_agent.
189 */
190 static char* get_user_agent(private_request_t *this)
191 {
192 char *agent = FCGX_GetParam("HTTP_USER_AGENT", this->req.envp);
193 return agent ? agent : "";
194 }
195
196 /**
197 * Implementation of request_t.get_post_data.
198 */
199 static char* get_query_data(private_request_t *this, char *name)
200 {
201 return hdf_get_valuef(this->hdf, "Query.%s", name);
202 }
203
204 /**
205 * Implementation of request_t.add_cookie.
206 */
207 static void add_cookie(private_request_t *this, char *name, char *value)
208 {
209 pthread_setspecific(this_key, this);
210 cgi_cookie_set (this->cgi, name, value,
211 FCGX_GetParam("SCRIPT_NAME", this->req.envp),
212 NULL, NULL, 0, 0);
213 }
214
215 /**
216 * Implementation of request_t.redirect.
217 */
218 static void redirect(private_request_t *this, char *fmt, ...)
219 {
220 va_list args;
221
222 FCGX_FPrintF(this->req.out, "Status: 303 See Other\n");
223 FCGX_FPrintF(this->req.out, "Location: %s%s",
224 FCGX_GetParam("SCRIPT_NAME", this->req.envp),
225 *fmt == '/' ? "" : "/");
226 va_start(args, fmt);
227 FCGX_VFPrintF(this->req.out, fmt, args);
228 va_end(args);
229 FCGX_FPrintF(this->req.out, "\n\n");
230 }
231
232 /**
233 * Implementation of request_t.to_referer.
234 */
235 static void to_referer(private_request_t *this)
236 {
237 FCGX_FPrintF(this->req.out, "Status: 303 See Other\n");
238 FCGX_FPrintF(this->req.out, "Location: %s\n\n",
239 FCGX_GetParam("HTTP_REFERER", this->req.envp));
240 }
241
242 /**
243 * Implementation of request_t.get_base.
244 */
245 static char* get_base(private_request_t *this)
246 {
247 return FCGX_GetParam("SCRIPT_NAME", this->req.envp);
248 }
249
250 /**
251 * Implementation of request_t.session_closed.
252 */
253 static bool session_closed(private_request_t *this)
254 {
255 return this->closed;
256 }
257
258 /**
259 * Implementation of request_t.close_session.
260 */
261 static void close_session(private_request_t *this)
262 {
263 this->closed = TRUE;
264 }
265
266 /**
267 * Implementation of request_t.serve.
268 */
269 static void serve(private_request_t *this, char *headers, chunk_t chunk)
270 {
271 FCGX_FPrintF(this->req.out, "%s\n\n", headers);
272
273 FCGX_PutStr(chunk.ptr, chunk.len, this->req.out);
274 }
275
276 /**
277 * Implementation of request_t.render.
278 */
279 static void render(private_request_t *this, char *template)
280 {
281 NEOERR* err;
282
283 pthread_setspecific(this_key, this);
284 err = cgi_display(this->cgi, template);
285 if (err)
286 {
287 cgi_neo_error(this->cgi, err);
288 nerr_log_error(err);
289 }
290 return;
291 }
292
293 /**
294 * Implementation of request_t.streamf.
295 */
296 static int streamf(private_request_t *this, char *format, ...)
297 {
298 va_list args;
299 int written;
300
301 va_start(args, format);
302 written = FCGX_VFPrintF(this->req.out, format, args);
303 va_end(args);
304 if (written >= 0 &&
305 FCGX_FFlush(this->req.out) == -1)
306 {
307 return -1;
308 }
309 return written;
310 }
311
312 /**
313 * Implementation of request_t.set.
314 */
315 static void set(private_request_t *this, char *key, char *value)
316 {
317 hdf_set_value(this->hdf, key, value);
318 }
319
320 /**
321 * Implementation of request_t.setf.
322 */
323 static void setf(private_request_t *this, char *format, ...)
324 {
325 va_list args;
326
327 va_start(args, format);
328 hdf_set_valuevf(this->hdf, format, args);
329 va_end(args);
330 }
331
332 /**
333 * Implementation of request_t.get_ref.
334 */
335 static request_t* get_ref(private_request_t *this)
336 {
337 ref_get(&this->ref);
338 return &this->public;
339 }
340
341 /**
342 * Implementation of request_t.destroy
343 */
344 static void destroy(private_request_t *this)
345 {
346 if (ref_put(&this->ref))
347 {
348 pthread_setspecific(this_key, this);
349 cgi_destroy(&this->cgi);
350 FCGX_Finish_r(&this->req);
351 free(this);
352 }
353 }
354
355 /**
356 * This initialization method is guaranteed to run only once
357 * for all threads.
358 */
359 static void init(void)
360 {
361 cgiwrap_init_emu(NULL, read_cb, writef_cb, write_cb,
362 getenv_cb, putenv_cb, iterenv_cb);
363 pthread_key_create(&this_key, NULL);
364 }
365
366 /*
367 * see header file
368 */
369 request_t *request_create(int fd, bool debug)
370 {
371 NEOERR* err;
372 private_request_t *this = malloc_thing(private_request_t);
373 bool failed = FALSE;
374
375 pthread_cleanup_push(free, this);
376 if (FCGX_InitRequest(&this->req, fd, 0) != 0 ||
377 FCGX_Accept_r(&this->req) != 0)
378 {
379 failed = TRUE;
380 }
381 pthread_cleanup_pop(failed);
382 if (failed)
383 {
384 return NULL;
385 }
386
387 this->public.get_path = (char*(*)(request_t*))get_path;
388 this->public.get_base = (char*(*)(request_t*))get_base;
389 this->public.get_host = (char*(*)(request_t*))get_host;
390 this->public.get_user_agent = (char*(*)(request_t*))get_user_agent;
391 this->public.add_cookie = (void(*)(request_t*, char *name, char *value))add_cookie;
392 this->public.get_cookie = (char*(*)(request_t*,char*))get_cookie;
393 this->public.get_query_data = (char*(*)(request_t*, char *name))get_query_data;
394 this->public.session_closed = (bool(*)(request_t*))session_closed;
395 this->public.close_session = (void(*)(request_t*))close_session;
396 this->public.redirect = (void(*)(request_t*, char *fmt,...))redirect;
397 this->public.to_referer = (void(*)(request_t*))to_referer;
398 this->public.render = (void(*)(request_t*,char*))render;
399 this->public.streamf = (int(*)(request_t*, char *format, ...))streamf;
400 this->public.serve = (void(*)(request_t*,char*,chunk_t))serve;
401 this->public.set = (void(*)(request_t*, char *, char*))set;
402 this->public.setf = (void(*)(request_t*, char *format, ...))setf;
403 this->public.get_ref = (request_t*(*)(request_t*))get_ref;
404 this->public.destroy = (void(*)(request_t*))destroy;
405
406 pthread_once(&once, init);
407 pthread_setspecific(this_key, this);
408
409 this->ref = 1;
410 this->closed = FALSE;
411 this->req_env_len = 0;
412 while (this->req.envp[this->req_env_len] != NULL)
413 {
414 this->req_env_len++;
415 }
416
417 err = hdf_init(&this->hdf);
418 if (!err)
419 {
420 hdf_set_value(this->hdf, "base", get_base(this));
421 hdf_set_value(this->hdf, "Config.NoCache", "true");
422 if (!debug)
423 {
424 hdf_set_value(this->hdf, "Config.TimeFooter", "0");
425 hdf_set_value(this->hdf, "Config.CompressionEnabled", "1");
426 hdf_set_value(this->hdf, "Config.WhiteSpaceStrip", "2");
427 }
428
429 err = cgi_init(&this->cgi, this->hdf);
430 if (!err)
431 {
432 err = cgi_parse(this->cgi);
433 if (!err)
434 {
435 return &this->public;
436 }
437 cgi_destroy(&this->cgi);
438 }
439 }
440 nerr_log_error(err);
441 FCGX_Finish_r(&this->req);
442 free(this);
443 return NULL;
444 }
445