nsapi.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104
  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: Jayakumar Muthukumarasamy <jk@kasenna.com> |
  16. | Uwe Schindler <uwe@thetaphi.de> |
  17. +----------------------------------------------------------------------+
  18. */
  19. /* $Id$ */
  20. /*
  21. * PHP includes
  22. */
  23. #define NSAPI 1
  24. #ifdef HAVE_CONFIG_H
  25. #include "config.h"
  26. #endif
  27. #include "php.h"
  28. #include "php_variables.h"
  29. #include "ext/standard/info.h"
  30. #include "php_ini.h"
  31. #include "php_globals.h"
  32. #include "SAPI.h"
  33. #include "php_main.h"
  34. #include "php_version.h"
  35. #include "TSRM.h"
  36. #include "ext/standard/php_standard.h"
  37. #include <sys/types.h>
  38. #include <sys/stat.h>
  39. #ifndef RTLD_DEFAULT
  40. #define RTLD_DEFAULT NULL
  41. #endif
  42. /*
  43. * If neither XP_UNIX not XP_WIN32 is defined use PHP_WIN32
  44. */
  45. #if !defined(XP_UNIX) && !defined(XP_WIN32)
  46. #ifdef PHP_WIN32
  47. #define XP_WIN32
  48. #else
  49. #define XP_UNIX
  50. #endif
  51. #endif
  52. /*
  53. * The manual define of HPUX is to fix bug #46020, nsapi.h needs this to detect HPUX
  54. */
  55. #ifdef __hpux
  56. #define HPUX
  57. #endif
  58. /*
  59. * NSAPI includes
  60. */
  61. #include "nsapi.h"
  62. /* fix for gcc4 visibility issue */
  63. #ifndef PHP_WIN32
  64. # undef NSAPI_PUBLIC
  65. # define NSAPI_PUBLIC PHPAPI
  66. #endif
  67. #define NSLS_D struct nsapi_request_context *request_context
  68. #define NSLS_DC , NSLS_D
  69. #define NSLS_C request_context
  70. #define NSLS_CC , NSLS_C
  71. #define NSG(v) (request_context->v)
  72. /*
  73. * ZTS needs to be defined for NSAPI to work
  74. */
  75. #if !defined(ZTS)
  76. #error "NSAPI module needs ZTS to be defined"
  77. #endif
  78. /*
  79. * Structure to encapsulate the NSAPI request in SAPI
  80. */
  81. typedef struct nsapi_request_context {
  82. pblock *pb;
  83. Session *sn;
  84. Request *rq;
  85. int read_post_bytes;
  86. char *path_info;
  87. int fixed_script; /* 0 if script is from URI, 1 if script is from "script" parameter */
  88. short http_error; /* 0 in normal mode; for errors the HTTP error code */
  89. } nsapi_request_context;
  90. /*
  91. * Mappings between NSAPI names and environment variables. This
  92. * mapping was obtained from the sample programs at the iplanet
  93. * website.
  94. */
  95. typedef struct nsapi_equiv {
  96. const char *env_var;
  97. const char *nsapi_eq;
  98. } nsapi_equiv;
  99. static nsapi_equiv nsapi_reqpb[] = {
  100. { "QUERY_STRING", "query" },
  101. { "REQUEST_LINE", "clf-request" },
  102. { "REQUEST_METHOD", "method" },
  103. { "PHP_SELF", "uri" },
  104. { "SERVER_PROTOCOL", "protocol" }
  105. };
  106. static size_t nsapi_reqpb_size = sizeof(nsapi_reqpb)/sizeof(nsapi_reqpb[0]);
  107. static nsapi_equiv nsapi_vars[] = {
  108. { "AUTH_TYPE", "auth-type" },
  109. { "CLIENT_CERT", "auth-cert" },
  110. { "REMOTE_USER", "auth-user" }
  111. };
  112. static size_t nsapi_vars_size = sizeof(nsapi_vars)/sizeof(nsapi_vars[0]);
  113. static nsapi_equiv nsapi_client[] = {
  114. { "HTTPS_KEYSIZE", "keysize" },
  115. { "HTTPS_SECRETSIZE", "secret-keysize" },
  116. { "REMOTE_ADDR", "ip" },
  117. { "REMOTE_HOST", "ip" }
  118. };
  119. static size_t nsapi_client_size = sizeof(nsapi_client)/sizeof(nsapi_client[0]);
  120. /* this parameters to "Service"/"Error" are NSAPI ones which should not be php.ini keys and are excluded */
  121. static char *nsapi_exclude_from_ini_entries[] = { "fn", "type", "method", "directive", "code", "reason", "script", "bucket", NULL };
  122. static void nsapi_free(void *addr)
  123. {
  124. if (addr != NULL) {
  125. FREE(addr);
  126. }
  127. }
  128. /*******************/
  129. /* PHP module part */
  130. /*******************/
  131. PHP_MINIT_FUNCTION(nsapi);
  132. PHP_MSHUTDOWN_FUNCTION(nsapi);
  133. PHP_RINIT_FUNCTION(nsapi);
  134. PHP_RSHUTDOWN_FUNCTION(nsapi);
  135. PHP_MINFO_FUNCTION(nsapi);
  136. PHP_FUNCTION(nsapi_virtual);
  137. PHP_FUNCTION(nsapi_request_headers);
  138. PHP_FUNCTION(nsapi_response_headers);
  139. ZEND_BEGIN_MODULE_GLOBALS(nsapi)
  140. long read_timeout;
  141. ZEND_END_MODULE_GLOBALS(nsapi)
  142. ZEND_DECLARE_MODULE_GLOBALS(nsapi)
  143. #define NSAPI_G(v) TSRMG(nsapi_globals_id, zend_nsapi_globals *, v)
  144. /* {{{ arginfo */
  145. ZEND_BEGIN_ARG_INFO_EX(arginfo_nsapi_virtual, 0, 0, 1)
  146. ZEND_ARG_INFO(0, uri)
  147. ZEND_END_ARG_INFO()
  148. ZEND_BEGIN_ARG_INFO(arginfo_nsapi_request_headers, 0)
  149. ZEND_END_ARG_INFO()
  150. ZEND_BEGIN_ARG_INFO(arginfo_nsapi_response_headers, 0)
  151. ZEND_END_ARG_INFO()
  152. /* }}} */
  153. /* {{{ nsapi_functions[]
  154. *
  155. * Every user visible function must have an entry in nsapi_functions[].
  156. */
  157. const zend_function_entry nsapi_functions[] = {
  158. PHP_FE(nsapi_virtual, arginfo_nsapi_virtual) /* Make subrequest */
  159. PHP_FALIAS(virtual, nsapi_virtual, arginfo_nsapi_virtual) /* compatibility */
  160. PHP_FE(nsapi_request_headers, arginfo_nsapi_request_headers) /* get request headers */
  161. PHP_FALIAS(getallheaders, nsapi_request_headers, arginfo_nsapi_request_headers) /* compatibility */
  162. PHP_FALIAS(apache_request_headers, nsapi_request_headers, arginfo_nsapi_request_headers) /* compatibility */
  163. PHP_FE(nsapi_response_headers, arginfo_nsapi_response_headers) /* get response headers */
  164. PHP_FALIAS(apache_response_headers, nsapi_response_headers, arginfo_nsapi_response_headers) /* compatibility */
  165. {NULL, NULL, NULL}
  166. };
  167. /* }}} */
  168. /* {{{ nsapi_module_entry
  169. */
  170. zend_module_entry nsapi_module_entry = {
  171. STANDARD_MODULE_HEADER,
  172. "nsapi",
  173. nsapi_functions,
  174. PHP_MINIT(nsapi),
  175. PHP_MSHUTDOWN(nsapi),
  176. NULL,
  177. NULL,
  178. PHP_MINFO(nsapi),
  179. NO_VERSION_YET,
  180. STANDARD_MODULE_PROPERTIES
  181. };
  182. /* }}} */
  183. /* {{{ PHP_INI
  184. */
  185. PHP_INI_BEGIN()
  186. STD_PHP_INI_ENTRY("nsapi.read_timeout", "60", PHP_INI_ALL, OnUpdateLong, read_timeout, zend_nsapi_globals, nsapi_globals)
  187. PHP_INI_END()
  188. /* }}} */
  189. /* newer servers hide this functions from the programmer so redefine the functions dynamically
  190. thanks to Chris Elving from Sun for the function declarations */
  191. typedef int (*nsapi_servact_prototype)(Session *sn, Request *rq);
  192. nsapi_servact_prototype nsapi_servact_uri2path = NULL;
  193. nsapi_servact_prototype nsapi_servact_pathchecks = NULL;
  194. nsapi_servact_prototype nsapi_servact_fileinfo = NULL;
  195. nsapi_servact_prototype nsapi_servact_service = NULL;
  196. #ifdef PHP_WIN32
  197. /* The following dll-names for nsapi are in use at this time. The undocumented
  198. * servact_* functions are always in the newest one, older ones are supported by
  199. * the server only by wrapping the function table nothing else. So choose
  200. * the newest one found in process space for dynamic linking */
  201. static char *nsapi_dlls[] = { "ns-httpd40.dll", "ns-httpd36.dll", "ns-httpd35.dll", "ns-httpd30.dll", NULL };
  202. /* if user specifies an other dll name by server_lib parameter
  203. * it is placed in the following variable and only this DLL is
  204. * checked for the servact_* functions */
  205. char *nsapi_dll = NULL;
  206. #endif
  207. /* {{{ php_nsapi_init_dynamic_symbols
  208. */
  209. static void php_nsapi_init_dynamic_symbols(void)
  210. {
  211. /* find address of internal NSAPI functions */
  212. #ifdef PHP_WIN32
  213. register int i;
  214. DL_HANDLE module = NULL;
  215. if (nsapi_dll) {
  216. /* try user specified server_lib */
  217. module = GetModuleHandle(nsapi_dll);
  218. if (!module) {
  219. log_error(LOG_WARN, "php5_init", NULL, NULL, "Cannot find DLL specified by server_lib parameter: %s", nsapi_dll);
  220. }
  221. } else {
  222. /* find a LOADED dll module from nsapi_dlls */
  223. for (i=0; nsapi_dlls[i]; i++) {
  224. if (module = GetModuleHandle(nsapi_dlls[i])) {
  225. break;
  226. }
  227. }
  228. }
  229. if (!module) return;
  230. #else
  231. DL_HANDLE module = RTLD_DEFAULT;
  232. #endif
  233. nsapi_servact_uri2path = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_uri2path");
  234. nsapi_servact_pathchecks = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_pathchecks");
  235. nsapi_servact_fileinfo = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_fileinfo");
  236. nsapi_servact_service = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_service");
  237. if (!(nsapi_servact_uri2path && nsapi_servact_pathchecks && nsapi_servact_fileinfo && nsapi_servact_service)) {
  238. /* not found - could be cause they are undocumented */
  239. nsapi_servact_uri2path = NULL;
  240. nsapi_servact_pathchecks = NULL;
  241. nsapi_servact_fileinfo = NULL;
  242. nsapi_servact_service = NULL;
  243. }
  244. }
  245. /* }}} */
  246. /* {{{ php_nsapi_init_globals
  247. */
  248. static void php_nsapi_init_globals(zend_nsapi_globals *nsapi_globals)
  249. {
  250. nsapi_globals->read_timeout = 60;
  251. }
  252. /* }}} */
  253. /* {{{ PHP_MINIT_FUNCTION
  254. */
  255. PHP_MINIT_FUNCTION(nsapi)
  256. {
  257. php_nsapi_init_dynamic_symbols();
  258. ZEND_INIT_MODULE_GLOBALS(nsapi, php_nsapi_init_globals, NULL);
  259. REGISTER_INI_ENTRIES();
  260. return SUCCESS;
  261. }
  262. /* }}} */
  263. /* {{{ PHP_MSHUTDOWN_FUNCTION
  264. */
  265. PHP_MSHUTDOWN_FUNCTION(nsapi)
  266. {
  267. UNREGISTER_INI_ENTRIES();
  268. return SUCCESS;
  269. }
  270. /* }}} */
  271. /* {{{ PHP_MINFO_FUNCTION
  272. */
  273. PHP_MINFO_FUNCTION(nsapi)
  274. {
  275. php_info_print_table_start();
  276. php_info_print_table_row(2, "NSAPI Module Revision", "$Id$");
  277. php_info_print_table_row(2, "Server Software", system_version());
  278. php_info_print_table_row(2, "Sub-requests with nsapi_virtual()",
  279. (nsapi_servact_service)?((zend_ini_long("zlib.output_compression", sizeof("zlib.output_compression"), 0))?"not supported with zlib.output_compression":"enabled"):"not supported on this platform" );
  280. php_info_print_table_end();
  281. DISPLAY_INI_ENTRIES();
  282. }
  283. /* }}} */
  284. /* {{{ proto bool nsapi_virtual(string uri)
  285. Perform an NSAPI sub-request */
  286. /* This function is equivalent to <!--#include virtual...-->
  287. * in SSI. It does an NSAPI sub-request. It is useful
  288. * for including CGI scripts or .shtml files, or anything else
  289. * that you'd parse through webserver.
  290. */
  291. PHP_FUNCTION(nsapi_virtual)
  292. {
  293. int uri_len,rv;
  294. char *uri,*value;
  295. Request *rq;
  296. nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
  297. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &uri, &uri_len) == FAILURE) {
  298. return;
  299. }
  300. if (!nsapi_servact_service) {
  301. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - Sub-requests not supported on this platform", uri);
  302. RETURN_FALSE;
  303. } else if (zend_ini_long("zlib.output_compression", sizeof("zlib.output_compression"), 0)) {
  304. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - Sub-requests do not work with zlib.output_compression", uri);
  305. RETURN_FALSE;
  306. } else {
  307. php_output_end_all(TSRMLS_C);
  308. php_header(TSRMLS_C);
  309. /* do the sub-request */
  310. /* thanks to Chris Elving from Sun for this code sniplet */
  311. if ((rq = request_restart_internal(uri, NULL)) == NULL) {
  312. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - Internal request creation failed", uri);
  313. RETURN_FALSE;
  314. }
  315. /* insert host of current request to get page from same vhost */
  316. param_free(pblock_remove("host", rq->headers));
  317. if (value = pblock_findval("host", rc->rq->headers)) {
  318. pblock_nvinsert("host", value, rq->headers);
  319. }
  320. /* go through the normal request stages as given in obj.conf,
  321. but leave out the logging/error section */
  322. do {
  323. rv = (*nsapi_servact_uri2path)(rc->sn, rq);
  324. if (rv != REQ_PROCEED) {
  325. continue;
  326. }
  327. rv = (*nsapi_servact_pathchecks)(rc->sn, rq);
  328. if (rv != REQ_PROCEED) {
  329. continue;
  330. }
  331. rv = (*nsapi_servact_fileinfo)(rc->sn, rq);
  332. if (rv != REQ_PROCEED) {
  333. continue;
  334. }
  335. rv = (*nsapi_servact_service)(rc->sn, rq);
  336. } while (rv == REQ_RESTART);
  337. if (rq->status_num != 200) {
  338. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - HTTP status code %d during subrequest", uri, rq->status_num);
  339. request_free(rq);
  340. RETURN_FALSE;
  341. }
  342. request_free(rq);
  343. RETURN_TRUE;
  344. }
  345. }
  346. /* }}} */
  347. /* {{{ proto array nsapi_request_headers(void)
  348. Get all headers from the request */
  349. PHP_FUNCTION(nsapi_request_headers)
  350. {
  351. register int i;
  352. struct pb_entry *entry;
  353. nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
  354. if (zend_parse_parameters_none() == FAILURE) {
  355. return;
  356. }
  357. array_init(return_value);
  358. for (i=0; i < rc->rq->headers->hsize; i++) {
  359. entry=rc->rq->headers->ht[i];
  360. while (entry) {
  361. add_assoc_string(return_value, entry->param->name, entry->param->value, 1);
  362. entry=entry->next;
  363. }
  364. }
  365. }
  366. /* }}} */
  367. /* {{{ proto array nsapi_response_headers(void)
  368. Get all headers from the response */
  369. PHP_FUNCTION(nsapi_response_headers)
  370. {
  371. register int i;
  372. struct pb_entry *entry;
  373. nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
  374. if (zend_parse_parameters_none() == FAILURE) {
  375. return;
  376. }
  377. array_init(return_value);
  378. for (i=0; i < rc->rq->srvhdrs->hsize; i++) {
  379. entry=rc->rq->srvhdrs->ht[i];
  380. while (entry) {
  381. add_assoc_string(return_value, entry->param->name, entry->param->value, 1);
  382. entry=entry->next;
  383. }
  384. }
  385. }
  386. /* }}} */
  387. /*************/
  388. /* SAPI part */
  389. /*************/
  390. static int sapi_nsapi_ub_write(const char *str, unsigned int str_length TSRMLS_DC)
  391. {
  392. int retval;
  393. nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
  394. if (!SG(headers_sent)) {
  395. sapi_send_headers(TSRMLS_C);
  396. }
  397. retval = net_write(rc->sn->csd, (char *)str, str_length);
  398. if (retval == IO_ERROR /* -1 */ || retval == IO_EOF /* 0 */) {
  399. php_handle_aborted_connection();
  400. }
  401. return retval;
  402. }
  403. /* modified version of apache2 */
  404. static void sapi_nsapi_flush(void *server_context)
  405. {
  406. nsapi_request_context *rc = (nsapi_request_context *)server_context;
  407. TSRMLS_FETCH();
  408. if (!rc) {
  409. /* we have no context, so no flushing needed. This fixes a SIGSEGV on shutdown */
  410. return;
  411. }
  412. if (!SG(headers_sent)) {
  413. sapi_send_headers(TSRMLS_C);
  414. }
  415. /* flushing is only supported in iPlanet servers from version 6.1 on, make it conditional */
  416. #if NSAPI_VERSION >= 302
  417. if (net_flush(rc->sn->csd) < 0) {
  418. php_handle_aborted_connection();
  419. }
  420. #endif
  421. }
  422. /* callback for zend_llist_apply on SAPI_HEADER_DELETE_ALL operation */
  423. static int php_nsapi_remove_header(sapi_header_struct *sapi_header TSRMLS_DC)
  424. {
  425. char *header_name, *p;
  426. nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
  427. /* copy the header, because NSAPI needs reformatting and we do not want to change the parameter */
  428. header_name = pool_strdup(rc->sn->pool, sapi_header->header);
  429. /* extract name, this works, if only the header without ':' is given, too */
  430. if (p = strchr(header_name, ':')) {
  431. *p = 0;
  432. }
  433. /* header_name to lower case because NSAPI reformats the headers and wants lowercase */
  434. for (p=header_name; *p; p++) {
  435. *p=tolower(*p);
  436. }
  437. /* remove the header */
  438. param_free(pblock_remove(header_name, rc->rq->srvhdrs));
  439. pool_free(rc->sn->pool, header_name);
  440. return ZEND_HASH_APPLY_KEEP;
  441. }
  442. static int sapi_nsapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
  443. {
  444. char *header_name, *header_content, *p;
  445. nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
  446. switch(op) {
  447. case SAPI_HEADER_DELETE_ALL:
  448. /* this only deletes headers set or overwritten by PHP, headers previously set by NSAPI are left intact */
  449. zend_llist_apply(&sapi_headers->headers, (llist_apply_func_t) php_nsapi_remove_header TSRMLS_CC);
  450. return 0;
  451. case SAPI_HEADER_DELETE:
  452. /* reuse the zend_llist_apply callback function for this, too */
  453. php_nsapi_remove_header(sapi_header TSRMLS_CC);
  454. return 0;
  455. case SAPI_HEADER_ADD:
  456. case SAPI_HEADER_REPLACE:
  457. /* copy the header, because NSAPI needs reformatting and we do not want to change the parameter */
  458. header_name = pool_strdup(rc->sn->pool, sapi_header->header);
  459. /* split header and align pointer for content */
  460. header_content = strchr(header_name, ':');
  461. if (header_content) {
  462. *header_content = 0;
  463. do {
  464. header_content++;
  465. } while (*header_content==' ');
  466. /* header_name to lower case because NSAPI reformats the headers and wants lowercase */
  467. for (p=header_name; *p; p++) {
  468. *p=tolower(*p);
  469. }
  470. /* if REPLACE, remove first. "Content-type" is always removed, as SAPI has a bug according to this */
  471. if (op==SAPI_HEADER_REPLACE || strcmp(header_name, "content-type")==0) {
  472. param_free(pblock_remove(header_name, rc->rq->srvhdrs));
  473. }
  474. /* ADD header to nsapi table */
  475. pblock_nvinsert(header_name, header_content, rc->rq->srvhdrs);
  476. }
  477. pool_free(rc->sn->pool, header_name);
  478. return SAPI_HEADER_ADD;
  479. default:
  480. return 0;
  481. }
  482. }
  483. static int sapi_nsapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
  484. {
  485. int retval;
  486. nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
  487. if (SG(sapi_headers).send_default_content_type) {
  488. char *hd;
  489. param_free(pblock_remove("content-type", rc->rq->srvhdrs));
  490. hd = sapi_get_default_content_type(TSRMLS_C);
  491. pblock_nvinsert("content-type", hd, rc->rq->srvhdrs);
  492. efree(hd);
  493. }
  494. protocol_status(rc->sn, rc->rq, SG(sapi_headers).http_response_code, NULL);
  495. retval = protocol_start_response(rc->sn, rc->rq);
  496. if (retval == REQ_PROCEED || retval == REQ_NOACTION) {
  497. return SAPI_HEADER_SENT_SUCCESSFULLY;
  498. } else {
  499. return SAPI_HEADER_SEND_FAILED;
  500. }
  501. }
  502. static int sapi_nsapi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
  503. {
  504. nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
  505. char *read_ptr = buffer, *content_length_str = NULL;
  506. uint bytes_read = 0;
  507. int length, content_length = 0;
  508. netbuf *nbuf = rc->sn->inbuf;
  509. /*
  510. * Yesss!
  511. */
  512. count_bytes = MIN(count_bytes, SG(request_info).content_length-rc->read_post_bytes);
  513. content_length = SG(request_info).content_length;
  514. if (content_length <= 0) {
  515. return 0;
  516. }
  517. /*
  518. * Gobble any pending data in the netbuf.
  519. */
  520. length = nbuf->cursize - nbuf->pos;
  521. length = MIN(count_bytes, length);
  522. if (length > 0) {
  523. memcpy(read_ptr, nbuf->inbuf + nbuf->pos, length);
  524. bytes_read += length;
  525. read_ptr += length;
  526. content_length -= length;
  527. nbuf->pos += length;
  528. }
  529. /*
  530. * Read the remaining from the socket.
  531. */
  532. while (content_length > 0 && bytes_read < count_bytes) {
  533. int bytes_to_read = count_bytes - bytes_read;
  534. if (content_length < bytes_to_read) {
  535. bytes_to_read = content_length;
  536. }
  537. length = net_read(rc->sn->csd, read_ptr, bytes_to_read, NSAPI_G(read_timeout));
  538. if (length == IO_ERROR || length == IO_EOF) {
  539. break;
  540. }
  541. bytes_read += length;
  542. read_ptr += length;
  543. content_length -= length;
  544. }
  545. if ( bytes_read > 0 ) {
  546. rc->read_post_bytes += bytes_read;
  547. }
  548. return bytes_read;
  549. }
  550. static char *sapi_nsapi_read_cookies(TSRMLS_D)
  551. {
  552. char *cookie_string;
  553. nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
  554. cookie_string = pblock_findval("cookie", rc->rq->headers);
  555. return cookie_string;
  556. }
  557. static void sapi_nsapi_register_server_variables(zval *track_vars_array TSRMLS_DC)
  558. {
  559. nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
  560. register size_t i;
  561. int pos;
  562. char *value,*p;
  563. char buf[32];
  564. struct pb_entry *entry;
  565. for (i = 0; i < nsapi_reqpb_size; i++) {
  566. value = pblock_findval(nsapi_reqpb[i].nsapi_eq, rc->rq->reqpb);
  567. if (value) {
  568. php_register_variable((char *)nsapi_reqpb[i].env_var, value, track_vars_array TSRMLS_CC);
  569. }
  570. }
  571. for (i=0; i < rc->rq->headers->hsize; i++) {
  572. entry=rc->rq->headers->ht[i];
  573. while (entry) {
  574. if (strcasecmp(entry->param->name, "content-length")==0 || strcasecmp(entry->param->name, "content-type")==0) {
  575. value=estrdup(entry->param->name);
  576. pos = 0;
  577. } else {
  578. spprintf(&value, 0, "HTTP_%s", entry->param->name);
  579. pos = 5;
  580. }
  581. if (value) {
  582. for(p = value + pos; *p; p++) {
  583. *p = toupper(*p);
  584. if (!isalnum(*p)) {
  585. *p = '_';
  586. }
  587. }
  588. php_register_variable(value, entry->param->value, track_vars_array TSRMLS_CC);
  589. efree(value);
  590. }
  591. entry=entry->next;
  592. }
  593. }
  594. for (i = 0; i < nsapi_vars_size; i++) {
  595. value = pblock_findval(nsapi_vars[i].nsapi_eq, rc->rq->vars);
  596. if (value) {
  597. php_register_variable((char *)nsapi_vars[i].env_var, value, track_vars_array TSRMLS_CC);
  598. }
  599. }
  600. for (i = 0; i < nsapi_client_size; i++) {
  601. value = pblock_findval(nsapi_client[i].nsapi_eq, rc->sn->client);
  602. if (value) {
  603. php_register_variable((char *)nsapi_client[i].env_var, value, track_vars_array TSRMLS_CC);
  604. }
  605. }
  606. if (value = session_dns(rc->sn)) {
  607. php_register_variable("REMOTE_HOST", value, track_vars_array TSRMLS_CC);
  608. nsapi_free(value);
  609. }
  610. slprintf(buf, sizeof(buf), "%d", conf_getglobals()->Vport);
  611. php_register_variable("SERVER_PORT", buf, track_vars_array TSRMLS_CC);
  612. php_register_variable("SERVER_NAME", conf_getglobals()->Vserver_hostname, track_vars_array TSRMLS_CC);
  613. value = http_uri2url_dynamic("", "", rc->sn, rc->rq);
  614. php_register_variable("SERVER_URL", value, track_vars_array TSRMLS_CC);
  615. nsapi_free(value);
  616. php_register_variable("SERVER_SOFTWARE", system_version(), track_vars_array TSRMLS_CC);
  617. if (security_active) {
  618. php_register_variable("HTTPS", "ON", track_vars_array TSRMLS_CC);
  619. }
  620. php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC);
  621. /* DOCUMENT_ROOT */
  622. if (value = request_translate_uri("/", rc->sn)) {
  623. pos = strlen(value);
  624. php_register_variable_safe("DOCUMENT_ROOT", value, pos-1, track_vars_array TSRMLS_CC);
  625. nsapi_free(value);
  626. }
  627. /* PATH_INFO / PATH_TRANSLATED */
  628. if (rc->path_info) {
  629. if (value = request_translate_uri(rc->path_info, rc->sn)) {
  630. php_register_variable("PATH_TRANSLATED", value, track_vars_array TSRMLS_CC);
  631. nsapi_free(value);
  632. }
  633. php_register_variable("PATH_INFO", rc->path_info, track_vars_array TSRMLS_CC);
  634. }
  635. /* Create full Request-URI & Script-Name */
  636. if (SG(request_info).request_uri) {
  637. pos = strlen(SG(request_info).request_uri);
  638. if (SG(request_info).query_string) {
  639. spprintf(&value, 0, "%s?%s", SG(request_info).request_uri, SG(request_info).query_string);
  640. if (value) {
  641. php_register_variable("REQUEST_URI", value, track_vars_array TSRMLS_CC);
  642. efree(value);
  643. }
  644. } else {
  645. php_register_variable_safe("REQUEST_URI", SG(request_info).request_uri, pos, track_vars_array TSRMLS_CC);
  646. }
  647. if (rc->path_info) {
  648. pos -= strlen(rc->path_info);
  649. if (pos<0) {
  650. pos = 0;
  651. }
  652. }
  653. php_register_variable_safe("SCRIPT_NAME", SG(request_info).request_uri, pos, track_vars_array TSRMLS_CC);
  654. }
  655. php_register_variable("SCRIPT_FILENAME", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
  656. /* special variables in error mode */
  657. if (rc->http_error) {
  658. slprintf(buf, sizeof(buf), "%d", rc->http_error);
  659. php_register_variable("ERROR_TYPE", buf, track_vars_array TSRMLS_CC);
  660. }
  661. }
  662. static void nsapi_log_message(char *message TSRMLS_DC)
  663. {
  664. nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
  665. if (rc) {
  666. log_error(LOG_INFORM, pblock_findval("fn", rc->pb), rc->sn, rc->rq, "%s", message);
  667. } else {
  668. log_error(LOG_INFORM, "php5", NULL, NULL, "%s", message);
  669. }
  670. }
  671. static double sapi_nsapi_get_request_time(TSRMLS_D)
  672. {
  673. return REQ_TIME( ((nsapi_request_context *)SG(server_context))->rq );
  674. }
  675. static int php_nsapi_startup(sapi_module_struct *sapi_module)
  676. {
  677. if (php_module_startup(sapi_module, &nsapi_module_entry, 1)==FAILURE) {
  678. return FAILURE;
  679. }
  680. return SUCCESS;
  681. }
  682. static struct stat* sapi_nsapi_get_stat(TSRMLS_D)
  683. {
  684. return request_stat_path(
  685. SG(request_info).path_translated,
  686. ((nsapi_request_context *)SG(server_context))->rq
  687. );
  688. }
  689. static sapi_module_struct nsapi_sapi_module = {
  690. "nsapi", /* name */
  691. "NSAPI", /* pretty name */
  692. php_nsapi_startup, /* startup */
  693. php_module_shutdown_wrapper, /* shutdown */
  694. NULL, /* activate */
  695. NULL, /* deactivate */
  696. sapi_nsapi_ub_write, /* unbuffered write */
  697. sapi_nsapi_flush, /* flush */
  698. sapi_nsapi_get_stat, /* get uid/stat */
  699. NULL, /* getenv */
  700. php_error, /* error handler */
  701. sapi_nsapi_header_handler, /* header handler */
  702. sapi_nsapi_send_headers, /* send headers handler */
  703. NULL, /* send header handler */
  704. sapi_nsapi_read_post, /* read POST data */
  705. sapi_nsapi_read_cookies, /* read Cookies */
  706. sapi_nsapi_register_server_variables, /* register server variables */
  707. nsapi_log_message, /* Log message */
  708. sapi_nsapi_get_request_time, /* Get request time */
  709. NULL, /* Child terminate */
  710. NULL, /* Block interruptions */
  711. NULL, /* Unblock interruptions */
  712. STANDARD_SAPI_MODULE_PROPERTIES
  713. };
  714. static void nsapi_php_ini_entries(NSLS_D TSRMLS_DC)
  715. {
  716. struct pb_entry *entry;
  717. register int i,j,ok;
  718. for (i=0; i < NSG(pb)->hsize; i++) {
  719. entry=NSG(pb)->ht[i];
  720. while (entry) {
  721. /* exclude standard entries given to "Service" which should not go into ini entries */
  722. ok=1;
  723. for (j=0; nsapi_exclude_from_ini_entries[j]; j++) {
  724. ok&=(strcasecmp(entry->param->name, nsapi_exclude_from_ini_entries[j])!=0);
  725. }
  726. if (ok) {
  727. /* change the ini entry */
  728. if (zend_alter_ini_entry(entry->param->name, strlen(entry->param->name)+1,
  729. entry->param->value, strlen(entry->param->value),
  730. PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE)==FAILURE) {
  731. log_error(LOG_WARN, pblock_findval("fn", NSG(pb)), NSG(sn), NSG(rq), "Cannot change php.ini key \"%s\" to \"%s\"", entry->param->name, entry->param->value);
  732. }
  733. }
  734. entry=entry->next;
  735. }
  736. }
  737. }
  738. void NSAPI_PUBLIC php5_close(void *vparam)
  739. {
  740. if (nsapi_sapi_module.shutdown) {
  741. nsapi_sapi_module.shutdown(&nsapi_sapi_module);
  742. }
  743. if (nsapi_sapi_module.php_ini_path_override) {
  744. free(nsapi_sapi_module.php_ini_path_override);
  745. }
  746. #ifdef PHP_WIN32
  747. if (nsapi_dll) {
  748. free(nsapi_dll);
  749. nsapi_dll = NULL;
  750. }
  751. #endif
  752. sapi_shutdown();
  753. tsrm_shutdown();
  754. log_error(LOG_INFORM, "php5_close", NULL, NULL, "Shutdown PHP Module");
  755. }
  756. /*********************************************************
  757. / init SAF
  758. /
  759. / Init fn="php5_init" [php_ini="/path/to/php.ini"] [server_lib="ns-httpdXX.dll"]
  760. / Initialize the NSAPI module in magnus.conf
  761. /
  762. / php_ini: gives path to php.ini file
  763. / server_lib: (only Win32) gives name of DLL (without path) to look for
  764. / servact_* functions
  765. /
  766. /*********************************************************/
  767. int NSAPI_PUBLIC php5_init(pblock *pb, Session *sn, Request *rq)
  768. {
  769. php_core_globals *core_globals;
  770. char *strval;
  771. int threads=128; /* default for server */
  772. /* fetch max threads from NSAPI and initialize TSRM with it */
  773. threads=conf_getglobals()->Vpool_maxthreads;
  774. if (threads<1) {
  775. threads=128; /* default for server */
  776. }
  777. tsrm_startup(threads, 1, 0, NULL);
  778. core_globals = ts_resource(core_globals_id);
  779. /* look if php_ini parameter is given to php5_init */
  780. if (strval = pblock_findval("php_ini", pb)) {
  781. nsapi_sapi_module.php_ini_path_override = strdup(strval);
  782. }
  783. #ifdef PHP_WIN32
  784. /* look if server_lib parameter is given to php5_init
  785. * (this disables the automatic search for the newest ns-httpdXX.dll) */
  786. if (strval = pblock_findval("server_lib", pb)) {
  787. nsapi_dll = strdup(strval);
  788. }
  789. #endif
  790. /* start SAPI */
  791. sapi_startup(&nsapi_sapi_module);
  792. nsapi_sapi_module.startup(&nsapi_sapi_module);
  793. daemon_atrestart(&php5_close, NULL);
  794. log_error(LOG_INFORM, pblock_findval("fn", pb), sn, rq, "Initialized PHP Module (%d threads expected)", threads);
  795. return REQ_PROCEED;
  796. }
  797. /*********************************************************
  798. / normal use in Service directive:
  799. /
  800. / Service fn="php5_execute" type=... method=... [inikey=inivalue inikey=inivalue...]
  801. /
  802. / use in Service for a directory to supply a php-made directory listing instead of server default:
  803. /
  804. / Service fn="php5_execute" type="magnus-internal/directory" script="/path/to/script.php" [inikey=inivalue inikey=inivalue...]
  805. /
  806. / use in Error SAF to display php script as error page:
  807. /
  808. / Error fn="php5_execute" code=XXX script="/path/to/script.php" [inikey=inivalue inikey=inivalue...]
  809. / Error fn="php5_execute" reason="Reason" script="/path/to/script.php" [inikey=inivalue inikey=inivalue...]
  810. /
  811. /*********************************************************/
  812. int NSAPI_PUBLIC php5_execute(pblock *pb, Session *sn, Request *rq)
  813. {
  814. int retval;
  815. nsapi_request_context *request_context;
  816. zend_file_handle file_handle = {0};
  817. struct stat *fst;
  818. char *path_info;
  819. char *query_string = pblock_findval("query", rq->reqpb);
  820. char *uri = pblock_findval("uri", rq->reqpb);
  821. char *request_method = pblock_findval("method", rq->reqpb);
  822. char *content_type = pblock_findval("content-type", rq->headers);
  823. char *content_length = pblock_findval("content-length", rq->headers);
  824. char *directive = pblock_findval("Directive", pb);
  825. int error_directive = (directive && !strcasecmp(directive, "error"));
  826. int fixed_script = 1;
  827. /* try to use script parameter -> Error or Service for directory listing */
  828. char *path_translated = pblock_findval("script", pb);
  829. TSRMLS_FETCH();
  830. /* if script parameter is missing: normal use as Service SAF */
  831. if (!path_translated) {
  832. path_translated = pblock_findval("path", rq->vars);
  833. path_info = pblock_findval("path-info", rq->vars);
  834. fixed_script = 0;
  835. if (error_directive) {
  836. /* go to next error directive if script parameter is missing */
  837. log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Missing 'script' parameter");
  838. return REQ_NOACTION;
  839. }
  840. } else {
  841. /* in error the path_info is the uri to the requested page */
  842. path_info = pblock_findval("uri", rq->reqpb);
  843. }
  844. /* check if this uri was included in an other PHP script with nsapi_virtual()
  845. by looking for a request context in the current thread */
  846. if (SG(server_context)) {
  847. /* send 500 internal server error */
  848. log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Cannot make nesting PHP requests with nsapi_virtual()");
  849. if (error_directive) {
  850. return REQ_NOACTION;
  851. } else {
  852. protocol_status(sn, rq, 500, NULL);
  853. return REQ_ABORTED;
  854. }
  855. }
  856. request_context = (nsapi_request_context *)pool_malloc(sn->pool, sizeof(nsapi_request_context));
  857. if (!request_context) {
  858. log_error(LOG_CATASTROPHE, pblock_findval("fn", pb), sn, rq, "Insufficient memory to process PHP request!");
  859. return REQ_ABORTED;
  860. }
  861. request_context->pb = pb;
  862. request_context->sn = sn;
  863. request_context->rq = rq;
  864. request_context->read_post_bytes = 0;
  865. request_context->fixed_script = fixed_script;
  866. request_context->http_error = (error_directive) ? rq->status_num : 0;
  867. request_context->path_info = path_info;
  868. SG(server_context) = request_context;
  869. SG(request_info).query_string = query_string;
  870. SG(request_info).request_uri = uri;
  871. SG(request_info).request_method = request_method;
  872. SG(request_info).path_translated = path_translated;
  873. SG(request_info).content_type = content_type;
  874. SG(request_info).content_length = (content_length == NULL) ? 0 : strtoul(content_length, 0, 0);
  875. SG(sapi_headers).http_response_code = (error_directive) ? rq->status_num : 200;
  876. nsapi_php_ini_entries(NSLS_C TSRMLS_CC);
  877. php_handle_auth_data(pblock_findval("authorization", rq->headers) TSRMLS_CC);
  878. file_handle.type = ZEND_HANDLE_FILENAME;
  879. file_handle.filename = SG(request_info).path_translated;
  880. file_handle.free_filename = 0;
  881. file_handle.opened_path = NULL;
  882. fst = request_stat_path(SG(request_info).path_translated, rq);
  883. if (fst && S_ISREG(fst->st_mode)) {
  884. if (php_request_startup(TSRMLS_C) == SUCCESS) {
  885. php_execute_script(&file_handle TSRMLS_CC);
  886. php_request_shutdown(NULL);
  887. retval=REQ_PROCEED;
  888. } else {
  889. /* send 500 internal server error */
  890. log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Cannot prepare PHP engine!");
  891. if (error_directive) {
  892. retval=REQ_NOACTION;
  893. } else {
  894. protocol_status(sn, rq, 500, NULL);
  895. retval=REQ_ABORTED;
  896. }
  897. }
  898. } else {
  899. /* send 404 because file not found */
  900. log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Cannot execute PHP script: %s (File not found)", SG(request_info).path_translated);
  901. if (error_directive) {
  902. retval=REQ_NOACTION;
  903. } else {
  904. protocol_status(sn, rq, 404, NULL);
  905. retval=REQ_ABORTED;
  906. }
  907. }
  908. pool_free(sn->pool, request_context);
  909. SG(server_context) = NULL;
  910. return retval;
  911. }
  912. /*********************************************************
  913. / authentication
  914. /
  915. / we have to make a 'fake' authenticator for netscape so it
  916. / will pass authentication through to php, and allow us to
  917. / check authentication with our scripts.
  918. /
  919. / php5_auth_trans
  920. / main function called from netscape server to authenticate
  921. / a line in obj.conf:
  922. / funcs=php5_auth_trans shlib="path/to/this/phpnsapi.dll"
  923. / and:
  924. / <Object ppath="path/to/be/authenticated/by/php/*">
  925. / AuthTrans fn="php5_auth_trans"
  926. /*********************************************************/
  927. int NSAPI_PUBLIC php5_auth_trans(pblock * pb, Session * sn, Request * rq)
  928. {
  929. /* This is a DO NOTHING function that allows authentication
  930. * information
  931. * to be passed through to PHP scripts.
  932. */
  933. return REQ_PROCEED;
  934. }
  935. /*
  936. * Local variables:
  937. * tab-width: 4
  938. * c-basic-offset: 4
  939. * End:
  940. * vim600: sw=4 ts=4 fdm=marker
  941. * vim<600: sw=4 ts=4
  942. */