1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2014 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Sascha Schumann <sascha@schumann.cx> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "php.h"
20 #include "SAPI.h"
21 #include "php_main.h"
22 #include "php_variables.h"
23
24 #include "ext/standard/php_smart_str.h"
25
26 #include "tuxmodule.h"
27
28 #include <sys/uio.h>
29
30 #if 0
31 #include <pthread.h>
32 #endif
33
34 void tux_closed_conn(int fd);
35
36 enum {
37 PHP_TUX_BACKGROUND_CONN = 1
38 };
39
40 typedef struct {
41 user_req_t *req;
42 void (*on_close)(int);
43 int tux_action;
44 struct iovec *header_vec;
45 int number_vec;
46 } php_tux_globals;
47
48 static php_tux_globals tux_globals;
49
50 #define TG(v) (tux_globals.v)
51
sapi_tux_ub_write(const char * str,uint str_length TSRMLS_DC)52 static int sapi_tux_ub_write(const char *str, uint str_length TSRMLS_DC)
53 {
54 int n;
55 int m;
56 const char *estr;
57
58 /* combine headers and body */
59 if (TG(number_vec)) {
60 struct iovec *vec = TG(header_vec);
61
62 n = TG(number_vec);
63 vec[n].iov_base = (void *) str;
64 vec[n++].iov_len = str_length;
65
66 /* XXX: this might need more complete error handling */
67 if ((m = writev(TG(req)->sock, vec, n)) == -1 && errno == EPIPE)
68 php_handle_aborted_connection();
69
70 if (m > 0)
71 TG(req)->bytes_sent += str_length;
72
73 TG(number_vec) = 0;
74 return str_length;
75 }
76
77 estr = str + str_length;
78
79 while (str < estr) {
80 n = send(TG(req)->sock, str, estr - str, 0);
81
82 if (n == -1 && errno == EPIPE)
83 php_handle_aborted_connection();
84 if (n == -1 && errno == EAGAIN)
85 continue;
86 if (n <= 0)
87 return n;
88
89 str += n;
90 }
91
92 n = str_length - (estr - str);
93
94 TG(req)->bytes_sent += n;
95
96 return n;
97 }
98
sapi_tux_send_headers(sapi_headers_struct * sapi_headers)99 static int sapi_tux_send_headers(sapi_headers_struct *sapi_headers)
100 {
101 char buf[1024];
102 struct iovec *vec;
103 int n;
104 int max_headers;
105 zend_llist_position pos;
106 sapi_header_struct *h;
107 size_t len;
108 char *status_line;
109 int locate_cl;
110 TSRMLS_FETCH();
111
112 max_headers = 30;
113 n = 1;
114
115 vec = malloc(sizeof(struct iovec) * max_headers);
116 status_line = malloc(30);
117
118 /* safe sprintf use */
119 len = slprintf(status_line, 30, "HTTP/1.1 %d NA\r\n", SG(sapi_headers).http_response_code);
120
121 vec[0].iov_base = status_line;
122 vec[0].iov_len = len;
123
124 TG(req)->http_status = SG(sapi_headers).http_response_code;
125
126 if (TG(tux_action) == TUX_ACTION_FINISH_CLOSE_REQ && TG(req)->http_version == HTTP_1_1)
127 locate_cl = 1;
128 else
129 locate_cl = 0;
130
131 h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
132 while (h) {
133 if (locate_cl
134 && strncasecmp(h->header, "Content-length:", sizeof("Content-length:")-1) == 0) {
135 TG(tux_action) = TUX_ACTION_FINISH_REQ;
136 locate_cl = 0;
137 }
138
139 vec[n].iov_base = h->header;
140 vec[n++].iov_len = h->header_len;
141 if (n >= max_headers - 3) {
142 max_headers *= 2;
143 vec = realloc(vec, sizeof(struct iovec) * max_headers);
144 }
145 vec[n].iov_base = "\r\n";
146 vec[n++].iov_len = 2;
147
148 h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
149 }
150
151 vec[n].iov_base = "\r\n";
152 vec[n++].iov_len = 2;
153
154 TG(number_vec) = n;
155 TG(header_vec) = vec;
156
157
158 return SAPI_HEADER_SENT_SUCCESSFULLY;
159 }
160
sapi_tux_read_post(char * buffer,uint count_bytes)161 static int sapi_tux_read_post(char *buffer, uint count_bytes)
162 {
163 #if 0
164 int amount = 0;
165 TSRMLS_FETCH();
166
167 TG(req)->objectlen = count_bytes;
168 TG(req)->object_addr = buffer;
169 if (tux(TUX_ACTION_READ_POST_DATA, TG(req)))
170 return 0;
171
172 TG(read_post_data) = 1;
173
174 return TG(req)->objectlen;
175 #else
176 return 0;
177 #endif
178 }
179
sapi_tux_read_cookies(void)180 static char *sapi_tux_read_cookies(void)
181 {
182 TSRMLS_FETCH();
183
184 return TG(req)->cookies;
185 }
186
187 #define BUF_SIZE 512
188 #define ADD_STRING(name) \
189 php_register_variable(name, buf, track_vars_array TSRMLS_CC)
190
sapi_tux_register_variables(zval * track_vars_array TSRMLS_DC)191 static void sapi_tux_register_variables(zval *track_vars_array TSRMLS_DC)
192 {
193 char buf[BUF_SIZE + 1];
194 char *p;
195 sapi_header_line ctr = {0};
196
197 ctr.line = buf;
198 ctr.line_len = slprintf(buf, sizeof(buf), "Server: %s", TUXAPI_version);
199 sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC);
200
201 php_register_variable("PHP_SELF", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
202 php_register_variable("SERVER_SOFTWARE", TUXAPI_version, track_vars_array TSRMLS_CC);
203 php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC);
204 php_register_variable("REQUEST_METHOD", (char *) SG(request_info).request_method, track_vars_array TSRMLS_CC);
205 php_register_variable("DOCUMENT_ROOT", TUXAPI_docroot, track_vars_array TSRMLS_CC);
206 php_register_variable("SERVER_NAME", TUXAPI_servername, track_vars_array TSRMLS_CC);
207 php_register_variable("REQUEST_URI", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
208 php_register_variable("PATH_TRANSLATED", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
209
210 p = inet_ntoa(TG(req)->client_host);
211 /* string representation of IPs are never larger than 512 bytes */
212 if (p) {
213 memcpy(buf, p, strlen(p) + 1);
214 ADD_STRING("REMOTE_ADDR");
215 ADD_STRING("REMOTE_HOST");
216 }
217
218 snprintf(buf, sizeof(buf), "%d", CGI_SERVER_PORT(TG(req)));
219 ADD_STRING("SERVER_PORT");
220
221 #if 0
222 snprintf(buf, BUF_SIZE, "/%s", TG(hc)->pathinfo);
223 ADD_STRING("PATH_INFO");
224
225 snprintf(buf, BUF_SIZE, "/%s", TG(hc)->origfilename);
226 ADD_STRING("SCRIPT_NAME");
227 #endif
228
229 #define CONDADD(name, field) \
230 if (TG(req)->field[0]) { \
231 php_register_variable(#name, TG(req)->field, track_vars_array TSRMLS_CC); \
232 }
233
234 CONDADD(HTTP_REFERER, referer);
235 CONDADD(HTTP_USER_AGENT, user_agent);
236 CONDADD(HTTP_ACCEPT, accept);
237 CONDADD(HTTP_ACCEPT_ENCODING, accept_encoding);
238 CONDADD(HTTP_ACCEPT_LANGUAGE, accept_language);
239 CONDADD(HTTP_COOKIE, cookies);
240 CONDADD(CONTENT_TYPE, content_type);
241
242 #if 0
243 if (TG(hc)->contentlength != -1) {
244 snprintf(buf, sizeof(buf), "%ld", (long) TG(hc)->contentlength);
245 ADD_STRING("CONTENT_LENGTH");
246 }
247 #endif
248
249 #if 0
250 if (TG(hc)->authorization[0])
251 php_register_variable("AUTH_TYPE", "Basic", track_vars_array TSRMLS_CC);
252 #endif
253 }
254
255
php_tux_startup(sapi_module_struct * sapi_module)256 static int php_tux_startup(sapi_module_struct *sapi_module)
257 {
258 if (php_module_startup(sapi_module, NULL, 0)==FAILURE) {
259 return FAILURE;
260 } else {
261 return SUCCESS;
262 }
263 }
264
265 static sapi_module_struct tux_sapi_module = {
266 "tux",
267 "tux",
268
269 php_tux_startup,
270 php_module_shutdown_wrapper,
271
272 NULL, /* activate */
273 NULL, /* deactivate */
274
275 sapi_tux_ub_write,
276 NULL,
277 NULL, /* get uid */
278 NULL, /* getenv */
279
280 php_error,
281
282 NULL,
283 sapi_tux_send_headers,
284 NULL,
285 sapi_tux_read_post,
286 sapi_tux_read_cookies,
287
288 sapi_tux_register_variables,
289 NULL, /* Log message */
290 NULL, /* Get request time */
291 NULL, /* Child terminate */
292
293 STANDARD_SAPI_MODULE_PROPERTIES
294 };
295
tux_module_main(TSRMLS_D)296 static void tux_module_main(TSRMLS_D)
297 {
298 zend_file_handle file_handle;
299
300 file_handle.type = ZEND_HANDLE_FILENAME;
301 file_handle.filename = SG(request_info).path_translated;
302 file_handle.free_filename = 0;
303 file_handle.opened_path = NULL;
304
305 if (php_request_startup(TSRMLS_C) == FAILURE) {
306 return;
307 }
308
309 php_execute_script(&file_handle TSRMLS_CC);
310 php_request_shutdown(NULL);
311 }
312
tux_request_ctor(TSRMLS_D)313 static void tux_request_ctor(TSRMLS_D)
314 {
315 char buf[1024];
316 int offset;
317 size_t filename_len;
318 size_t cwd_len;
319 smart_str s = {0};
320 char *p;
321
322 TG(number_vec) = 0;
323 TG(header_vec) = NULL;
324 SG(request_info).query_string = strdup(TG(req)->query);
325
326 smart_str_appends_ex(&s, "/", 1);
327 smart_str_appends_ex(&s, TG(req)->query, 1);
328 smart_str_0(&s);
329 p = strchr(s.c, '&');
330 if (p)
331 *p = '\0';
332 SG(request_info).path_translated = s.c;
333
334 s.c = NULL;
335 smart_str_appendc_ex(&s, '/', 1);
336 smart_str_appends_ex(&s, TG(req)->objectname, 1);
337 smart_str_0(&s);
338 SG(request_info).request_uri = s.c;
339 SG(request_info).request_method = CGI_REQUEST_METHOD(TG(req));
340 if(TG(req)->http_version == HTTP_1_1) SG(request_info).proto_num = 1001;
341 else SG(request_info).proto_num = 1000;
342 SG(sapi_headers).http_response_code = 200;
343 SG(request_info).content_type = TG(req)->content_type;
344 SG(request_info).content_length = 0; /* TG(req)->contentlength; */
345
346 #if 0
347 php_handle_auth_data(TG(hc)->authorization TSRMLS_CC);
348 #endif
349 }
350
tux_request_dtor(TSRMLS_D)351 static void tux_request_dtor(TSRMLS_D)
352 {
353 if (TG(header_vec)) {
354 /* free status_line */
355 free(TG(header_vec)[0].iov_base);
356 free(TG(header_vec));
357 }
358 if (SG(request_info).query_string)
359 free(SG(request_info).query_string);
360 free(SG(request_info).request_uri);
361 free(SG(request_info).path_translated);
362 }
363
364 #if 0
365 static void *separate_thread(void *bla)
366 {
367 int fd;
368 int i = 0;
369
370 fd = (int) bla;
371
372 while (i++ < 5) {
373 send(fd, "test<br />\n", 9, 0);
374 sleep(1);
375 }
376
377 tux(TUX_ACTION_CONTINUE_REQ, (user_req_t *) fd);
378 /* We HAVE to trigger some event on the fd. Otherwise
379 fast_thread won't wake up, so that the eventloop
380 won't be entered -> TUX hangs */
381 shutdown(fd, 2);
382 pthread_exit(NULL);
383 }
384 #endif
385
TUXAPI_handle_events(user_req_t * req)386 int TUXAPI_handle_events(user_req_t *req)
387 {
388 TSRMLS_FETCH();
389
390 if (req->event == PHP_TUX_BACKGROUND_CONN) {
391 tux_closed_conn(req->sock);
392 return tux(TUX_ACTION_FINISH_CLOSE_REQ, req);
393 }
394
395 TG(req) = req;
396 TG(tux_action) = TUX_ACTION_FINISH_CLOSE_REQ;
397
398 tux_request_ctor(TSRMLS_C);
399
400 tux_module_main(TSRMLS_C);
401
402 tux_request_dtor(TSRMLS_C);
403
404 return tux(TG(tux_action), req);
405 }
406
tux_register_on_close(void (* arg)(int))407 void tux_register_on_close(void (*arg)(int))
408 {
409 TG(on_close) = arg;
410 }
411
tux_closed_conn(int fd)412 void tux_closed_conn(int fd)
413 {
414 TSRMLS_FETCH();
415
416 if (TG(on_close)) TG(on_close)(fd);
417 }
418
tux_get_fd(void)419 int tux_get_fd(void)
420 {
421 TSRMLS_FETCH();
422
423 return TG(req)->sock;
424 }
425
tux_set_dont_close(void)426 void tux_set_dont_close(void)
427 {
428 TSRMLS_FETCH();
429
430 TG(req)->event = PHP_TUX_BACKGROUND_CONN;
431 tux(TUX_ACTION_POSTPONE_REQ, TG(req));
432 TG(tux_action) = TUX_ACTION_EVENTLOOP;
433 }
434
TUXAPI_init(void)435 void TUXAPI_init(void)
436 {
437 sapi_startup(&tux_sapi_module);
438 tux_sapi_module.startup(&tux_sapi_module);
439 SG(server_context) = (void *) 1;
440 }
441
doesnotmatter_fini(void)442 void doesnotmatter_fini(void)
443 {
444 if (SG(server_context) != NULL) {
445 tux_sapi_module.shutdown(&tux_sapi_module);
446 sapi_shutdown();
447 }
448 }
449
450 /*
451 * Local variables:
452 * tab-width: 4
453 * c-basic-offset: 4
454 * End:
455 * vim600: sw=4 ts=4 fdm=marker
456 * vim<600: sw=4 ts=4
457 */
458