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