1: /*
2: +----------------------------------------------------------------------+
3: | PHP Version 5 |
4: +----------------------------------------------------------------------+
5: | Copyright (c) 1997-2012 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: aolserver.c,v 1.1.1.1 2012/02/21 23:48:06 misho Exp $ */
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
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
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
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
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:
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:
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: aolserver.c,v 1.1.1.1 2012/02/21 23:48:06 misho Exp $");
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:
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
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
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
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
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
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
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
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
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:
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
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>