xref: /PHP-5.4/sapi/aolserver/aolserver.c (revision c0d060f5)
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