aolserver.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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: Sascha Schumann <sascha@schumann.cx> |
  16. +----------------------------------------------------------------------+
  17. */
  18. /*
  19. * TODO:
  20. * - write documentation
  21. * - CGI/1.1 conformance
  22. */
  23. /* $Id$ */
  24. /* conflict between PHP and AOLserver headers */
  25. #define Debug php_Debug
  26. #include "php.h"
  27. #undef Debug
  28. #ifdef HAVE_AOLSERVER
  29. #ifndef ZTS
  30. #error AOLserver module is only useable in thread-safe mode
  31. #endif
  32. #include "ext/standard/info.h"
  33. #define SECTION(name) PUTS("<h2>" name "</h2>\n")
  34. #define NS_BUF_SIZE 511
  35. #include "php_ini.h"
  36. #include "php_globals.h"
  37. #include "SAPI.h"
  38. #include "php_main.h"
  39. #include "php_variables.h"
  40. #include "ns.h"
  41. #include "php_version.h"
  42. /* This symbol is used by AOLserver to tell the API version we expect */
  43. int Ns_ModuleVersion = 1;
  44. #define NSG(v) TSRMG(ns_globals_id, ns_globals_struct *, v)
  45. /* php_ns_context is per-server (thus only once at all) */
  46. typedef struct {
  47. sapi_module_struct *sapi_module;
  48. char *ns_server;
  49. char *ns_module;
  50. } php_ns_context;
  51. /* ns_globals_struct is per-thread */
  52. typedef struct {
  53. Ns_Conn *conn;
  54. size_t data_avail;
  55. } ns_globals_struct;
  56. /* TSRM id */
  57. static int ns_globals_id;
  58. /* global context */
  59. static php_ns_context *global_context;
  60. static void php_ns_config(php_ns_context *ctx, char global);
  61. /*
  62. * php_ns_sapi_ub_write() writes data to the client connection.
  63. */
  64. static int
  65. php_ns_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
  66. {
  67. int n;
  68. uint sent = 0;
  69. while (str_length > 0) {
  70. n = Ns_ConnWrite(NSG(conn), (void *) str, str_length);
  71. if (n == -1)
  72. php_handle_aborted_connection();
  73. str += n;
  74. sent += n;
  75. str_length -= n;
  76. }
  77. return sent;
  78. }
  79. /*
  80. * php_ns_sapi_header_handler() sets a HTTP reply header to be
  81. * sent to the client.
  82. */
  83. static int
  84. php_ns_sapi_header_handler(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC)
  85. {
  86. char *header_name, *header_content;
  87. char *p;
  88. header_name = sapi_header->header;
  89. header_content = p = strchr(header_name, ':');
  90. if (p) {
  91. *p = '\0';
  92. do {
  93. header_content++;
  94. } while (*header_content == ' ');
  95. if (!strcasecmp(header_name, "Content-type")) {
  96. Ns_ConnSetTypeHeader(NSG(conn), header_content);
  97. } else {
  98. Ns_ConnSetHeaders(NSG(conn), header_name, header_content);
  99. }
  100. *p = ':';
  101. }
  102. sapi_free_header(sapi_header);
  103. return 0;
  104. }
  105. /*
  106. * php_ns_sapi_send_headers() flushes the headers to the client.
  107. * Called before real content is sent by PHP.
  108. */
  109. static int
  110. php_ns_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
  111. {
  112. if(SG(sapi_headers).send_default_content_type) {
  113. Ns_ConnSetRequiredHeaders(NSG(conn), "text/html", 0);
  114. }
  115. Ns_ConnFlushHeaders(NSG(conn), SG(sapi_headers).http_response_code);
  116. return SAPI_HEADER_SENT_SUCCESSFULLY;
  117. }
  118. /*
  119. * php_ns_sapi_read_post() reads a specified number of bytes from
  120. * the client. Used for POST/PUT requests.
  121. */
  122. static int
  123. php_ns_sapi_read_post(char *buf, uint count_bytes TSRMLS_DC)
  124. {
  125. uint max_read;
  126. uint total_read = 0;
  127. max_read = MIN(NSG(data_avail), count_bytes);
  128. total_read = Ns_ConnRead(NSG(conn), buf, max_read);
  129. if(total_read == NS_ERROR) {
  130. total_read = -1;
  131. } else {
  132. NSG(data_avail) -= total_read;
  133. }
  134. return total_read;
  135. }
  136. /*
  137. * php_ns_sapi_read_cookies() returns the Cookie header from
  138. * the HTTP request header
  139. */
  140. static char *php_ns_sapi_read_cookies(TSRMLS_D)
  141. {
  142. int i;
  143. char *http_cookie = NULL;
  144. i = Ns_SetIFind(NSG(conn->headers), "cookie");
  145. if(i != -1) {
  146. http_cookie = Ns_SetValue(NSG(conn->headers), i);
  147. }
  148. return http_cookie;
  149. }
  150. static void php_info_aolserver(ZEND_MODULE_INFO_FUNC_ARGS)
  151. {
  152. char buf[512];
  153. int uptime = Ns_InfoUptime();
  154. int i;
  155. php_info_print_table_start();
  156. php_info_print_table_row(2, "SAPI module version", "$Id$");
  157. php_info_print_table_row(2, "Build date", Ns_InfoBuildDate());
  158. php_info_print_table_row(2, "Config file path", Ns_InfoConfigFile());
  159. php_info_print_table_row(2, "Error Log path", Ns_InfoErrorLog());
  160. php_info_print_table_row(2, "Installation path", Ns_InfoHomePath());
  161. php_info_print_table_row(2, "Hostname of server", Ns_InfoHostname());
  162. php_info_print_table_row(2, "Source code label", Ns_InfoLabel());
  163. php_info_print_table_row(2, "Server platform", Ns_InfoPlatform());
  164. snprintf(buf, 511, "%s/%s", Ns_InfoServerName(), Ns_InfoServerVersion());
  165. php_info_print_table_row(2, "Server version", buf);
  166. snprintf(buf, 511, "%d day(s), %02d:%02d:%02d",
  167. uptime / 86400,
  168. (uptime / 3600) % 24,
  169. (uptime / 60) % 60,
  170. uptime % 60);
  171. php_info_print_table_row(2, "Server uptime", buf);
  172. php_info_print_table_end();
  173. SECTION("HTTP Headers Information");
  174. php_info_print_table_start();
  175. php_info_print_table_colspan_header(2, "HTTP Request Headers");
  176. php_info_print_table_row(2, "HTTP Request", NSG(conn)->request->line);
  177. for (i = 0; i < Ns_SetSize(NSG(conn)->headers); i++) {
  178. php_info_print_table_row(2, Ns_SetKey(NSG(conn)->headers, i), Ns_SetValue(NSG(conn)->headers, i));
  179. }
  180. php_info_print_table_colspan_header(2, "HTTP Response Headers");
  181. for (i = 0; i < Ns_SetSize(NSG(conn)->outputheaders); i++) {
  182. php_info_print_table_row(2, Ns_SetKey(NSG(conn)->outputheaders, i), Ns_SetValue(NSG(conn)->outputheaders, i));
  183. }
  184. php_info_print_table_end();
  185. }
  186. PHP_FUNCTION(getallheaders);
  187. /* {{{ arginfo */
  188. ZEND_BEGIN_ARG_INFO(arginfo_aolserver_getallheaders, 0)
  189. ZEND_END_ARG_INFO()
  190. /* }}} */
  191. static const zend_function_entry aolserver_functions[] = {
  192. PHP_FE(getallheaders, arginfo_aolserver_getallheaders)
  193. {NULL, NULL, NULL}
  194. };
  195. static zend_module_entry php_aolserver_module = {
  196. STANDARD_MODULE_HEADER,
  197. "AOLserver",
  198. aolserver_functions,
  199. NULL,
  200. NULL,
  201. NULL,
  202. NULL,
  203. php_info_aolserver,
  204. NULL,
  205. STANDARD_MODULE_PROPERTIES
  206. };
  207. PHP_FUNCTION(getallheaders)
  208. {
  209. int i;
  210. array_init(return_value);
  211. for (i = 0; i < Ns_SetSize(NSG(conn->headers)); i++) {
  212. char *key = Ns_SetKey(NSG(conn->headers), i);
  213. char *value = Ns_SetValue(NSG(conn->headers), i);
  214. add_assoc_string(return_value, key, value, 1);
  215. }
  216. }
  217. static int
  218. php_ns_startup(sapi_module_struct *sapi_module)
  219. {
  220. if (php_module_startup(sapi_module, &php_aolserver_module, 1) == FAILURE) {
  221. return FAILURE;
  222. } else {
  223. return SUCCESS;
  224. }
  225. }
  226. /*
  227. * php_ns_sapi_register_variables() populates the php script environment
  228. * with a number of variables. HTTP_* variables are created for
  229. * the HTTP header data, so that a script can access these.
  230. */
  231. #define ADD_STRINGX(name, buf) \
  232. php_register_variable(name, buf, track_vars_array TSRMLS_CC)
  233. #define ADD_STRING(name) \
  234. ADD_STRINGX(name, buf)
  235. static void
  236. php_ns_sapi_register_variables(zval *track_vars_array TSRMLS_DC)
  237. {
  238. int i;
  239. char buf[NS_BUF_SIZE + 1];
  240. char *tmp;
  241. for(i = 0; i < Ns_SetSize(NSG(conn->headers)); i++) {
  242. char *key = Ns_SetKey(NSG(conn->headers), i);
  243. char *value = Ns_SetValue(NSG(conn->headers), i);
  244. char *p;
  245. char c;
  246. snprintf(buf, NS_BUF_SIZE, "HTTP_%s", key);
  247. for(p = buf + 5; (c = *p); p++) {
  248. c = toupper(c);
  249. if(c < 'A' || c > 'Z') {
  250. c = '_';
  251. }
  252. *p = c;
  253. }
  254. ADD_STRINGX(buf, value);
  255. }
  256. snprintf(buf, NS_BUF_SIZE, "%s/%s", Ns_InfoServerName(), Ns_InfoServerVersion());
  257. ADD_STRING("SERVER_SOFTWARE");
  258. snprintf(buf, NS_BUF_SIZE, "HTTP/%1.1f", NSG(conn)->request->version);
  259. ADD_STRING("SERVER_PROTOCOL");
  260. ADD_STRINGX("REQUEST_METHOD", NSG(conn)->request->method);
  261. if(NSG(conn)->request->query)
  262. ADD_STRINGX("QUERY_STRING", NSG(conn)->request->query);
  263. ADD_STRINGX("SERVER_BUILDDATE", Ns_InfoBuildDate());
  264. ADD_STRINGX("REMOTE_ADDR", Ns_ConnPeer(NSG(conn)));
  265. snprintf(buf, NS_BUF_SIZE, "%d", Ns_ConnPeerPort(NSG(conn)));
  266. ADD_STRING("REMOTE_PORT");
  267. snprintf(buf, NS_BUF_SIZE, "%d", Ns_ConnPort(NSG(conn)));
  268. ADD_STRING("SERVER_PORT");
  269. tmp = Ns_ConnHost(NSG(conn));
  270. if (tmp)
  271. ADD_STRINGX("SERVER_NAME", tmp);
  272. ADD_STRINGX("PATH_TRANSLATED", SG(request_info).path_translated);
  273. ADD_STRINGX("REQUEST_URI", SG(request_info).request_uri);
  274. ADD_STRINGX("PHP_SELF", SG(request_info).request_uri);
  275. ADD_STRINGX("GATEWAY_INTERFACE", "CGI/1.1");
  276. snprintf(buf, NS_BUF_SIZE, "%d", Ns_InfoBootTime());
  277. ADD_STRING("SERVER_BOOTTIME");
  278. }
  279. /* this structure is static (as in "it does not change") */
  280. static sapi_module_struct aolserver_sapi_module = {
  281. "aolserver",
  282. "AOLserver",
  283. php_ns_startup, /* startup */
  284. php_module_shutdown_wrapper, /* shutdown */
  285. NULL, /* activate */
  286. NULL, /* deactivate */
  287. php_ns_sapi_ub_write, /* unbuffered write */
  288. NULL, /* flush */
  289. NULL, /* get uid */
  290. NULL, /* getenv */
  291. php_error, /* error handler */
  292. php_ns_sapi_header_handler, /* header handler */
  293. php_ns_sapi_send_headers, /* send headers handler */
  294. NULL, /* send header handler */
  295. php_ns_sapi_read_post, /* read POST data */
  296. php_ns_sapi_read_cookies, /* read Cookies */
  297. php_ns_sapi_register_variables,
  298. NULL, /* Log message */
  299. NULL, /* Get request time */
  300. NULL, /* child terminate */
  301. STANDARD_SAPI_MODULE_PROPERTIES
  302. };
  303. /*
  304. * php_ns_module_main() is called by the per-request handler and
  305. * "executes" the script
  306. */
  307. static int
  308. php_ns_module_main(TSRMLS_D)
  309. {
  310. zend_file_handle file_handle;
  311. file_handle.type = ZEND_HANDLE_FILENAME;
  312. file_handle.filename = SG(request_info).path_translated;
  313. file_handle.free_filename = 0;
  314. file_handle.opened_path = NULL;
  315. php_ns_config(global_context, 0);
  316. if (php_request_startup(TSRMLS_C) == FAILURE) {
  317. return NS_ERROR;
  318. }
  319. php_execute_script(&file_handle TSRMLS_CC);
  320. php_request_shutdown(NULL);
  321. return NS_OK;
  322. }
  323. /*
  324. * php_ns_request_ctor() initializes the per-request data structure
  325. * and fills it with data provided by the web server
  326. */
  327. static void
  328. php_ns_request_ctor(TSRMLS_D)
  329. {
  330. char *server;
  331. Ns_DString ds;
  332. char *root;
  333. int index;
  334. char *tmp;
  335. server = Ns_ConnServer(NSG(conn));
  336. #define safe_strdup(x) ((x)?strdup((x)):NULL)
  337. SG(request_info).query_string = safe_strdup(NSG(conn->request->query));
  338. Ns_DStringInit(&ds);
  339. Ns_UrlToFile(&ds, server, NSG(conn->request->url));
  340. /* path_translated is the absolute path to the file */
  341. SG(request_info).path_translated = safe_strdup(Ns_DStringValue(&ds));
  342. Ns_DStringFree(&ds);
  343. root = Ns_PageRoot(server);
  344. SG(request_info).request_uri = strdup(SG(request_info).path_translated + strlen(root));
  345. SG(request_info).request_method = NSG(conn)->request->method;
  346. if(NSG(conn)->request->version > 1.0) SG(request_info).proto_num = 1001;
  347. else SG(request_info).proto_num = 1000;
  348. SG(request_info).content_length = Ns_ConnContentLength(NSG(conn));
  349. index = Ns_SetIFind(NSG(conn)->headers, "content-type");
  350. SG(request_info).content_type = index == -1 ? NULL :
  351. Ns_SetValue(NSG(conn)->headers, index);
  352. SG(sapi_headers).http_response_code = 200;
  353. tmp = Ns_ConnAuthUser(NSG(conn));
  354. if (tmp)
  355. tmp = estrdup(tmp);
  356. SG(request_info).auth_user = tmp;
  357. tmp = Ns_ConnAuthPasswd(NSG(conn));
  358. if (tmp)
  359. tmp = estrdup(tmp);
  360. SG(request_info).auth_password = tmp;
  361. NSG(data_avail) = SG(request_info).content_length;
  362. }
  363. /*
  364. * php_ns_request_dtor() destroys all data associated with
  365. * the per-request structure
  366. */
  367. static void
  368. php_ns_request_dtor(TSRMLS_D)
  369. {
  370. free(SG(request_info).path_translated);
  371. if (SG(request_info).query_string)
  372. free(SG(request_info).query_string);
  373. free(SG(request_info).request_uri);
  374. }
  375. /*
  376. * The php_ns_request_handler() is called per request and handles
  377. * everything for one request.
  378. */
  379. static int
  380. php_ns_request_handler(void *context, Ns_Conn *conn)
  381. {
  382. int status = NS_OK;
  383. TSRMLS_FETCH();
  384. NSG(conn) = conn;
  385. SG(server_context) = global_context;
  386. php_ns_request_ctor(TSRMLS_C);
  387. status = php_ns_module_main(TSRMLS_C);
  388. php_ns_request_dtor(TSRMLS_C);
  389. return status;
  390. }
  391. /*
  392. * php_ns_config() fetches the configuration data.
  393. *
  394. * It understands the "map" and "php_value" command.
  395. */
  396. static void
  397. php_ns_config(php_ns_context *ctx, char global)
  398. {
  399. int i;
  400. char *path;
  401. Ns_Set *set;
  402. path = Ns_ConfigGetPath(ctx->ns_server, ctx->ns_module, NULL);
  403. set = Ns_ConfigGetSection(path);
  404. for (i = 0; set && i < Ns_SetSize(set); i++) {
  405. char *key = Ns_SetKey(set, i);
  406. char *value = Ns_SetValue(set, i);
  407. if (global && !strcasecmp(key, "map")) {
  408. Ns_Log(Notice, "Registering PHP for \"%s\"", value);
  409. Ns_RegisterRequest(ctx->ns_server, "GET", value, php_ns_request_handler, NULL, ctx, 0);
  410. Ns_RegisterRequest(ctx->ns_server, "POST", value, php_ns_request_handler, NULL, ctx, 0);
  411. Ns_RegisterRequest(ctx->ns_server, "HEAD", value, php_ns_request_handler, NULL, ctx, 0);
  412. /*
  413. * Deactivated for now. The ini system will cause random crashes when
  414. * accessed from here (since there are no locks to protect the global
  415. * known_directives)
  416. */
  417. } else if (!global && !strcasecmp(key, "php_value")) {
  418. Ns_Log(Notice, "php_value has been deactivated temporarily. Please use a php.ini file to pass directives to PHP. Thanks.");
  419. #if 0
  420. char *val;
  421. val = strchr(value, ' ');
  422. if (val) {
  423. char *new_key;
  424. new_key = estrndup(value, val - value);
  425. do {
  426. val++;
  427. } while(*val == ' ');
  428. Ns_Log(Debug, "PHP configuration option '%s=%s'", new_key, val);
  429. zend_alter_ini_entry(new_key, strlen(new_key) + 1, val,
  430. strlen(val) + 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
  431. efree(new_key);
  432. }
  433. #endif
  434. }
  435. }
  436. }
  437. /*
  438. * php_ns_server_shutdown() performs the last steps before the
  439. * server exits. Shutdowns basic services and frees memory
  440. */
  441. static void
  442. php_ns_server_shutdown(void *context)
  443. {
  444. php_ns_context *ctx = (php_ns_context *) context;
  445. ctx->sapi_module->shutdown(ctx->sapi_module);
  446. sapi_shutdown();
  447. tsrm_shutdown();
  448. free(ctx->ns_module);
  449. free(ctx->ns_server);
  450. free(ctx);
  451. }
  452. /*
  453. * Ns_ModuleInit() is called by AOLserver once at startup
  454. *
  455. * This functions allocates basic structures and initializes
  456. * basic services.
  457. */
  458. int Ns_ModuleInit(char *server, char *module)
  459. {
  460. php_ns_context *ctx;
  461. tsrm_startup(1, 1, 0, NULL);
  462. sapi_startup(&aolserver_sapi_module);
  463. sapi_module.startup(&aolserver_sapi_module);
  464. /* TSRM is used to allocate a per-thread structure */
  465. ts_allocate_id(&ns_globals_id, sizeof(ns_globals_struct), NULL, NULL);
  466. /* the context contains data valid for all threads */
  467. ctx = malloc(sizeof *ctx);
  468. ctx->sapi_module = &aolserver_sapi_module;
  469. ctx->ns_server = strdup(server);
  470. ctx->ns_module = strdup(module);
  471. /* read the configuration */
  472. php_ns_config(ctx, 1);
  473. global_context = ctx;
  474. /* register shutdown handler */
  475. Ns_RegisterServerShutdown(server, php_ns_server_shutdown, ctx);
  476. return NS_OK;
  477. }
  478. #endif