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: Alex Leigh <php (at) postfin (dot) com> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* For more information on Continuity: http://www.ashpool.com/ */
20
21 /*
22 * This code is based on the PHP5 SAPI module for NSAPI by Jayakumar
23 * Muthukumarasamy
24 */
25
26 /* PHP includes */
27 #define CONTINUITY 1
28 #define CAPI_DEBUG
29
30 /* Define for CDP specific extensions */
31 #undef CONTINUITY_CDPEXT
32
33 #include "php.h"
34 #include "php_variables.h"
35 #include "ext/standard/info.h"
36 #include "php_ini.h"
37 #include "php_globals.h"
38 #include "SAPI.h"
39 #include "php_main.h"
40 #include "php_version.h"
41 #include "TSRM.h"
42 #include "ext/standard/php_standard.h"
43
44 /*
45 * CAPI includes
46 */
47 #include <continuity.h>
48 #include <http.h>
49
50 #define NSLS_D struct capi_request_context *request_context
51 #define NSLS_DC , NSLS_D
52 #define NSLS_C request_context
53 #define NSLS_CC , NSLS_C
54 #define NSG(v) (request_context->v)
55
56 /*
57 * ZTS needs to be defined for CAPI to work
58 */
59 #if !defined(ZTS)
60 #error "CAPI module needs ZTS to be defined"
61 #endif
62
63 /*
64 * Structure to encapsulate the CAPI request in SAPI
65 */
66 typedef struct capi_request_context {
67 httpTtrans *t;
68 int read_post_bytes;
69 } capi_request_context;
70
71 /**************/
72
73 PHP_MINIT_FUNCTION(continuity);
74 PHP_MSHUTDOWN_FUNCTION(continuity);
75 PHP_RINIT_FUNCTION(continuity);
76 PHP_RSHUTDOWN_FUNCTION(continuity);
77 PHP_MINFO_FUNCTION(continuity);
78
79 PHP_FUNCTION(continuity_virtual);
80 PHP_FUNCTION(continuity_request_headers);
81 PHP_FUNCTION(continuity_response_headers);
82
83 const zend_function_entry continuity_functions[] = {
84 {NULL, NULL, NULL}
85 };
86
87 zend_module_entry continuity_module_entry = {
88 STANDARD_MODULE_HEADER,
89 "continuity",
90 continuity_functions,
91 PHP_MINIT(continuity),
92 PHP_MSHUTDOWN(continuity),
93 NULL,
94 NULL,
95 PHP_MINFO(continuity),
96 NO_VERSION_YET,
97 STANDARD_MODULE_PROPERTIES
98 };
99
PHP_MINIT_FUNCTION(continuity)100 PHP_MINIT_FUNCTION(continuity)
101 {
102 return SUCCESS;
103 }
104
PHP_MSHUTDOWN_FUNCTION(continuity)105 PHP_MSHUTDOWN_FUNCTION(continuity)
106 {
107 return SUCCESS;
108 }
109
PHP_MINFO_FUNCTION(continuity)110 PHP_MINFO_FUNCTION(continuity)
111 {
112 php_info_print_table_start();
113 php_info_print_table_row(2, "Continuity Module Revision", "$Id: 953991474d209af19f1e972ca77b9e5869dba973 $");
114 php_info_print_table_row(2, "Server Version", conFget_build());
115 #ifdef CONTINUITY_CDPEXT
116 php_info_print_table_row(2,"CDP Extensions", "enabled");
117 #else
118 php_info_print_table_row(2,"CDP Extensions", "disabled");
119 #endif
120 php_info_print_table_end();
121
122 /* DISPLAY_INI_ENTRIES(); */
123 }
124
125 /**************/
126
127 /*
128 * sapi_capi_ub_write: Write len bytes to the connection output.
129 */
sapi_capi_ub_write(const char * str,unsigned int str_length TSRMLS_DC)130 static int sapi_capi_ub_write(const char *str, unsigned int str_length TSRMLS_DC)
131 {
132 int retval;
133 capi_request_context *rc;
134
135 rc = (capi_request_context *) SG(server_context);
136 retval = httpFwrite(rc->t, (char *) str, str_length);
137 if (retval == -1 || retval == 0)
138 php_handle_aborted_connection();
139 return retval;
140 }
141
142 /*
143 * sapi_capi_header_handler: Add/update response headers with those provided
144 * by the PHP engine.
145 */
sapi_capi_header_handler(sapi_header_struct * sapi_header,sapi_headers_struct * sapi_headers TSRMLS_DC)146 static int sapi_capi_header_handler(sapi_header_struct * sapi_header, sapi_headers_struct * sapi_headers TSRMLS_DC)
147 {
148 char *header_name, *header_content, *p;
149 capi_request_context *rc = (capi_request_context *) SG(server_context);
150
151 lstFset_delete_key(rc->t->res_hdrs, "Content-Type");
152
153 header_name = sapi_header->header;
154 header_content = p = strchr(header_name, ':');
155 if (p == NULL) {
156 return 0;
157 }
158 *p = 0;
159 do {
160 header_content++;
161 } while (*header_content == ' ');
162
163 lstFset_add(rc->t->res_hdrs, header_name, header_content);
164
165 *p = ':'; /* restore '*p' */
166
167 efree(sapi_header->header);
168
169 return 0; /* don't use the default SAPI mechanism, CAPI
170 * duplicates this functionality */
171 }
172
173 /*
174 * sapi_capi_send_headers: Transmit the headers to the client. This has the
175 * effect of starting the response under Continuity.
176 */
sapi_capi_send_headers(sapi_headers_struct * sapi_headers TSRMLS_DC)177 static int sapi_capi_send_headers(sapi_headers_struct * sapi_headers TSRMLS_DC)
178 {
179 int retval;
180 capi_request_context *rc = (capi_request_context *) SG(server_context);
181
182 /*
183 * We could probably just do this in the header_handler. But, I don't know
184 * what the implication of doing it there is.
185 */
186
187 if (SG(sapi_headers).send_default_content_type) {
188 /* lstFset_delete_key(rc->t->res_hdrs, "Content-Type"); */
189 lstFset_update(rc->t->res_hdrs, "Content-Type", "text/html");
190 }
191 httpFset_status(rc->t, SG(sapi_headers).http_response_code, NULL);
192 httpFstart_response(rc->t);
193
194 return SAPI_HEADER_SENT_SUCCESSFULLY;
195
196 }
197
sapi_capi_read_post(char * buffer,uint count_bytes TSRMLS_DC)198 static int sapi_capi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
199 {
200 unsigned int max_read, total_read = 0;
201 capi_request_context *rc = (capi_request_context *) SG(server_context);
202
203 if (rc->read_post_bytes == -1) {
204 max_read = MIN(count_bytes, SG(request_info).content_length);
205 } else {
206 if (rc->read_post_bytes == 0)
207 return 0;
208 max_read = MIN(count_bytes, (SG(request_info).content_length - rc->read_post_bytes));
209 }
210
211 total_read = httpFread(rc->t, buffer, max_read);
212
213 if (total_read < 0)
214 total_read = -1;
215 else
216 rc->read_post_bytes = total_read;
217
218 return total_read;
219 }
220
221 /*
222 * sapi_capi_read_cookies: Return cookie information into PHP.
223 */
sapi_capi_read_cookies(TSRMLS_D)224 static char *sapi_capi_read_cookies(TSRMLS_D)
225 {
226 char *cookie_string;
227 capi_request_context *rc = (capi_request_context *) SG(server_context);
228
229 cookie_string = lstFset_get(rc->t->req_hdrs, "cookie");
230 return cookie_string;
231 }
232
sapi_capi_register_server_variables(zval * track_vars_array TSRMLS_DC)233 static void sapi_capi_register_server_variables(zval * track_vars_array TSRMLS_DC)
234 {
235 capi_request_context *rc = (capi_request_context *) SG(server_context);
236 size_t i;
237 char *value;
238 char buf[128];
239
240 /* PHP_SELF and REQUEST_URI */
241 value = lstFset_get(rc->t->vars, "uri");
242 if (value != NULL) {
243 php_register_variable("PHP_SELF", value, track_vars_array TSRMLS_CC);
244 php_register_variable("REQUEST_URI", value, track_vars_array TSRMLS_CC);
245 }
246
247 /* COUNTRY CODE */
248 value = lstFset_get(rc->t->vars, "ccode");
249 if(value!=NULL)
250 php_register_variable("COUNTRY_CODE", value, track_vars_array TSRMLS_CC);
251
252 /* argv */
253 value = lstFset_get(rc->t->vars, "query");
254 if (value != NULL)
255 php_register_variable("argv", value, track_vars_array TSRMLS_CC);
256
257 /* GATEWAY_INTERFACE */
258 php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC);
259
260 /* SERVER_NAME and HTTP_HOST */
261 value = lstFset_get(rc->t->req_hdrs, "host");
262 if (value != NULL) {
263 php_register_variable("HTTP_HOST", value, track_vars_array TSRMLS_CC);
264 /* TODO: This should probably scrub the port value if one is present. */
265 php_register_variable("SERVER_NAME", value, track_vars_array TSRMLS_CC);
266 }
267 /* SERVER_SOFTWARE */
268 value = lstFset_get(rc->t->res_hdrs, "Server");
269 if (value != NULL)
270 php_register_variable("SERVER_SOFTWARE", value, track_vars_array TSRMLS_CC);
271
272 /* SERVER_PROTOCOL */
273 value = lstFset_get(rc->t->vars, "protocol");
274 if (value != NULL)
275 php_register_variable("SERVER_PROTOCOL", value, track_vars_array TSRMLS_CC);
276
277 /* REQUEST_METHOD */
278 value = lstFset_get(rc->t->vars, "method");
279 if (value != NULL)
280 php_register_variable("REQUEST_METHOD", value, track_vars_array TSRMLS_CC);
281
282 /* QUERY_STRING */
283 value = lstFset_get(rc->t->vars, "query");
284 if (value != NULL)
285 php_register_variable("QUERY_STRING", value, track_vars_array TSRMLS_CC);
286
287 /* DOCUMENT_ROOT */
288 value = lstFset_get(rc->t->vars, "docroot");
289 if (value != NULL)
290 php_register_variable("DOCUMENT_ROOT", value, track_vars_array TSRMLS_CC);
291
292 /* HTTP_ACCEPT */
293 value = lstFset_get(rc->t->req_hdrs, "accept");
294 if (value != NULL)
295 php_register_variable("HTTP_ACCEPT", value, track_vars_array TSRMLS_CC);
296
297 /* HTTP_ACCEPT_CHARSET */
298 value = lstFset_get(rc->t->req_hdrs, "accept-charset");
299 if (value != NULL)
300 php_register_variable("HTTP_ACCEPT_CHARSET", value, track_vars_array TSRMLS_CC);
301
302 /* HTTP_ACCEPT_ENCODING */
303 value = lstFset_get(rc->t->req_hdrs, "accept-encoding");
304 if (value != NULL)
305 php_register_variable("HTTP_ACCEPT_ENCODING", value, track_vars_array TSRMLS_CC);
306
307 /* HTTP_ACCEPT_LANGUAGE */
308 value = lstFset_get(rc->t->req_hdrs, "accept-language");
309 if (value != NULL)
310 php_register_variable("HTTP_ACCEPT_LANGUAGE", value, track_vars_array TSRMLS_CC);
311
312 /* HTTP_CONNECTION */
313 value = lstFset_get(rc->t->req_hdrs, "connection");
314 if (value != NULL)
315 php_register_variable("HTTP_CONNECTION", value, track_vars_array TSRMLS_CC);
316
317 /* HTTP_REFERER */
318 value = lstFset_get(rc->t->req_hdrs, "referer");
319 if (value != NULL)
320 php_register_variable("HTTP_REFERER", value, track_vars_array TSRMLS_CC);
321
322 /* HTTP_USER_AGENT */
323 value = lstFset_get(rc->t->req_hdrs, "user-agent");
324 if (value != NULL)
325 php_register_variable("HTTP_USER_AGENT", value, track_vars_array TSRMLS_CC);
326
327 /* REMOTE_ADDR */
328 utlFip_to_str(rc->t->cli_ipv4_addr, buf, sizeof(buf));
329 php_register_variable("REMOTE_ADDR", buf, track_vars_array TSRMLS_CC);
330
331 /* REMOTE_PORT */
332
333 /* SCRIPT_FILENAME and PATH_TRANSLATED */
334 value = lstFset_get(rc->t->vars, "path");
335 if (value != NULL) {
336 php_register_variable("SCRIPT_FILENAME", value, track_vars_array TSRMLS_CC);
337 php_register_variable("PATH_TRANSLATED", value, track_vars_array TSRMLS_CC);
338 }
339 /* SERVER_ADMIN */
340 /* Not applicable */
341
342 /* SERVER_PORT */
343
344 }
345
capi_log_message(char * message TSRMLS_DC)346 static void capi_log_message(char *message TSRMLS_DC)
347 {
348 capi_request_context *rc = (capi_request_context *) SG(server_context);
349 logFmsg(0, "mod/php: %s", message);
350 }
351
352 static int php_capi_startup(sapi_module_struct *sapi_module);
353
354 sapi_module_struct capi_sapi_module = {
355 "Continuity", /* name */
356 "Continuity Server Enterprise Edition", /* pretty name */
357
358 php_capi_startup, /* startup */
359 php_module_shutdown_wrapper, /* shutdown */
360
361 NULL, /* activate */
362 NULL, /* deactivate */
363
364 sapi_capi_ub_write, /* unbuffered write */
365 NULL, /* flush */
366 NULL, /* get uid */
367 NULL, /* getenv */
368
369 php_error, /* error handler */
370
371 sapi_capi_header_handler, /* header handler */
372 sapi_capi_send_headers, /* send headers handler */
373 NULL, /* send header handler */
374
375 sapi_capi_read_post, /* read POST data */
376 sapi_capi_read_cookies, /* read Cookies */
377
378 sapi_capi_register_server_variables, /* register server variables */
379 capi_log_message, /* Log message */
380 NULL, /* Get request time */
381 NULL, /* Child terminate */
382
383 NULL, /* Block interruptions */
384 NULL, /* Unblock interruptions */
385
386 STANDARD_SAPI_MODULE_PROPERTIES
387 };
388
php_capi_startup(sapi_module_struct * sapi_module)389 static int php_capi_startup(sapi_module_struct *sapi_module) {
390 if(php_module_startup(sapi_module,&continuity_module_entry,1)==FAILURE) {
391 return FAILURE;
392 }
393 return SUCCESS;
394 }
395
396
397 static char *
capi_strdup(char * str)398 capi_strdup(char *str)
399 {
400 if (str != NULL)
401 return strFcopy(str);
402 return NULL;
403 }
404
capi_free(void * addr)405 static void capi_free(void *addr)
406 {
407 if (addr != NULL)
408 free(addr);
409 }
410
capi_request_ctor(NSLS_D TSRMLS_DC)411 static void capi_request_ctor(NSLS_D TSRMLS_DC)
412 {
413 char *query_string = lstFset_get(NSG(t->vars), "query");
414 char *uri = lstFset_get(NSG(t->vars), "uri");
415 char *path_info = lstFset_get(NSG(t->vars), "path-info");
416 char *path_translated = lstFset_get(NSG(t->vars), "path");
417 char *request_method = lstFset_get(NSG(t->vars), "method");
418 char *content_type = lstFset_get(NSG(t->req_hdrs), "content-type");
419 char *content_length = lstFset_get(NSG(t->req_hdrs), "content-length");
420
421 SG(request_info).query_string = capi_strdup(query_string);
422 SG(request_info).request_uri = capi_strdup(uri);
423 SG(request_info).request_method = capi_strdup(request_method);
424 SG(request_info).path_translated = capi_strdup(path_translated);
425 SG(request_info).content_type = capi_strdup(content_type);
426 SG(request_info).content_length = (content_length == NULL) ? 0 : strtoul(content_length, 0, 0);
427 SG(sapi_headers).http_response_code = 200;
428 }
429
capi_request_dtor(NSLS_D TSRMLS_DC)430 static void capi_request_dtor(NSLS_D TSRMLS_DC)
431 {
432 capi_free(SG(request_info).query_string);
433 capi_free(SG(request_info).request_uri);
434 capi_free(SG(request_info).request_method);
435 capi_free(SG(request_info).path_translated);
436 capi_free(SG(request_info).content_type);
437 }
438
capi_module_main(NSLS_D TSRMLS_DC)439 int capi_module_main(NSLS_D TSRMLS_DC)
440 {
441 zend_file_handle file_handle;
442
443 if (php_request_startup(TSRMLS_C) == FAILURE) {
444 return FAILURE;
445 }
446 file_handle.type = ZEND_HANDLE_FILENAME;
447 file_handle.filename = SG(request_info).path_translated;
448 file_handle.free_filename = 0;
449 file_handle.opened_path = NULL;
450
451 php_execute_script(&file_handle TSRMLS_CC);
452 php_request_shutdown(NULL);
453
454 return SUCCESS;
455 }
456
phpFinit(lstTset * opt)457 int phpFinit(lstTset * opt)
458 {
459 php_core_globals *core_globals;
460
461 tsrm_startup(128, 1, 0, NULL);
462 core_globals = ts_resource(core_globals_id);
463
464 logFmsg(0, "mod/php: PHP Interface v3 (module)");
465 logFmsg(0, "mod/php: Copyright (c) 1999-2005 The PHP Group. All rights reserved.");
466
467 sapi_startup(&capi_sapi_module);
468 capi_sapi_module.startup(&capi_sapi_module);
469
470 return STATUS_PROCEED;
471 }
472
phpFservice(httpTtrans * t,lstTset * opts)473 int phpFservice(httpTtrans * t, lstTset * opts)
474 {
475 int retval;
476 capi_request_context *request_context;
477
478 TSRMLS_FETCH();
479
480 request_context = (capi_request_context *) malloc(sizeof(capi_request_context));
481 request_context->t = t;
482 request_context->read_post_bytes = -1;
483
484 SG(server_context) = request_context;
485
486 capi_request_ctor(NSLS_C TSRMLS_CC);
487 retval = capi_module_main(NSLS_C TSRMLS_CC);
488 capi_request_dtor(NSLS_C TSRMLS_CC);
489
490 free(request_context);
491
492 /*
493 * This call is ostensibly provided to free the memory from PHP/TSRM when
494 * the thread terminated, but, it leaks a structure in some hash list
495 * according to the developers. Not calling this will leak the entire
496 * interpreter, around 100k, but calling it and then terminating the
497 * thread will leak the struct (around a k). The only answer with the
498 * current TSRM implementation is to reuse the threads that allocate TSRM
499 * resources.
500 */
501 /* ts_free_thread(); */
502
503 if (retval == SUCCESS) {
504 return STATUS_EXIT;
505 } else {
506 return STATUS_ERROR;
507 }
508 }
509