capi.c 15 KB

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