* replaced __thread with pthread_key_t/pthread_setspecific
[strongswan.git] / src / manager / lib / request.c
1 /**
2 * @file request.c
3 *
4 * @brief Implementation of request_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2007 Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #define _GNU_SOURCE
24
25 #include "request.h"
26
27 #include <library.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <pthread.h>
31 #include <ClearSilver/ClearSilver.h>
32
33 typedef struct private_request_t private_request_t;
34
35 /**
36 * private data of the task manager
37 */
38 struct private_request_t {
39
40 /**
41 * public functions
42 */
43 request_t public;
44
45 /**
46 * FastCGI request object
47 */
48 FCGX_Request *req;
49
50 /**
51 * ClearSilver CGI Kit context
52 */
53 CGI *cgi;
54
55 /**
56 * ClearSilver HDF dataset for this request
57 */
58 HDF *hdf;
59 };
60
61 /**
62 * key to a thread specific FCGX_Request, used for ClearSilver cgiwrap callbacks.
63 * ClearSilver cgiwrap is not threadsave, so we use a private
64 * context for each thread.
65 */
66 static pthread_key_t req_key;
67
68 /**
69 * length of param list in req->envp
70 */
71 static pthread_key_t req_env_len_key;
72
73 /**
74 * control variable for pthread_once
75 */
76 pthread_once_t once = PTHREAD_ONCE_INIT;
77
78 /**
79 * fcgiwrap read callback
80 */
81 static int read_cb(void *null, char *buf, int size)
82 {
83 FCGX_Request *req = (FCGX_Request*)pthread_getspecific(req_key);
84 return FCGX_GetStr(buf, size, req->in);
85 }
86
87 /**
88 * fcgiwrap writef callback
89 */
90 static int writef_cb(void *null, const char *format, va_list args)
91 {
92 FCGX_Request *req = (FCGX_Request*)pthread_getspecific(req_key);
93 FCGX_VFPrintF(req->out, format, args);
94 return 0;
95 }
96 /**
97 * fcgiwrap write callback
98 */
99 static int write_cb(void *null, const char *buf, int size)
100 {
101 FCGX_Request *req = (FCGX_Request*)pthread_getspecific(req_key);
102 return FCGX_PutStr(buf, size, req->out);
103 }
104
105 /**
106 * fcgiwrap getenv callback
107 */
108 static char *getenv_cb(void *null, const char *key)
109 {
110 char *value;
111 FCGX_Request *req = (FCGX_Request*)pthread_getspecific(req_key);
112 value = FCGX_GetParam(key, req->envp);
113 return value ? strdup(value) : NULL;
114 }
115
116 /**
117 * fcgiwrap getenv callback
118 */
119 static int putenv_cb(void *null, const char *key, const char *value)
120 {
121 /* not supported */
122 return 1;
123 }
124
125 /**
126 * fcgiwrap iterenv callback
127 */
128 static int iterenv_cb(void *null, int num, char **key, char **value)
129 {
130 *key = NULL;
131 *value = NULL;
132 FCGX_Request *req = (FCGX_Request*)pthread_getspecific(req_key);
133 int req_env_len = (int)pthread_getspecific(req_env_len_key);
134 if (num < req_env_len)
135 {
136 char *eq;
137
138 eq = strchr(req->envp[num], '=');
139 if (eq)
140 {
141 *key = strndup(req->envp[num], eq - req->envp[num]);
142 *value = strdup(eq + 1);
143 }
144 if (*key == NULL || *value == NULL)
145 {
146 free(*key);
147 free(*value);
148 return 1;
149 }
150 }
151 return 0;
152 }
153
154 /**
155 * Implementation of request_t.get_cookie.
156 */
157 static char* get_cookie(private_request_t *this, char *name)
158 {
159 return hdf_get_valuef(this->hdf, "Cookie.%s", name);
160 }
161
162 /**
163 * Implementation of request_t.get_path.
164 */
165 static char* get_path(private_request_t *this)
166 {
167 char * path = FCGX_GetParam("PATH_INFO", this->req->envp);
168 return path ? path : "";
169 }
170
171 /**
172 * Implementation of request_t.get_post_data.
173 */
174 static char* get_query_data(private_request_t *this, char *name)
175 {
176 return hdf_get_valuef(this->hdf, "Query.%s", name);
177 }
178
179 /**
180 * Implementation of request_t.add_cookie.
181 */
182 static void add_cookie(private_request_t *this, char *name, char *value)
183 {
184 cgi_cookie_set (this->cgi, name, value,
185 FCGX_GetParam("SCRIPT_NAME", this->req->envp),
186 NULL, NULL, 0, 0);
187 }
188
189 /**
190 * Implementation of request_t.redirect.
191 */
192 static void redirect(private_request_t *this, char *fmt, ...)
193 {
194 va_list args;
195
196 FCGX_FPrintF(this->req->out, "Status: 303 See Other\n");
197 FCGX_FPrintF(this->req->out, "Location: %s%s",
198 FCGX_GetParam("SCRIPT_NAME", this->req->envp),
199 *fmt == '/' ? "" : "/");
200 va_start(args, fmt);
201 FCGX_VFPrintF(this->req->out, fmt, args);
202 va_end(args);
203 FCGX_FPrintF(this->req->out, "\n\n");
204 }
205
206 /**
207 * Implementation of request_t.get_base.
208 */
209 static char* get_base(private_request_t *this)
210 {
211 return FCGX_GetParam("SCRIPT_NAME", this->req->envp);
212 }
213
214 /**
215 * Implementation of request_t.serve.
216 */
217 static void serve(private_request_t *this, char *headers, chunk_t chunk)
218 {
219 FCGX_FPrintF(this->req->out, "%s\n\n", headers);
220
221 FCGX_PutStr(chunk.ptr, chunk.len, this->req->out);
222 }
223
224 /**
225 * Implementation of request_t.render.
226 */
227 static void render(private_request_t *this, char *template)
228 {
229 NEOERR* err;
230
231 err = cgi_display(this->cgi, template);
232 if (err)
233 {
234 cgi_neo_error(this->cgi, err);
235 nerr_log_error(err);
236 }
237 return;
238 }
239
240 /**
241 * Implementation of request_t.set.
242 */
243 static void set(private_request_t *this, char *key, char *value)
244 {
245 hdf_set_value(this->hdf, key, value);
246 }
247
248 /**
249 * Implementation of request_t.setf.
250 */
251 static void setf(private_request_t *this, char *format, ...)
252 {
253 va_list args;
254
255 va_start(args, format);
256 hdf_set_valuevf(this->hdf, format, args);
257 va_end(args);
258 }
259
260 /**
261 * Implementation of request_t.destroy
262 */
263 static void destroy(private_request_t *this)
264 {
265 cgi_destroy(&this->cgi);
266 free(this);
267 }
268
269 /**
270 * This initialization method is guaranteed to run only once
271 * for all threads.
272 */
273 static void init(void)
274 {
275 cgiwrap_init_emu(NULL, read_cb, writef_cb, write_cb,
276 getenv_cb, putenv_cb, iterenv_cb);
277 pthread_key_create(&req_key, NULL);
278 pthread_key_create(&req_env_len_key, NULL);
279 }
280
281 /*
282 * see header file
283 */
284 request_t *request_create(FCGX_Request *request, bool debug)
285 {
286 NEOERR* err;
287 private_request_t *this = malloc_thing(private_request_t);
288
289 this->public.get_path = (char*(*)(request_t*))get_path;
290 this->public.get_base = (char*(*)(request_t*))get_base;
291 this->public.add_cookie = (void(*)(request_t*, char *name, char *value))add_cookie;
292 this->public.get_cookie = (char*(*)(request_t*,char*))get_cookie;
293 this->public.get_query_data = (char*(*)(request_t*, char *name))get_query_data;
294 this->public.redirect = (void(*)(request_t*, char *fmt,...))redirect;
295 this->public.render = (void(*)(request_t*,char*))render;
296 this->public.serve = (void(*)(request_t*,char*,chunk_t))serve;
297 this->public.set = (void(*)(request_t*, char *, char*))set;
298 this->public.setf = (void(*)(request_t*, char *format, ...))setf;
299 this->public.destroy = (void(*)(request_t*))destroy;
300
301 pthread_once(&once, init);
302
303 this->req = request;
304 pthread_setspecific(req_key, (void*)request);
305
306 int req_env_len = 0;
307 while (request->envp[req_env_len] != NULL)
308 {
309 req_env_len++;
310 }
311
312 pthread_setspecific(req_env_len_key, (void*)req_env_len);
313
314 err = hdf_init(&this->hdf);
315 if (!err)
316 {
317 hdf_set_value(this->hdf, "base", get_base(this));
318 hdf_set_value(this->hdf, "Config.NoCache", "true");
319 if (!debug)
320 {
321 hdf_set_value(this->hdf, "Config.TimeFooter", "0");
322 hdf_set_value(this->hdf, "Config.CompressionEnabled", "1");
323 hdf_set_value(this->hdf, "Config.WhiteSpaceStrip", "2");
324 }
325
326 err = cgi_init(&this->cgi, this->hdf);
327 if (!err)
328 {
329 err = cgi_parse(this->cgi);
330 if (!err)
331 {
332 return &this->public;
333 }
334 cgi_destroy(&this->cgi);
335 }
336 }
337 nerr_log_error(err);
338 free(this);
339 return NULL;
340 }
341