xref: /PHP-5.5/sapi/continuity/capi.c (revision 73c1be26)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2015 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: fc46547db58f52b264abdbd0f6d3838f1020679d $");
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