summaryrefslogtreecommitdiff
path: root/sapi
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@baserock.org>2013-03-14 05:42:27 +0000
committer <>2013-04-03 16:25:08 +0000
commitc4dd7a1a684490673e25aaf4fabec5df138854c4 (patch)
tree4d57c44caae4480efff02b90b9be86f44bf25409 /sapi
downloadphp2-master.tar.gz
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'sapi')
-rw-r--r--sapi/aolserver/CREDITS2
-rw-r--r--sapi/aolserver/README69
-rw-r--r--sapi/aolserver/aolserver.c625
-rw-r--r--sapi/aolserver/config.m431
-rw-r--r--sapi/aolserver/config.w3216
-rw-r--r--sapi/aolserver/php.sym2
-rw-r--r--sapi/aolserver/php5aolserver.dsp135
-rw-r--r--sapi/apache/CREDITS3
-rw-r--r--sapi/apache/apMakefile.libdir4
-rw-r--r--sapi/apache/apMakefile.tmpl77
-rw-r--r--sapi/apache/config.m4273
-rw-r--r--sapi/apache/config.w3224
-rw-r--r--sapi/apache/libphp5.module.in11
-rw-r--r--sapi/apache/libpre.c55
-rw-r--r--sapi/apache/mod_php5.c1040
-rw-r--r--sapi/apache/mod_php5.exp1
-rw-r--r--sapi/apache/mod_php5.h60
-rw-r--r--sapi/apache/php.sym1
-rw-r--r--sapi/apache/php5apache.dsp151
-rw-r--r--sapi/apache/php_apache.c607
-rw-r--r--sapi/apache/php_apache_http.h70
-rw-r--r--sapi/apache/sapi_apache.c73
-rw-r--r--sapi/apache2filter/CREDITS2
-rw-r--r--sapi/apache2filter/EXPERIMENTAL5
-rw-r--r--sapi/apache2filter/README71
-rw-r--r--sapi/apache2filter/apache_config.c218
-rw-r--r--sapi/apache2filter/config.m4139
-rwxr-xr-xsapi/apache2filter/config.w3239
-rw-r--r--sapi/apache2filter/php.sym1
-rw-r--r--sapi/apache2filter/php_apache.h81
-rw-r--r--sapi/apache2filter/php_functions.c425
-rw-r--r--sapi/apache2filter/sapi_apache2.c764
-rw-r--r--sapi/apache2handler/CREDITS2
-rw-r--r--sapi/apache2handler/README76
-rw-r--r--sapi/apache2handler/apache_config.c241
-rw-r--r--sapi/apache2handler/config.m4138
-rw-r--r--sapi/apache2handler/config.w3258
-rw-r--r--sapi/apache2handler/mod_php5.c45
-rw-r--r--sapi/apache2handler/php.sym1
-rw-r--r--sapi/apache2handler/php5apache2.dsp146
-rw-r--r--sapi/apache2handler/php_apache.h91
-rw-r--r--sapi/apache2handler/php_functions.c571
-rw-r--r--sapi/apache2handler/sapi_apache2.c718
-rw-r--r--sapi/apache_hooks/CREDITS2
-rw-r--r--sapi/apache_hooks/README206
-rw-r--r--sapi/apache_hooks/apMakefile.libdir4
-rw-r--r--sapi/apache_hooks/apMakefile.tmpl77
-rw-r--r--sapi/apache_hooks/config.m4275
-rw-r--r--sapi/apache_hooks/config.w3221
-rw-r--r--sapi/apache_hooks/libphp5.module.in11
-rw-r--r--sapi/apache_hooks/mod_php5.c1493
-rw-r--r--sapi/apache_hooks/mod_php5.exp1
-rw-r--r--sapi/apache_hooks/mod_php5.h88
-rw-r--r--sapi/apache_hooks/php.sym1
-rwxr-xr-xsapi/apache_hooks/php5apache_hooks.dsp151
-rw-r--r--sapi/apache_hooks/php_apache.c1972
-rw-r--r--sapi/apache_hooks/php_apache_http.h44
-rw-r--r--sapi/apache_hooks/sapi_apache.c131
-rw-r--r--sapi/caudium/CREDITS2
-rw-r--r--sapi/caudium/README16
-rw-r--r--sapi/caudium/TODO30
-rw-r--r--sapi/caudium/caudium.c784
-rw-r--r--sapi/caudium/config.m498
-rwxr-xr-xsapi/cgi/CHANGES34
-rw-r--r--sapi/cgi/CREDITS2
-rw-r--r--sapi/cgi/Makefile.frag9
-rw-r--r--sapi/cgi/README.FastCGI151
-rw-r--r--sapi/cgi/cgi_main.c2606
-rw-r--r--sapi/cgi/config.w3210
-rw-r--r--sapi/cgi/config9.m476
-rw-r--r--sapi/cgi/fastcgi.c1535
-rw-r--r--sapi/cgi/fastcgi.h151
-rw-r--r--sapi/cgi/php.sym0
-rw-r--r--sapi/cgi/tests/001.phpt22
-rw-r--r--sapi/cgi/tests/002.phpt52
-rw-r--r--sapi/cgi/tests/003.phpt62
-rw-r--r--sapi/cgi/tests/004.phpt43
-rw-r--r--sapi/cgi/tests/005.phpt27
-rw-r--r--sapi/cgi/tests/006.phpt62
-rw-r--r--sapi/cgi/tests/007.phpt22
-rw-r--r--sapi/cgi/tests/008.phpt54
-rw-r--r--sapi/cgi/tests/009.phpt30
-rw-r--r--sapi/cgi/tests/010.phpt53
-rw-r--r--sapi/cgi/tests/011.phpt165
-rw-r--r--sapi/cgi/tests/apache_request_headers.phpt51
-rw-r--r--sapi/cgi/tests/bug61605.phpt34
-rw-r--r--sapi/cgi/tests/include.inc57
-rw-r--r--sapi/cgi/tests/skipif.inc17
-rw-r--r--sapi/cli/CREDITS2
-rw-r--r--sapi/cli/Makefile.frag13
-rw-r--r--sapi/cli/README20
-rw-r--r--sapi/cli/TODO2
-rw-r--r--sapi/cli/cli.h52
-rw-r--r--sapi/cli/cli_win32.c2
-rw-r--r--sapi/cli/config.m450
-rw-r--r--sapi/cli/config.w3221
-rw-r--r--sapi/cli/php.1.in451
-rw-r--r--sapi/cli/php_cli.c1399
-rw-r--r--sapi/cli/php_cli_server.c2425
-rw-r--r--sapi/cli/php_cli_server.h48
-rw-r--r--sapi/cli/php_http_parser.c1608
-rw-r--r--sapi/cli/php_http_parser.h180
-rw-r--r--sapi/cli/tests/001.phpt19
-rw-r--r--sapi/cli/tests/002-win32.phpt22
-rw-r--r--sapi/cli/tests/002.phpt22
-rw-r--r--sapi/cli/tests/003-2.phpt25
-rw-r--r--sapi/cli/tests/003.phpt32
-rw-r--r--sapi/cli/tests/004.phpt34
-rw-r--r--sapi/cli/tests/005.phpt104
-rw-r--r--sapi/cli/tests/006.phpt140
-rw-r--r--sapi/cli/tests/007.phpt52
-rw-r--r--sapi/cli/tests/008.phpt43
-rw-r--r--sapi/cli/tests/009.phpt20
-rw-r--r--sapi/cli/tests/010-2.phpt35
-rw-r--r--sapi/cli/tests/010.phpt46
-rw-r--r--sapi/cli/tests/011.phpt58
-rw-r--r--sapi/cli/tests/012.phpt38
-rw-r--r--sapi/cli/tests/013.phpt34
-rw-r--r--sapi/cli/tests/014.phpt44
-rw-r--r--sapi/cli/tests/015.phpt35
-rw-r--r--sapi/cli/tests/016.phpt130
-rw-r--r--sapi/cli/tests/017.phpt106
-rw-r--r--sapi/cli/tests/018.phpt27
-rw-r--r--sapi/cli/tests/019.phpt36
-rw-r--r--sapi/cli/tests/020.phpt32
-rw-r--r--sapi/cli/tests/021.phpt43
-rw-r--r--sapi/cli/tests/022.inc14
-rw-r--r--sapi/cli/tests/022.phpt47
-rw-r--r--sapi/cli/tests/bug43177.phpt82
-rw-r--r--sapi/cli/tests/bug44564.phpt22
-rw-r--r--sapi/cli/tests/bug61546.phpt31
-rw-r--r--sapi/cli/tests/bug61679.phpt43
-rw-r--r--sapi/cli/tests/bug61977.phpt55
-rw-r--r--sapi/cli/tests/php_cli_server.inc62
-rw-r--r--sapi/cli/tests/php_cli_server_001.phpt16
-rw-r--r--sapi/cli/tests/php_cli_server_002.phpt20
-rw-r--r--sapi/cli/tests/php_cli_server_003.phpt18
-rw-r--r--sapi/cli/tests/php_cli_server_004.phpt48
-rw-r--r--sapi/cli/tests/php_cli_server_005.phpt71
-rw-r--r--sapi/cli/tests/php_cli_server_006.phpt42
-rw-r--r--sapi/cli/tests/php_cli_server_007.phpt40
-rw-r--r--sapi/cli/tests/php_cli_server_008.phpt68
-rw-r--r--sapi/cli/tests/php_cli_server_009.phpt93
-rw-r--r--sapi/cli/tests/php_cli_server_010.phpt75
-rw-r--r--sapi/cli/tests/php_cli_server_011.phpt41
-rw-r--r--sapi/cli/tests/php_cli_server_012.phpt55
-rw-r--r--sapi/cli/tests/php_cli_server_013.phpt108
-rw-r--r--sapi/cli/tests/php_cli_server_014.phpt80
-rw-r--r--sapi/cli/tests/php_cli_server_015.phpt49
-rw-r--r--sapi/cli/tests/php_cli_server_016.phpt46
-rw-r--r--sapi/cli/tests/php_cli_server_017.phpt44
-rw-r--r--sapi/cli/tests/php_cli_server_018.phpt44
-rw-r--r--sapi/cli/tests/skipif.inc7
-rw-r--r--sapi/continuity/CREDITS2
-rw-r--r--sapi/continuity/capi.c508
-rw-r--r--sapi/continuity/config.m428
-rw-r--r--sapi/embed/CREDITS2
-rw-r--r--sapi/embed/EXPERIMENTAL5
-rw-r--r--sapi/embed/config.m433
-rw-r--r--sapi/embed/config.w329
-rw-r--r--sapi/embed/php5embed.dsp100
-rw-r--r--sapi/embed/php_embed.c241
-rw-r--r--sapi/embed/php_embed.h73
-rw-r--r--sapi/fpm/CREDITS2
-rw-r--r--sapi/fpm/LICENSE23
-rw-r--r--sapi/fpm/Makefile.frag23
-rw-r--r--sapi/fpm/config.m4657
-rw-r--r--sapi/fpm/fpm/events/devpoll.c248
-rw-r--r--sapi/fpm/fpm/events/devpoll.h29
-rw-r--r--sapi/fpm/fpm/events/epoll.c211
-rw-r--r--sapi/fpm/fpm/events/epoll.h29
-rw-r--r--sapi/fpm/fpm/events/kqueue.c208
-rw-r--r--sapi/fpm/fpm/events/kqueue.h29
-rw-r--r--sapi/fpm/fpm/events/poll.c276
-rw-r--r--sapi/fpm/fpm/events/poll.h29
-rw-r--r--sapi/fpm/fpm/events/port.c185
-rw-r--r--sapi/fpm/fpm/events/port.h29
-rw-r--r--sapi/fpm/fpm/events/select.c175
-rw-r--r--sapi/fpm/fpm/events/select.h29
-rw-r--r--sapi/fpm/fpm/fastcgi.c1111
-rw-r--r--sapi/fpm/fpm/fastcgi.h145
-rw-r--r--sapi/fpm/fpm/fpm.c123
-rw-r--r--sapi/fpm/fpm/fpm.h63
-rw-r--r--sapi/fpm/fpm/fpm_arrays.h116
-rw-r--r--sapi/fpm/fpm/fpm_atomic.h168
-rw-r--r--sapi/fpm/fpm/fpm_children.c478
-rw-r--r--sapi/fpm/fpm/fpm_children.h36
-rw-r--r--sapi/fpm/fpm/fpm_cleanup.c52
-rw-r--r--sapi/fpm/fpm/fpm_cleanup.h21
-rw-r--r--sapi/fpm/fpm/fpm_clock.c121
-rw-r--r--sapi/fpm/fpm/fpm_clock.h13
-rw-r--r--sapi/fpm/fpm/fpm_conf.c1658
-rw-r--r--sapi/fpm/fpm/fpm_conf.h106
-rw-r--r--sapi/fpm/fpm/fpm_config.h79
-rw-r--r--sapi/fpm/fpm/fpm_env.c276
-rw-r--r--sapi/fpm/fpm/fpm_env.h27
-rw-r--r--sapi/fpm/fpm/fpm_events.c533
-rw-r--r--sapi/fpm/fpm/fpm_events.h52
-rw-r--r--sapi/fpm/fpm/fpm_log.c466
-rw-r--r--sapi/fpm/fpm/fpm_log.h13
-rw-r--r--sapi/fpm/fpm/fpm_main.c2000
-rw-r--r--sapi/fpm/fpm/fpm_php.c297
-rw-r--r--sapi/fpm/fpm/fpm_php.h50
-rw-r--r--sapi/fpm/fpm/fpm_php_trace.c177
-rw-r--r--sapi/fpm/fpm/fpm_php_trace.h13
-rw-r--r--sapi/fpm/fpm/fpm_process_ctl.c539
-rw-r--r--sapi/fpm/fpm/fpm_process_ctl.h53
-rw-r--r--sapi/fpm/fpm/fpm_request.c316
-rw-r--r--sapi/fpm/fpm/fpm_request.h32
-rw-r--r--sapi/fpm/fpm/fpm_scoreboard.c331
-rw-r--r--sapi/fpm/fpm/fpm_scoreboard.h94
-rw-r--r--sapi/fpm/fpm/fpm_shm.c70
-rw-r--r--sapi/fpm/fpm/fpm_shm.h13
-rw-r--r--sapi/fpm/fpm/fpm_signals.c251
-rw-r--r--sapi/fpm/fpm/fpm_signals.h16
-rw-r--r--sapi/fpm/fpm/fpm_sockets.c477
-rw-r--r--sapi/fpm/fpm/fpm_sockets.h54
-rw-r--r--sapi/fpm/fpm/fpm_status.c477
-rw-r--r--sapi/fpm/fpm/fpm_status.h35
-rw-r--r--sapi/fpm/fpm/fpm_stdio.c301
-rw-r--r--sapi/fpm/fpm/fpm_stdio.h20
-rw-r--r--sapi/fpm/fpm/fpm_str.h29
-rw-r--r--sapi/fpm/fpm/fpm_trace.c41
-rw-r--r--sapi/fpm/fpm/fpm_trace.h17
-rw-r--r--sapi/fpm/fpm/fpm_trace_mach.c99
-rw-r--r--sapi/fpm/fpm/fpm_trace_pread.c67
-rw-r--r--sapi/fpm/fpm/fpm_trace_ptrace.c82
-rw-r--r--sapi/fpm/fpm/fpm_unix.c368
-rw-r--r--sapi/fpm/fpm/fpm_unix.h17
-rw-r--r--sapi/fpm/fpm/fpm_worker_pool.c65
-rw-r--r--sapi/fpm/fpm/fpm_worker_pool.h53
-rw-r--r--sapi/fpm/fpm/zlog.c199
-rw-r--r--sapi/fpm/fpm/zlog.h45
-rw-r--r--sapi/fpm/init.d.php-fpm.in138
-rw-r--r--sapi/fpm/php-fpm.8.in220
-rw-r--r--sapi/fpm/php-fpm.conf.in510
-rw-r--r--sapi/fpm/php-fpm.service.in12
-rw-r--r--sapi/fpm/status.html.in459
-rw-r--r--sapi/isapi/CREDITS2
-rw-r--r--sapi/isapi/config.m424
-rw-r--r--sapi/isapi/config.w3213
-rw-r--r--sapi/isapi/php.sym5
-rw-r--r--sapi/isapi/php5isapi.c973
-rw-r--r--sapi/isapi/php5isapi.def5
-rw-r--r--sapi/isapi/php5isapi.dsp165
-rw-r--r--sapi/isapi/stresstest/getopt.c175
-rw-r--r--sapi/isapi/stresstest/getopt.h12
-rw-r--r--sapi/isapi/stresstest/notes.txt56
-rw-r--r--sapi/isapi/stresstest/stresstest.cpp936
-rw-r--r--sapi/isapi/stresstest/stresstest.dsp108
-rw-r--r--sapi/litespeed/CREDITS2
-rw-r--r--sapi/litespeed/Makefile.frag9
-rw-r--r--sapi/litespeed/README225
-rw-r--r--sapi/litespeed/config.m431
-rw-r--r--sapi/litespeed/lsapi_main.c1118
-rw-r--r--sapi/litespeed/lsapidef.h196
-rw-r--r--sapi/litespeed/lsapilib.c2227
-rw-r--r--sapi/litespeed/lsapilib.h363
-rw-r--r--sapi/milter/CREDITS2
-rw-r--r--sapi/milter/EXPERIMENTAL5
-rw-r--r--sapi/milter/Makefile.frag8
-rw-r--r--sapi/milter/TODO5
-rw-r--r--sapi/milter/config.m431
-rw-r--r--sapi/milter/getopt.c173
-rw-r--r--sapi/milter/milter.php132
-rw-r--r--sapi/milter/php_getopt.h7
-rw-r--r--sapi/milter/php_milter.c1209
-rw-r--r--sapi/milter/php_milter.h31
-rw-r--r--sapi/nsapi/CREDITS2
-rw-r--r--sapi/nsapi/config.m439
-rw-r--r--sapi/nsapi/config.w3220
-rw-r--r--sapi/nsapi/nsapi-readme.txt154
-rw-r--r--sapi/nsapi/nsapi.c1104
-rw-r--r--sapi/nsapi/php5nsapi.dsp135
-rw-r--r--sapi/phttpd/CREDITS2
-rw-r--r--sapi/phttpd/README5
-rw-r--r--sapi/phttpd/config.m421
-rw-r--r--sapi/phttpd/php.sym4
-rw-r--r--sapi/phttpd/php_phttpd.h24
-rw-r--r--sapi/phttpd/phttpd.c301
-rw-r--r--sapi/pi3web/CREDITS2
-rw-r--r--sapi/pi3web/README50
-rw-r--r--sapi/pi3web/config.m427
-rw-r--r--sapi/pi3web/config.w3216
-rw-r--r--sapi/pi3web/php.sym0
-rw-r--r--sapi/pi3web/php5pi3web.dsp136
-rw-r--r--sapi/pi3web/pi3web_sapi.c439
-rw-r--r--sapi/pi3web/pi3web_sapi.h102
-rw-r--r--sapi/roxen/README18
-rw-r--r--sapi/roxen/TODO33
-rw-r--r--sapi/roxen/config.m455
-rw-r--r--sapi/roxen/roxen.c727
-rw-r--r--sapi/tests/test001.phpt16
-rw-r--r--sapi/tests/test002.phpt22
-rw-r--r--sapi/tests/test003.phpt21
-rw-r--r--sapi/tests/test004.phpt26
-rw-r--r--sapi/tests/test005.phpt27
-rw-r--r--sapi/tests/test006.phpt73
-rw-r--r--sapi/tests/test007.phpt46
-rw-r--r--sapi/thttpd/CREDITS2
-rw-r--r--sapi/thttpd/README85
-rw-r--r--sapi/thttpd/config.m439
-rw-r--r--sapi/thttpd/php.sym3
-rw-r--r--sapi/thttpd/php_thttpd.h35
-rw-r--r--sapi/thttpd/stub.c0
-rw-r--r--sapi/thttpd/thttpd.c772
-rw-r--r--sapi/thttpd/thttpd_patch2377
-rw-r--r--sapi/tux/CREDITS2
-rw-r--r--sapi/tux/README86
-rw-r--r--sapi/tux/config.m416
-rw-r--r--sapi/tux/php.sym2
-rw-r--r--sapi/tux/php_tux.c457
-rw-r--r--sapi/webjames/CREDITS2
-rw-r--r--sapi/webjames/README28
-rw-r--r--sapi/webjames/config.m421
-rw-r--r--sapi/webjames/php_webjames.h28
-rw-r--r--sapi/webjames/webjames.c330
317 files changed, 59722 insertions, 0 deletions
diff --git a/sapi/aolserver/CREDITS b/sapi/aolserver/CREDITS
new file mode 100644
index 0000000..8f9a4af
--- /dev/null
+++ b/sapi/aolserver/CREDITS
@@ -0,0 +1,2 @@
+AOLserver
+Sascha Schumann
diff --git a/sapi/aolserver/README b/sapi/aolserver/README
new file mode 100644
index 0000000..80c186e
--- /dev/null
+++ b/sapi/aolserver/README
@@ -0,0 +1,69 @@
+AOLserver README ($Id$)
+
+To compile PHP 4.0 as a module for AOLserver, you need:
+
+- installed AOLserver 3.1 or later
+ (see the note below for AOLserver 3.0)
+
+NOTE: You should not use this module in production. PHP is not 100% stable
+ yet in threaded mode. To increase reliability enable the Global Lock
+ by removing #define NO_GLOBAL_LOCK in main/main.c. Also don't use
+ php_value as it will lead to races in a sub-system (use an ini file
+ instead).
+
+
+1.) Configuring AOLserver
+
+Read doc/install.txt in the source distribution
+
+It usually boils down to changing the INST/PREFIX variable in
+include/Makefile.global and running make all install.
+
+2.) Configuring PHP
+
+$ ./configure \
+ --with-aolserver=/path/to/installed/aolserver \
+ <other options>
+
+NOTE: If you are still using AOLserver 3.0, you need to retain the
+ AOLserver source code and pass another option to PHP:
+
+ --with-aolserver-src=/path/to/source/distribution
+
+3.) Compiling and Installing PHP
+
+$ make install
+
+4.) Changing nsd.tcl
+
+a) New section
+
+Add a new section to pass options to PHP (required):
+
+ns_section "ns/server/${servername}/module/php"
+
+You can use the following commands in this section:
+
+The 'map' command will cause AOLserver to pass all requests to *.php to
+the PHP module (can be specified multiple times). Example:
+
+ns_param map *.php
+
+The 'php_value "name val"' command assigns the configuration option name
+the value val (can be used multiple times). Example:
+
+ns_param php_value "session.auto_start 1"
+
+b) Enabling PHP
+
+Then enable the PHP module:
+
+ns_section "ns/server/${servername}/modules"
+...
+ns_param php ${bindir}/libphp5.so
+
+
+=============================================================================
+This has been tested with AOLserver release 3.0.
+
+AOLserver support has been written by Sascha Schumann <sascha@schumann.cx>.
diff --git a/sapi/aolserver/aolserver.c b/sapi/aolserver/aolserver.c
new file mode 100644
index 0000000..3dcbc8d
--- /dev/null
+++ b/sapi/aolserver/aolserver.c
@@ -0,0 +1,625 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+ */
+
+/*
+ * TODO:
+ * - write documentation
+ * - CGI/1.1 conformance
+ */
+
+/* $Id$ */
+
+/* conflict between PHP and AOLserver headers */
+#define Debug php_Debug
+#include "php.h"
+#undef Debug
+
+#ifdef HAVE_AOLSERVER
+
+#ifndef ZTS
+#error AOLserver module is only useable in thread-safe mode
+#endif
+
+#include "ext/standard/info.h"
+#define SECTION(name) PUTS("<h2>" name "</h2>\n")
+
+#define NS_BUF_SIZE 511
+
+#include "php_ini.h"
+#include "php_globals.h"
+#include "SAPI.h"
+#include "php_main.h"
+#include "php_variables.h"
+
+#include "ns.h"
+
+#include "php_version.h"
+
+/* This symbol is used by AOLserver to tell the API version we expect */
+
+int Ns_ModuleVersion = 1;
+
+#define NSG(v) TSRMG(ns_globals_id, ns_globals_struct *, v)
+
+/* php_ns_context is per-server (thus only once at all) */
+
+typedef struct {
+ sapi_module_struct *sapi_module;
+ char *ns_server;
+ char *ns_module;
+} php_ns_context;
+
+/* ns_globals_struct is per-thread */
+
+typedef struct {
+ Ns_Conn *conn;
+ size_t data_avail;
+} ns_globals_struct;
+
+/* TSRM id */
+
+static int ns_globals_id;
+
+/* global context */
+
+static php_ns_context *global_context;
+
+static void php_ns_config(php_ns_context *ctx, char global);
+
+/*
+ * php_ns_sapi_ub_write() writes data to the client connection.
+ */
+
+static int
+php_ns_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ int n;
+ uint sent = 0;
+
+ while (str_length > 0) {
+ n = Ns_ConnWrite(NSG(conn), (void *) str, str_length);
+
+ if (n == -1)
+ php_handle_aborted_connection();
+
+ str += n;
+ sent += n;
+ str_length -= n;
+ }
+
+ return sent;
+}
+
+/*
+ * php_ns_sapi_header_handler() sets a HTTP reply header to be
+ * sent to the client.
+ */
+
+static int
+php_ns_sapi_header_handler(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ char *header_name, *header_content;
+ char *p;
+
+ header_name = sapi_header->header;
+ header_content = p = strchr(header_name, ':');
+
+ if (p) {
+ *p = '\0';
+ do {
+ header_content++;
+ } while (*header_content == ' ');
+
+ if (!strcasecmp(header_name, "Content-type")) {
+ Ns_ConnSetTypeHeader(NSG(conn), header_content);
+ } else {
+ Ns_ConnSetHeaders(NSG(conn), header_name, header_content);
+ }
+
+ *p = ':';
+ }
+
+ sapi_free_header(sapi_header);
+
+ return 0;
+}
+
+/*
+ * php_ns_sapi_send_headers() flushes the headers to the client.
+ * Called before real content is sent by PHP.
+ */
+
+static int
+php_ns_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ if(SG(sapi_headers).send_default_content_type) {
+ Ns_ConnSetRequiredHeaders(NSG(conn), "text/html", 0);
+ }
+
+ Ns_ConnFlushHeaders(NSG(conn), SG(sapi_headers).http_response_code);
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+/*
+ * php_ns_sapi_read_post() reads a specified number of bytes from
+ * the client. Used for POST/PUT requests.
+ */
+
+static int
+php_ns_sapi_read_post(char *buf, uint count_bytes TSRMLS_DC)
+{
+ uint max_read;
+ uint total_read = 0;
+
+ max_read = MIN(NSG(data_avail), count_bytes);
+
+ total_read = Ns_ConnRead(NSG(conn), buf, max_read);
+
+ if(total_read == NS_ERROR) {
+ total_read = -1;
+ } else {
+ NSG(data_avail) -= total_read;
+ }
+
+ return total_read;
+}
+
+/*
+ * php_ns_sapi_read_cookies() returns the Cookie header from
+ * the HTTP request header
+ */
+
+static char *php_ns_sapi_read_cookies(TSRMLS_D)
+{
+ int i;
+ char *http_cookie = NULL;
+
+ i = Ns_SetIFind(NSG(conn->headers), "cookie");
+ if(i != -1) {
+ http_cookie = Ns_SetValue(NSG(conn->headers), i);
+ }
+
+ return http_cookie;
+}
+
+static void php_info_aolserver(ZEND_MODULE_INFO_FUNC_ARGS)
+{
+ char buf[512];
+ int uptime = Ns_InfoUptime();
+ int i;
+
+ php_info_print_table_start();
+ php_info_print_table_row(2, "SAPI module version", "$Id$");
+ php_info_print_table_row(2, "Build date", Ns_InfoBuildDate());
+ php_info_print_table_row(2, "Config file path", Ns_InfoConfigFile());
+ php_info_print_table_row(2, "Error Log path", Ns_InfoErrorLog());
+ php_info_print_table_row(2, "Installation path", Ns_InfoHomePath());
+ php_info_print_table_row(2, "Hostname of server", Ns_InfoHostname());
+ php_info_print_table_row(2, "Source code label", Ns_InfoLabel());
+ php_info_print_table_row(2, "Server platform", Ns_InfoPlatform());
+ snprintf(buf, 511, "%s/%s", Ns_InfoServerName(), Ns_InfoServerVersion());
+ php_info_print_table_row(2, "Server version", buf);
+ snprintf(buf, 511, "%d day(s), %02d:%02d:%02d",
+ uptime / 86400,
+ (uptime / 3600) % 24,
+ (uptime / 60) % 60,
+ uptime % 60);
+ php_info_print_table_row(2, "Server uptime", buf);
+ php_info_print_table_end();
+
+ SECTION("HTTP Headers Information");
+ php_info_print_table_start();
+ php_info_print_table_colspan_header(2, "HTTP Request Headers");
+ php_info_print_table_row(2, "HTTP Request", NSG(conn)->request->line);
+ for (i = 0; i < Ns_SetSize(NSG(conn)->headers); i++) {
+ php_info_print_table_row(2, Ns_SetKey(NSG(conn)->headers, i), Ns_SetValue(NSG(conn)->headers, i));
+ }
+
+ php_info_print_table_colspan_header(2, "HTTP Response Headers");
+ for (i = 0; i < Ns_SetSize(NSG(conn)->outputheaders); i++) {
+ php_info_print_table_row(2, Ns_SetKey(NSG(conn)->outputheaders, i), Ns_SetValue(NSG(conn)->outputheaders, i));
+ }
+ php_info_print_table_end();
+}
+
+PHP_FUNCTION(getallheaders);
+
+/* {{{ arginfo */
+ZEND_BEGIN_ARG_INFO(arginfo_aolserver_getallheaders, 0)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+static const zend_function_entry aolserver_functions[] = {
+ PHP_FE(getallheaders, arginfo_aolserver_getallheaders)
+ {NULL, NULL, NULL}
+};
+
+static zend_module_entry php_aolserver_module = {
+ STANDARD_MODULE_HEADER,
+ "AOLserver",
+ aolserver_functions,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ php_info_aolserver,
+ NULL,
+ STANDARD_MODULE_PROPERTIES
+};
+
+PHP_FUNCTION(getallheaders)
+{
+ int i;
+
+ array_init(return_value);
+
+ for (i = 0; i < Ns_SetSize(NSG(conn->headers)); i++) {
+ char *key = Ns_SetKey(NSG(conn->headers), i);
+ char *value = Ns_SetValue(NSG(conn->headers), i);
+
+ add_assoc_string(return_value, key, value, 1);
+ }
+}
+
+static int
+php_ns_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &php_aolserver_module, 1) == FAILURE) {
+ return FAILURE;
+ } else {
+ return SUCCESS;
+ }
+}
+
+
+/*
+ * php_ns_sapi_register_variables() populates the php script environment
+ * with a number of variables. HTTP_* variables are created for
+ * the HTTP header data, so that a script can access these.
+ */
+
+#define ADD_STRINGX(name, buf) \
+ php_register_variable(name, buf, track_vars_array TSRMLS_CC)
+
+#define ADD_STRING(name) \
+ ADD_STRINGX(name, buf)
+
+static void
+php_ns_sapi_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ int i;
+ char buf[NS_BUF_SIZE + 1];
+ char *tmp;
+
+ for(i = 0; i < Ns_SetSize(NSG(conn->headers)); i++) {
+ char *key = Ns_SetKey(NSG(conn->headers), i);
+ char *value = Ns_SetValue(NSG(conn->headers), i);
+ char *p;
+ char c;
+
+ snprintf(buf, NS_BUF_SIZE, "HTTP_%s", key);
+
+ for(p = buf + 5; (c = *p); p++) {
+ c = toupper(c);
+ if(c < 'A' || c > 'Z') {
+ c = '_';
+ }
+ *p = c;
+ }
+
+ ADD_STRINGX(buf, value);
+ }
+
+ snprintf(buf, NS_BUF_SIZE, "%s/%s", Ns_InfoServerName(), Ns_InfoServerVersion());
+ ADD_STRING("SERVER_SOFTWARE");
+ snprintf(buf, NS_BUF_SIZE, "HTTP/%1.1f", NSG(conn)->request->version);
+ ADD_STRING("SERVER_PROTOCOL");
+
+ ADD_STRINGX("REQUEST_METHOD", NSG(conn)->request->method);
+
+ if(NSG(conn)->request->query)
+ ADD_STRINGX("QUERY_STRING", NSG(conn)->request->query);
+
+ ADD_STRINGX("SERVER_BUILDDATE", Ns_InfoBuildDate());
+
+ ADD_STRINGX("REMOTE_ADDR", Ns_ConnPeer(NSG(conn)));
+
+ snprintf(buf, NS_BUF_SIZE, "%d", Ns_ConnPeerPort(NSG(conn)));
+ ADD_STRING("REMOTE_PORT");
+
+ snprintf(buf, NS_BUF_SIZE, "%d", Ns_ConnPort(NSG(conn)));
+ ADD_STRING("SERVER_PORT");
+
+ tmp = Ns_ConnHost(NSG(conn));
+ if (tmp)
+ ADD_STRINGX("SERVER_NAME", tmp);
+
+ ADD_STRINGX("PATH_TRANSLATED", SG(request_info).path_translated);
+ ADD_STRINGX("REQUEST_URI", SG(request_info).request_uri);
+ ADD_STRINGX("PHP_SELF", SG(request_info).request_uri);
+
+ ADD_STRINGX("GATEWAY_INTERFACE", "CGI/1.1");
+
+ snprintf(buf, NS_BUF_SIZE, "%d", Ns_InfoBootTime());
+ ADD_STRING("SERVER_BOOTTIME");
+}
+
+
+
+/* this structure is static (as in "it does not change") */
+
+static sapi_module_struct aolserver_sapi_module = {
+ "aolserver",
+ "AOLserver",
+
+ php_ns_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ php_ns_sapi_ub_write, /* unbuffered write */
+ NULL, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ php_ns_sapi_header_handler, /* header handler */
+ php_ns_sapi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ php_ns_sapi_read_post, /* read POST data */
+ php_ns_sapi_read_cookies, /* read Cookies */
+
+ php_ns_sapi_register_variables,
+ NULL, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+/*
+ * php_ns_module_main() is called by the per-request handler and
+ * "executes" the script
+ */
+
+static int
+php_ns_module_main(TSRMLS_D)
+{
+ zend_file_handle file_handle;
+
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.free_filename = 0;
+ file_handle.opened_path = NULL;
+
+ php_ns_config(global_context, 0);
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ return NS_ERROR;
+ }
+
+ php_execute_script(&file_handle TSRMLS_CC);
+ php_request_shutdown(NULL);
+
+ return NS_OK;
+}
+
+/*
+ * php_ns_request_ctor() initializes the per-request data structure
+ * and fills it with data provided by the web server
+ */
+
+static void
+php_ns_request_ctor(TSRMLS_D)
+{
+ char *server;
+ Ns_DString ds;
+ char *root;
+ int index;
+ char *tmp;
+
+ server = Ns_ConnServer(NSG(conn));
+
+#define safe_strdup(x) ((x)?strdup((x)):NULL)
+ SG(request_info).query_string = safe_strdup(NSG(conn->request->query));
+
+ Ns_DStringInit(&ds);
+ Ns_UrlToFile(&ds, server, NSG(conn->request->url));
+
+ /* path_translated is the absolute path to the file */
+ SG(request_info).path_translated = safe_strdup(Ns_DStringValue(&ds));
+ Ns_DStringFree(&ds);
+ root = Ns_PageRoot(server);
+ SG(request_info).request_uri = strdup(SG(request_info).path_translated + strlen(root));
+ SG(request_info).request_method = NSG(conn)->request->method;
+ if(NSG(conn)->request->version > 1.0) SG(request_info).proto_num = 1001;
+ else SG(request_info).proto_num = 1000;
+ SG(request_info).content_length = Ns_ConnContentLength(NSG(conn));
+ index = Ns_SetIFind(NSG(conn)->headers, "content-type");
+ SG(request_info).content_type = index == -1 ? NULL :
+ Ns_SetValue(NSG(conn)->headers, index);
+ SG(sapi_headers).http_response_code = 200;
+
+ tmp = Ns_ConnAuthUser(NSG(conn));
+ if (tmp)
+ tmp = estrdup(tmp);
+ SG(request_info).auth_user = tmp;
+
+ tmp = Ns_ConnAuthPasswd(NSG(conn));
+ if (tmp)
+ tmp = estrdup(tmp);
+ SG(request_info).auth_password = tmp;
+
+ NSG(data_avail) = SG(request_info).content_length;
+}
+
+/*
+ * php_ns_request_dtor() destroys all data associated with
+ * the per-request structure
+ */
+
+static void
+php_ns_request_dtor(TSRMLS_D)
+{
+ free(SG(request_info).path_translated);
+ if (SG(request_info).query_string)
+ free(SG(request_info).query_string);
+ free(SG(request_info).request_uri);
+}
+
+/*
+ * The php_ns_request_handler() is called per request and handles
+ * everything for one request.
+ */
+
+static int
+php_ns_request_handler(void *context, Ns_Conn *conn)
+{
+ int status = NS_OK;
+ TSRMLS_FETCH();
+
+ NSG(conn) = conn;
+
+ SG(server_context) = global_context;
+
+ php_ns_request_ctor(TSRMLS_C);
+
+ status = php_ns_module_main(TSRMLS_C);
+
+ php_ns_request_dtor(TSRMLS_C);
+
+ return status;
+}
+
+/*
+ * php_ns_config() fetches the configuration data.
+ *
+ * It understands the "map" and "php_value" command.
+ */
+
+static void
+php_ns_config(php_ns_context *ctx, char global)
+{
+ int i;
+ char *path;
+ Ns_Set *set;
+
+ path = Ns_ConfigGetPath(ctx->ns_server, ctx->ns_module, NULL);
+ set = Ns_ConfigGetSection(path);
+
+ for (i = 0; set && i < Ns_SetSize(set); i++) {
+ char *key = Ns_SetKey(set, i);
+ char *value = Ns_SetValue(set, i);
+
+ if (global && !strcasecmp(key, "map")) {
+ Ns_Log(Notice, "Registering PHP for \"%s\"", value);
+ Ns_RegisterRequest(ctx->ns_server, "GET", value, php_ns_request_handler, NULL, ctx, 0);
+ Ns_RegisterRequest(ctx->ns_server, "POST", value, php_ns_request_handler, NULL, ctx, 0);
+ Ns_RegisterRequest(ctx->ns_server, "HEAD", value, php_ns_request_handler, NULL, ctx, 0);
+
+ /*
+ * Deactivated for now. The ini system will cause random crashes when
+ * accessed from here (since there are no locks to protect the global
+ * known_directives)
+ */
+
+ } else if (!global && !strcasecmp(key, "php_value")) {
+ Ns_Log(Notice, "php_value has been deactivated temporarily. Please use a php.ini file to pass directives to PHP. Thanks.");
+#if 0
+ char *val;
+
+ val = strchr(value, ' ');
+ if (val) {
+ char *new_key;
+
+ new_key = estrndup(value, val - value);
+
+ do {
+ val++;
+ } while(*val == ' ');
+
+ Ns_Log(Debug, "PHP configuration option '%s=%s'", new_key, val);
+ zend_alter_ini_entry(new_key, strlen(new_key) + 1, val,
+ strlen(val) + 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+
+ efree(new_key);
+ }
+#endif
+ }
+
+ }
+}
+
+/*
+ * php_ns_server_shutdown() performs the last steps before the
+ * server exits. Shutdowns basic services and frees memory
+ */
+
+static void
+php_ns_server_shutdown(void *context)
+{
+ php_ns_context *ctx = (php_ns_context *) context;
+
+ ctx->sapi_module->shutdown(ctx->sapi_module);
+ sapi_shutdown();
+ tsrm_shutdown();
+
+ free(ctx->ns_module);
+ free(ctx->ns_server);
+ free(ctx);
+}
+
+/*
+ * Ns_ModuleInit() is called by AOLserver once at startup
+ *
+ * This functions allocates basic structures and initializes
+ * basic services.
+ */
+
+int Ns_ModuleInit(char *server, char *module)
+{
+ php_ns_context *ctx;
+
+ tsrm_startup(1, 1, 0, NULL);
+ sapi_startup(&aolserver_sapi_module);
+ sapi_module.startup(&aolserver_sapi_module);
+
+ /* TSRM is used to allocate a per-thread structure */
+ ts_allocate_id(&ns_globals_id, sizeof(ns_globals_struct), NULL, NULL);
+
+ /* the context contains data valid for all threads */
+ ctx = malloc(sizeof *ctx);
+ ctx->sapi_module = &aolserver_sapi_module;
+ ctx->ns_server = strdup(server);
+ ctx->ns_module = strdup(module);
+
+ /* read the configuration */
+ php_ns_config(ctx, 1);
+
+ global_context = ctx;
+
+ /* register shutdown handler */
+ Ns_RegisterServerShutdown(server, php_ns_server_shutdown, ctx);
+
+ return NS_OK;
+}
+
+#endif
diff --git a/sapi/aolserver/config.m4 b/sapi/aolserver/config.m4
new file mode 100644
index 0000000..a193bfd
--- /dev/null
+++ b/sapi/aolserver/config.m4
@@ -0,0 +1,31 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(aolserver,,
+[ --with-aolserver=DIR Specify path to the installed AOLserver], no, no)
+
+AC_MSG_CHECKING([for AOLserver support])
+
+if test "$PHP_AOLSERVER" != "no"; then
+ if test -d "$PHP_AOLSERVER/include"; then
+ PHP_AOLSERVER_SRC=$PHP_AOLSERVER
+ fi
+ if test -z "$PHP_AOLSERVER_SRC" || test ! -d $PHP_AOLSERVER_SRC/include; then
+ AC_MSG_ERROR(Please specify the path to the source distribution of AOLserver using --with-aolserver-src=DIR)
+ fi
+ if test ! -d $PHP_AOLSERVER/bin ; then
+ AC_MSG_ERROR(Please specify the path to the root of AOLserver using --with-aolserver=DIR)
+ fi
+ PHP_BUILD_THREAD_SAFE
+ PHP_ADD_INCLUDE($PHP_AOLSERVER_SRC/include)
+ AC_DEFINE(HAVE_AOLSERVER,1,[Whether you have AOLserver])
+ PHP_SELECT_SAPI(aolserver, shared, aolserver.c)
+ INSTALL_IT="\$(INSTALL) -m 0755 $SAPI_SHARED \$(INSTALL_ROOT)$PHP_AOLSERVER/bin/"
+fi
+
+AC_MSG_RESULT([$PHP_AOLSERVER])
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/aolserver/config.w32 b/sapi/aolserver/config.w32
new file mode 100644
index 0000000..75b4361
--- /dev/null
+++ b/sapi/aolserver/config.w32
@@ -0,0 +1,16 @@
+// vim:ft=javascript
+// $Id$
+
+ARG_WITH('aolserver', 'Build AOLserver support', 'no');
+
+if (PHP_AOLSERVER != "no") {
+ if (PHP_ZTS == "no") {
+ WARNING("AOLSERVER module requires an --enable-zts build of PHP");
+ } else {
+ if (CHECK_HEADER_ADD_INCLUDE("ns.h", "CFLAGS_AOLSERVER", PHP_AOLSERVER) && CHECK_LIB("nsd.lib", "aolserver", PHP_AOLSERVER)) {
+ SAPI('aolserver', 'aolserver.c', 'php' + PHP_VERSION + 'aolserver.so', '/D XP_WIN32 ');
+ } else {
+ WARNING("sapi/aolserver not enabled: Could not find libraries/headers");
+ }
+ }
+}
diff --git a/sapi/aolserver/php.sym b/sapi/aolserver/php.sym
new file mode 100644
index 0000000..b401ffd
--- /dev/null
+++ b/sapi/aolserver/php.sym
@@ -0,0 +1,2 @@
+Ns_ModuleVersion
+Ns_ModuleInit
diff --git a/sapi/aolserver/php5aolserver.dsp b/sapi/aolserver/php5aolserver.dsp
new file mode 100644
index 0000000..dd6ad71
--- /dev/null
+++ b/sapi/aolserver/php5aolserver.dsp
@@ -0,0 +1,135 @@
+# Microsoft Developer Studio Project File - Name="php5aolserver" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=php5aolserver - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "php5aolserver.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "php5aolserver.mak" CFG="php5aolserver - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "php5aolserver - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5aolserver - Win32 Release_TS_inline" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5aolserver - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "php5aolserver - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP5AOLSERVER_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /I "..\..\..\php_build\nsapi30\include\\" /I "..\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\main" /I "..\..\tsrm" /D ZEND_DEBUG=0 /D "NDEBUG" /D "PHP5AOLSERVER_EXPORTS" /D "PHP_WIN32" /D "ZTS" /D "ZEND_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "WIN32" /D "_MBCS" /D "HAVE_AOLSERVER" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 nsd.lib php5ts.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x62000000" /version:4.0 /dll /machine:I386 /out:"../../Release_TS/php5aolserver.so" /libpath:"..\..\..\php_build\nsapi30\lib\\" /libpath:"..\..\Release_TS" /libpath:"..\..\TSRM\Release_TS" /libpath:"..\..\Zend\Release_TS"
+
+!ELSEIF "$(CFG)" == "php5aolserver - Win32 Release_TS_inline"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS_inline"
+# PROP BASE Intermediate_Dir "Release_TS_inline"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS_inline"
+# PROP Intermediate_Dir "Release_TS_inline"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP5AOLSERVER_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /I "..\..\..\php_build\nsapi30\include\\" /I "..\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\main" /I "..\..\tsrm" /D ZEND_DEBUG=0 /D "ZEND_WIN32_FORCE_INLINE" /D "NDEBUG" /D "PHPAOLSERVER_EXPORTS" /D "PHP_WIN32" /D "ZTS" /D "ZEND_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "WIN32" /D "_MBCS" /D "HAVE_AOLSERVER" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 nsd.lib php5ts.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x62000000" /version:4.0 /dll /machine:I386 /out:"../../Release_TS_inline/php5aolserver.so" /libpath:"..\..\..\php_build\nsapi30\lib\\" /libpath:"..\..\Release_TS_inline" /libpath:"..\..\TSRM\Release_TS_inline" /libpath:"..\..\Zend\Release_TS_inline"
+
+!ELSEIF "$(CFG)" == "php5aolserver - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\..\Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP5AOLSERVER_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "." /I "..\..\..\php_build\nsapi30\include\\" /I "..\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\main" /I "..\..\tsrm" /D "_DEBUG" /D ZEND_DEBUG=1 /D "PHP5AOLSERVER_EXPORTS" /D "PHP_WIN32" /D "ZTS" /D "ZEND_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "WIN32" /D "_MBCS" /D "HAVE_AOLSERVER" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 nsd.lib php5ts_debug.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x62000000" /version:4.0 /dll /debug /machine:I386 /out:"..\..\Debug_TS/php5aolserver.so" /pdbtype:sept /libpath:"..\..\..\php_build\nsapi30\lib\\" /libpath:"..\..\Debug_TS" /libpath:"..\..\TSRM\Debug_TS" /libpath:"..\..\Zend\Debug_TS"
+
+!ENDIF
+
+# Begin Target
+
+# Name "php5aolserver - Win32 Release_TS"
+# Name "php5aolserver - Win32 Release_TS_inline"
+# Name "php5aolserver - Win32 Debug_TS"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\aolserver.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/sapi/apache/CREDITS b/sapi/apache/CREDITS
new file mode 100644
index 0000000..991deb5
--- /dev/null
+++ b/sapi/apache/CREDITS
@@ -0,0 +1,3 @@
+Apache 1.3
+Rasmus Lerdorf, Zeev Suraski, Stig Bakken, David Sklar
+
diff --git a/sapi/apache/apMakefile.libdir b/sapi/apache/apMakefile.libdir
new file mode 100644
index 0000000..7b52540
--- /dev/null
+++ b/sapi/apache/apMakefile.libdir
@@ -0,0 +1,4 @@
+This is a place-holder which indicates to Configure that it shouldn't
+provide the default targets when building the Makefile in this directory.
+Instead it'll just prepend all the important variable definitions, and
+copy the Makefile.tmpl onto the end.
diff --git a/sapi/apache/apMakefile.tmpl b/sapi/apache/apMakefile.tmpl
new file mode 100644
index 0000000..5f77d9c
--- /dev/null
+++ b/sapi/apache/apMakefile.tmpl
@@ -0,0 +1,77 @@
+##
+## Apache 1.3 Makefile template for PHP 5.0 Module
+## [src/modules/php5/Makefile.tmpl]
+##
+
+# the parametrized target
+LIB=libphp5.$(LIBEXT)
+
+# objects for building the static library
+OBJS=mod_php5.o
+OBJS_LIB=libmodphp5.a
+
+# objects for building the shared object library
+SHLIB_OBJS=mod_php5.so-o
+SHLIB_OBJS_LIB=libmodphp5.a
+
+# the general targets
+all: lib
+lib: $(LIB)
+
+# build the static library by merging the object files
+libphp5.a: $(OBJS) $(OBJS_LIB)
+ cp $(OBJS_LIB) $@
+ ar r $@ $(OBJS)
+ $(RANLIB) $@
+
+# ugly hack to support older Apache-1.3 betas that don't set $LIBEXT
+libphp5.: $(OBJS) $(OBJS_LIB)
+ cp $(OBJS_LIB) $@
+ ar r $@ $(OBJS)
+ $(RANLIB) $@
+ cp libphp5. libphp5.a
+
+# build the shared object library by linking the object files
+libphp5.so: $(SHLIB_OBJS) $(SHLIB_OBJS_LIB)
+ rm -f $@
+ $(LD_SHLIB) $(LDFLAGS_SHLIB) -o $@ $(SHLIB_OBJS) $(SHLIB_OBJS_LIB) $(LIBS) $(PHP_LIBS)
+
+# 1. extension .o for shared objects cannot be used here because
+# first these files aren't still shared objects and second we
+# have to use a different name to trigger the different
+# implicit Make rule
+# 2. extension -so.o (as used elsewhere) cannot be used because
+# the suffix feature of Make really wants just .x, so we use
+# extension .so-o
+.SUFFIXES: .o .so-o
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $(PHP_CFLAGS) $(CPPFLAGS) $(SPACER) $<
+.c.so-o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $(CFLAGS_SHLIB) $(PHP_CFLAGS) $(CPPFLAGS) $(SPACER) $< && mv $*.o $*.so-o
+
+# cleanup
+clean:
+ -rm -f $(OBJS) $(SHLIB_OBJS) $(LIB)
+
+# We really don't expect end users to use this rule. It works only with
+# gcc, and rebuilds Makefile.tmpl. You have to re-run Configure after
+# using it.
+depend:
+ cp Makefile.tmpl Makefile.tmpl.bak \
+ && sed -ne '1,/^# DO NOT REMOVE/p' Makefile.tmpl > Makefile.new \
+ && gcc -MM $(INCLUDES) $(CFLAGS) $(PHP_CFLAGS) $(CPPFLAGS) *.c >> Makefile.new \
+ && sed -e '1,$$s: $(INCDIR)/: $$(INCDIR)/:g' Makefile.new \
+ > Makefile.tmpl \
+ && rm Makefile.new
+
+#Dependencies
+
+$(OBJS): Makefile
+
+# DO NOT REMOVE
+mod_php5.o: mod_php5.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \
+ $(INCDIR)/buff.h \
+ $(INCDIR)/http_config.h \
+ $(INCDIR)/http_core.h $(INCDIR)/http_main.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_log.h $(INCDIR)/util_script.h mod_php5.h
diff --git a/sapi/apache/config.m4 b/sapi/apache/config.m4
new file mode 100644
index 0000000..af83e9b
--- /dev/null
+++ b/sapi/apache/config.m4
@@ -0,0 +1,273 @@
+dnl
+dnl $Id$
+dnl
+AC_DEFUN([PHP_APACHE_FD_CHECK], [
+AC_CACHE_CHECK([for member fd in BUFF *],ac_cv_php_fd_in_buff,[
+ save=$CPPFLAGS
+ if test -n "$APXS_INCLUDEDIR"; then
+ CPPFLAGS="$CPPFLAGS -I$APXS_INCLUDEDIR"
+ else
+ CPPFLAGS="$CPPFLAGS $APACHE_INCLUDE"
+ fi
+ AC_TRY_COMPILE([#include <httpd.h>],[conn_rec *c; int fd = c->client->fd;],[
+ ac_cv_php_fd_in_buff=yes],[ac_cv_php_fd_in_buff=no],[ac_cv_php_fd_in_buff=no])
+ CPPFLAGS=$save
+])
+if test "$ac_cv_php_fd_in_buff" = "yes"; then
+ AC_DEFINE(PHP_APACHE_HAVE_CLIENT_FD,1,[ ])
+fi
+])
+
+dnl Apache 1.x shared module
+PHP_ARG_WITH(apxs,,
+[ --with-apxs[=FILE] Build shared Apache 1.x module. FILE is the optional
+ pathname to the Apache apxs tool [apxs]], no, no)
+
+AC_MSG_CHECKING([for Apache 1.x module support via DSO through APXS])
+
+if test "$PHP_APXS" != "no"; then
+ if test "$PHP_APXS" = "yes"; then
+ APXS=apxs
+ $APXS -q CFLAGS >/dev/null 2>&1
+ if test "$?" != "0" && test -x /usr/sbin/apxs; then #SUSE 6.x
+ APXS=/usr/sbin/apxs
+ fi
+ else
+ PHP_EXPAND_PATH($PHP_APXS, APXS)
+ fi
+
+ $APXS -q CFLAGS >/dev/null 2>&1
+ if test "$?" != "0"; then
+ AC_MSG_RESULT()
+ AC_MSG_RESULT()
+ AC_MSG_RESULT([Sorry, I was not able to successfully run APXS. Possible reasons:])
+ AC_MSG_RESULT()
+ AC_MSG_RESULT([1. Perl is not installed;])
+ AC_MSG_RESULT([2. Apache was not compiled with DSO support (--enable-module=so);])
+ AC_MSG_RESULT([3. 'apxs' is not in your path. Try to use --with-apxs=/path/to/apxs])
+ AC_MSG_RESULT([The output of $APXS follows])
+ $APXS -q CFLAGS
+ AC_MSG_ERROR([Aborting])
+ fi
+
+ APXS_LDFLAGS="@SYBASE_LFLAGS@ @SYBASE_LIBS@ @SYBASE_CT_LFLAGS@ @SYBASE_CT_LIBS@"
+ APXS_INCLUDEDIR=`$APXS -q INCLUDEDIR`
+ APXS_CFLAGS=`$APXS -q CFLAGS`
+ APXS_HTTPD=`$APXS -q SBINDIR`/`$APXS -q TARGET`
+ APACHE_INCLUDE=-I$APXS_INCLUDEDIR
+
+ # Test that we're trying to configure with apache 1.x
+ PHP_AP_EXTRACT_VERSION($APXS_HTTPD)
+ if test "$APACHE_VERSION" -ge 2000000; then
+ AC_MSG_ERROR([You have enabled Apache 1.3 support while your server is Apache 2. Please use the appropiate switch --with-apxs2])
+ fi
+
+ for flag in $APXS_CFLAGS; do
+ case $flag in
+ -D*) APACHE_CPPFLAGS="$APACHE_CPPFLAGS $flag";;
+ esac
+ done
+
+ case $host_alias in
+ *aix*)
+ APXS_LIBEXECDIR=`$APXS -q LIBEXECDIR`
+ EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-brtl -Wl,-bI:$APXS_LIBEXECDIR/httpd.exp"
+ PHP_AIX_LDFLAGS="-Wl,-brtl"
+ build_type=shared
+ ;;
+ *darwin*)
+ MH_BUNDLE_FLAGS="-dynamic -twolevel_namespace -bundle -bundle_loader $APXS_HTTPD"
+ PHP_SUBST(MH_BUNDLE_FLAGS)
+ SAPI_SHARED=libs/libphp5.so
+ build_type=bundle
+ ;;
+ *)
+ build_type=shared
+ ;;
+ esac
+
+ PHP_SELECT_SAPI(apache, $build_type, sapi_apache.c mod_php5.c php_apache.c, $APACHE_CPPFLAGS -I$APXS_INCLUDEDIR)
+
+ # Test whether apxs support -S option
+ $APXS -q -S CFLAGS="$APXS_CFLAGS" CFLAGS >/dev/null 2>&1
+
+ if test "$?" != "0"; then
+ APACHE_INSTALL="$APXS -i -a -n php5 $SAPI_SHARED" # Old apxs does not have -S option
+ else
+ APXS_LIBEXECDIR='$(INSTALL_ROOT)'`$APXS -q LIBEXECDIR`
+ if test -z `$APXS -q SYSCONFDIR`; then
+ APACHE_INSTALL="\$(mkinstalldirs) '$APXS_LIBEXECDIR' && \
+ $APXS -S LIBEXECDIR='$APXS_LIBEXECDIR' \
+ -i -n php5 $SAPI_SHARED"
+ else
+ APXS_SYSCONFDIR='$(INSTALL_ROOT)'`$APXS -q SYSCONFDIR`
+ APACHE_INSTALL="\$(mkinstalldirs) '$APXS_LIBEXECDIR' && \
+ \$(mkinstalldirs) '$APXS_SYSCONFDIR' && \
+ $APXS -S LIBEXECDIR='$APXS_LIBEXECDIR' \
+ -S SYSCONFDIR='$APXS_SYSCONFDIR' \
+ -i -a -n php5 $SAPI_SHARED"
+ fi
+ fi
+
+ if test -z "`$APXS -q LD_SHLIB`" || test "`$APXS -q LIBEXECDIR`" = "modules"; then
+ PHP_APXS_BROKEN=yes
+ fi
+ STRONGHOLD=
+ AC_DEFINE(HAVE_AP_CONFIG_H,1,[ ])
+ AC_DEFINE(HAVE_AP_COMPAT_H,1,[ ])
+ AC_DEFINE(HAVE_APACHE,1,[ ])
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+
+dnl Apache 1.x static module
+PHP_ARG_WITH(apache,,
+[ --with-apache[=DIR] Build Apache 1.x module. DIR is the top-level Apache
+ build directory [/usr/local/apache]], no, no)
+
+AC_MSG_CHECKING([for Apache 1.x module support])
+
+if test "$PHP_SAPI" != "apache" && test "$PHP_APACHE" != "no"; then
+
+ if test "$PHP_APACHE" = "yes"; then
+ # Apache's default directory
+ PHP_APACHE=/usr/local/apache
+ fi
+
+ APACHE_INSTALL_FILES="\$(srcdir)/sapi/apache/mod_php5.* sapi/apache/libphp5.module"
+
+ AC_DEFINE(HAVE_APACHE,1,[ ])
+ APACHE_MODULE=yes
+ PHP_EXPAND_PATH($PHP_APACHE, PHP_APACHE)
+ # For Apache 1.2.x
+ if test -f $PHP_APACHE/src/httpd.h; then
+ APACHE_INCLUDE=-I$PHP_APACHE/src
+ APACHE_TARGET=$PHP_APACHE/src
+ PHP_SELECT_SAPI(apache, static, sapi_apache.c mod_php5.c php_apache.c, $APACHE_INCLUDE)
+ APACHE_INSTALL="mkdir -p $APACHE_TARGET; cp $SAPI_STATIC $APACHE_INSTALL_FILES $APACHE_TARGET"
+ PHP_LIBS="-L. -lphp3"
+ AC_MSG_RESULT([yes - Apache 1.2.x])
+ STRONGHOLD=
+ if test -f $PHP_APACHE/src/ap_config.h; then
+ AC_DEFINE(HAVE_AP_CONFIG_H,1,[ ])
+ fi
+ # For Apache 2.0.x
+ elif test -f $PHP_APACHE/include/httpd.h && test -f $PHP_APACHE/srclib/apr/include/apr_general.h ; then
+ AC_MSG_ERROR([Use --with-apxs2 with Apache 2.x!])
+ # For Apache 1.3.x
+ elif test -f $PHP_APACHE/src/main/httpd.h; then
+ APACHE_HAS_REGEX=1
+ APACHE_INCLUDE="-I$PHP_APACHE/src/main -I$PHP_APACHE/src/os/unix -I$PHP_APACHE/src/ap"
+ APACHE_TARGET=$PHP_APACHE/src/modules/php5
+ if test ! -d $APACHE_TARGET; then
+ mkdir $APACHE_TARGET
+ fi
+ PHP_SELECT_SAPI(apache, static, sapi_apache.c mod_php5.c php_apache.c, $APACHE_INCLUDE)
+ APACHE_INSTALL="mkdir -p $APACHE_TARGET; cp $SAPI_STATIC $APACHE_TARGET/libmodphp5.a; cp $APACHE_INSTALL_FILES $APACHE_TARGET; cp $srcdir/sapi/apache/apMakefile.tmpl $APACHE_TARGET/Makefile.tmpl; cp $srcdir/sapi/apache/apMakefile.libdir $APACHE_TARGET/Makefile.libdir"
+ PHP_LIBS="-Lmodules/php5 -L../modules/php5 -L../../modules/php5 -lmodphp5"
+ AC_MSG_RESULT([yes - Apache 1.3.x])
+ STRONGHOLD=
+ if test -f $PHP_APACHE/src/include/ap_config.h; then
+ AC_DEFINE(HAVE_AP_CONFIG_H, 1, [ ])
+ fi
+ if test -f $PHP_APACHE/src/include/ap_compat.h; then
+ AC_DEFINE(HAVE_AP_COMPAT_H, 1, [ ])
+ if test ! -f $PHP_APACHE/src/include/ap_config_auto.h; then
+ AC_MSG_ERROR([Please run Apache\'s configure or src/Configure program once and try again])
+ fi
+ elif test -f $PHP_APACHE/src/include/compat.h; then
+ AC_DEFINE(HAVE_OLD_COMPAT_H, 1, [ ])
+ fi
+ # Also for Apache 1.3.x
+ elif test -f $PHP_APACHE/src/include/httpd.h; then
+ APACHE_HAS_REGEX=1
+ APACHE_INCLUDE="-I$PHP_APACHE/src/include -I$PHP_APACHE/src/os/unix"
+ APACHE_TARGET=$PHP_APACHE/src/modules/php5
+ if test ! -d $APACHE_TARGET; then
+ mkdir $APACHE_TARGET
+ fi
+ PHP_SELECT_SAPI(apache, static, sapi_apache.c mod_php5.c php_apache.c, $APACHE_INCLUDE)
+ PHP_LIBS="-Lmodules/php5 -L../modules/php5 -L../../modules/php5 -lmodphp5"
+ APACHE_INSTALL="mkdir -p $APACHE_TARGET; cp $SAPI_STATIC $APACHE_TARGET/libmodphp5.a; cp $APACHE_INSTALL_FILES $APACHE_TARGET; cp $srcdir/sapi/apache/apMakefile.tmpl $APACHE_TARGET/Makefile.tmpl; cp $srcdir/sapi/apache/apMakefile.libdir $APACHE_TARGET/Makefile.libdir"
+ AC_MSG_RESULT([yes - Apache 1.3.x])
+ STRONGHOLD=
+ if test -f $PHP_APACHE/src/include/ap_config.h; then
+ AC_DEFINE(HAVE_AP_CONFIG_H, 1, [ ])
+ fi
+ if test -f $PHP_APACHE/src/include/ap_compat.h; then
+ AC_DEFINE(HAVE_AP_COMPAT_H, 1, [ ])
+ if test ! -f $PHP_APACHE/src/include/ap_config_auto.h; then
+ AC_MSG_ERROR([Please run Apache\'s configure or src/Configure program once and try again])
+ fi
+ elif test -f $PHP_APACHE/src/include/compat.h; then
+ AC_DEFINE(HAVE_OLD_COMPAT_H, 1, [ ])
+ fi
+ # For StrongHold 2.2
+ elif test -f $PHP_APACHE/apache/httpd.h; then
+ APACHE_INCLUDE="-I$PHP_APACHE/apache -I$PHP_APACHE/ssl/include"
+ APACHE_TARGET=$PHP_APACHE/apache
+ PHP_SELECT_SAPI(apache, static, sapi_apache.c mod_php5.c php_apache.c, $APACHE_INCLUDE)
+ PHP_LIBS="-Lmodules/php5 -L../modules/php5 -L../../modules/php5 -lmodphp5"
+ APACHE_INSTALL="mkdir -p $APACHE_TARGET; cp $SAPI_STATIC $APACHE_TARGET/libmodphp5.a; cp $APACHE_INSTALL_FILES $APACHE_TARGET"
+ STRONGHOLD=-DSTRONGHOLD=1
+ AC_MSG_RESULT([yes - StrongHold])
+ if test -f $PHP_APACHE/apache/ap_config.h; then
+ AC_DEFINE(HAVE_AP_CONFIG_H, 1, [ ])
+ fi
+ if test -f $PHP_APACHE/src/ap_compat.h; then
+ AC_DEFINE(HAVE_AP_COMPAT_H, 1, [ ])
+ if test ! -f $PHP_APACHE/src/include/ap_config_auto.h; then
+ AC_MSG_ERROR([Please run Apache\'s configure or src/Configure program once and try again])
+ fi
+ elif test -f $PHP_APACHE/src/compat.h; then
+ AC_DEFINE(HAVE_OLD_COMPAT_H, 1, [ ])
+ fi
+ else
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([Invalid Apache directory - unable to find httpd.h under $PHP_APACHE])
+ fi
+else
+ AC_MSG_RESULT(no)
+fi
+
+# compatibility
+if test -z "$enable_mod_charset" && test "$with_mod_charset"; then
+ enable_mod_charset=$with_mod_charset
+fi
+
+PHP_ARG_ENABLE(mod-charset, whether to enable Apache charset compatibility option,
+[ --enable-mod-charset APACHE: Enable transfer tables for mod_charset (Rus Apache)], no, no)
+
+if test "$PHP_MOD_CHARSET" = "yes"; then
+ AC_DEFINE(USE_TRANSFER_TABLES, 1, [ ])
+fi
+
+dnl Build as static module
+if test "$APACHE_MODULE" = "yes"; then
+ PHP_TARGET_RDYNAMIC
+ $php_shtool mkdir -p sapi/apache
+ PHP_OUTPUT(sapi/apache/libphp5.module)
+fi
+
+dnl General
+if test -n "$APACHE_INSTALL"; then
+ if test "x$APXS" != "x" -a "`uname -sv`" = "AIX 4" -a "$GCC" != "yes"; then
+ APXS_EXP=-bE:sapi/apache/mod_php5.exp
+ fi
+
+ PHP_APACHE_FD_CHECK
+ INSTALL_IT=$APACHE_INSTALL
+
+ PHP_SUBST(APXS_EXP)
+ PHP_SUBST(APACHE_INCLUDE)
+ PHP_SUBST(APACHE_TARGET)
+ PHP_SUBST(APXS)
+ PHP_SUBST(APXS_LDFLAGS)
+ PHP_SUBST(APACHE_INSTALL)
+ PHP_SUBST(STRONGHOLD)
+fi
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/apache/config.w32 b/sapi/apache/config.w32
new file mode 100644
index 0000000..e876d75
--- /dev/null
+++ b/sapi/apache/config.w32
@@ -0,0 +1,24 @@
+// vim:ft=javascript
+// $Id$
+
+ARG_ENABLE('apache', 'Build Apache 1.3.x version of PHP', 'no');
+
+ARG_WITH('apache-includes', 'Where to find Apache 1.3 headers', null);
+ARG_WITH('apache-libs', 'Where to find Apache 1.3 libraries', null);
+
+if (PHP_APACHE != "no") {
+ if (CHECK_HEADER_ADD_INCLUDE("httpd.h", "CFLAGS_APACHE", php_usual_include_suspects +
+ ";" + PROGRAM_FILES + "\\Apache Group\\Apache\\include" +
+ ";" + PHP_PHP_BUILD + "\\apache\\src\\include") &&
+ CHECK_LIB("ApacheCore.lib", "apache", php_usual_lib_suspects +
+ ';' + PROGRAM_FILES + '\\Apache Group\\Apache\\libexec' +
+ ";" + PHP_PHP_BUILD + "\\apache\\src\\corer")) {
+ // We need to play tricks to get our readdir.h used by apache
+ // headers
+ SAPI('apache', 'mod_php5.c sapi_apache.c php_apache.c',
+ 'php' + PHP_VERSION + 'apache.dll',
+ '/D APACHEPHP5_EXPORTS /D APACHE_READDIR_H /I win32');
+ } else {
+ WARNING("Could not find apache libraries/headers");
+ }
+}
diff --git a/sapi/apache/libphp5.module.in b/sapi/apache/libphp5.module.in
new file mode 100644
index 0000000..00d9c49
--- /dev/null
+++ b/sapi/apache/libphp5.module.in
@@ -0,0 +1,11 @@
+Name: php5_module
+ConfigStart
+ RULE_WANTHSREGEX=no
+ RULE_HIDE=yes
+ PHP_LIBS="@NATIVE_RPATHS@ @PHP_LDFLAGS@ @PHP_LIBS@ @EXTRA_LDFLAGS@ @EXTRA_LIBS@ $LIBS"
+ PHP_CFLAGS="$CFLAGS @OPENSSL_INCDIR_OPT@ -I@php_abs_top_builddir@/main -I@php_abs_top_builddir@/Zend -I@php_abs_top_builddir@/TSRM -I@php_abs_top_srcdir@ -I@php_abs_top_srcdir@/sapi/apache -I@php_abs_top_srcdir@/main -I@php_abs_top_srcdir@/Zend -I@php_abs_top_srcdir@/TSRM"
+ my_outfile="Makefile.config"
+ echo "PHP_CFLAGS=$PHP_CFLAGS" >>$my_outfile
+ echo "PHP_LIBS=$PHP_LIBS" >>$my_outfile
+ LIBS=$PHP_LIBS
+ConfigEnd
diff --git a/sapi/apache/libpre.c b/sapi/apache/libpre.c
new file mode 100644
index 0000000..35b47cf
--- /dev/null
+++ b/sapi/apache/libpre.c
@@ -0,0 +1,55 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifdef NETWARE
+
+/* ------------------------------------------------------------------
+ * These functions are to be called when the shared NLM starts and
+ * stops. By using these functions instead of defining a main()
+ * and calling ExitThread(TSR_THREAD, 0), the load time of the
+ * shared NLM is faster and memory size reduced.
+ *
+ * You may also want to override these in your own Apache module
+ * to do any cleanup other than the mechanism Apache modules provide.
+ * ------------------------------------------------------------------
+ */
+
+
+#ifdef __GNUC__
+#include <string.h> /* memset */
+extern char _edata, _end ; /* end of DATA (start of BSS), end of BSS */
+#endif
+
+int _lib_start()
+{
+/* printf("Inside _lib_start\n");*/
+#ifdef __GNUC__
+ memset (&_edata, 0, &_end - &_edata);
+#endif
+ return 0;
+}
+
+int _lib_stop()
+{
+/* printf("Inside _lib_stop\n");*/
+ return 0;
+}
+
+#endif /* NETWARE */
diff --git a/sapi/apache/mod_php5.c b/sapi/apache/mod_php5.c
new file mode 100644
index 0000000..11be0ed
--- /dev/null
+++ b/sapi/apache/mod_php5.c
@@ -0,0 +1,1040 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@php.net> |
+ | (with helpful hints from Dean Gaudet <dgaudet@arctic.org> |
+ | PHP 4.0 patches by Zeev Suraski <zeev@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#include "php_apache_http.h"
+#include "http_conf_globals.h"
+
+#ifdef NETWARE
+#define SIGPIPE SIGINT
+#endif
+
+#undef shutdown
+
+/* {{{ Prototypes
+ */
+int apache_php_module_main(request_rec *r, int display_source_mode TSRMLS_DC);
+static void php_save_umask(void);
+static void php_restore_umask(void);
+static int sapi_apache_read_post(char *buffer, uint count_bytes TSRMLS_DC);
+static char *sapi_apache_read_cookies(TSRMLS_D);
+static int sapi_apache_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC);
+static int sapi_apache_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC);
+static int send_php(request_rec *r, int display_source_mode, char *filename);
+static int send_parsed_php(request_rec * r);
+static int send_parsed_php_source(request_rec * r);
+static int php_xbithack_handler(request_rec * r);
+static void php_init_handler(server_rec *s, pool *p);
+/* }}} */
+
+#if MODULE_MAGIC_NUMBER >= 19970728
+static void php_child_exit_handler(server_rec *s, pool *p);
+#endif
+
+#if MODULE_MAGIC_NUMBER > 19961007
+#define CONST_PREFIX const
+#else
+#define CONST_PREFIX
+#endif
+static CONST_PREFIX char *php_apache_value_handler_ex(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2, int mode);
+static CONST_PREFIX char *php_apache_value_handler(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2);
+static CONST_PREFIX char *php_apache_admin_value_handler(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2);
+static CONST_PREFIX char *php_apache_flag_handler(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2);
+static CONST_PREFIX char *php_apache_flag_handler_ex(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2, int mode);
+static CONST_PREFIX char *php_apache_admin_flag_handler(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2);
+
+/* ### these should be defined in mod_php5.h or somewhere else */
+#define USE_PATH 1
+#define IGNORE_URL 2
+#define MAX_STATUS_LENGTH sizeof("xxxx LONGEST POSSIBLE STATUS DESCRIPTION")
+
+module MODULE_VAR_EXPORT php5_module;
+
+int saved_umask;
+static unsigned char apache_php_initialized;
+
+typedef struct _php_per_dir_entry {
+ char *key;
+ char *value;
+ uint key_length;
+ uint value_length;
+ int type;
+ char htaccess;
+} php_per_dir_entry;
+
+/* some systems are missing these from their header files */
+
+/* {{{ php_save_umask
+ */
+static void php_save_umask(void)
+{
+ saved_umask = umask(077);
+ umask(saved_umask);
+}
+/* }}} */
+
+/* {{{ sapi_apache_ub_write
+ */
+static int sapi_apache_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ int ret=0;
+
+ if (SG(server_context)) {
+ ret = rwrite(str, str_length, (request_rec *) SG(server_context));
+ }
+ if (ret != str_length) {
+ php_handle_aborted_connection();
+ }
+ return ret;
+}
+/* }}} */
+
+/* {{{ sapi_apache_flush
+ */
+static void sapi_apache_flush(void *server_context)
+{
+ if (server_context) {
+#if MODULE_MAGIC_NUMBER > 19970110
+ rflush((request_rec *) server_context);
+#else
+ bflush((request_rec *) server_context->connection->client);
+#endif
+ }
+}
+/* }}} */
+
+/* {{{ sapi_apache_read_post
+ */
+static int sapi_apache_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ int total_read_bytes=0, read_bytes;
+ request_rec *r = (request_rec *) SG(server_context);
+ void (*handler)(int);
+
+ /*
+ * This handles the situation where the browser sends a Expect: 100-continue header
+ * and needs to recieve confirmation from the server on whether or not it can send
+ * the rest of the request. RFC 2616
+ *
+ */
+ if (!SG(read_post_bytes) && !ap_should_client_block(r)) {
+ return total_read_bytes;
+ }
+
+ handler = signal(SIGPIPE, SIG_IGN);
+ while (total_read_bytes<count_bytes) {
+ hard_timeout("Read POST information", r); /* start timeout timer */
+ read_bytes = get_client_block(r, buffer+total_read_bytes, count_bytes-total_read_bytes);
+ reset_timeout(r);
+ if (read_bytes<=0) {
+ break;
+ }
+ total_read_bytes += read_bytes;
+ }
+ signal(SIGPIPE, handler);
+ return total_read_bytes;
+}
+/* }}} */
+
+/* {{{ sapi_apache_read_cookies
+ */
+static char *sapi_apache_read_cookies(TSRMLS_D)
+{
+ return (char *) table_get(((request_rec *) SG(server_context))->subprocess_env, "HTTP_COOKIE");
+}
+/* }}} */
+
+/* {{{ sapi_apache_header_handler
+ */
+static int sapi_apache_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ char *header_name, *header_content, *p;
+ request_rec *r = (request_rec *) SG(server_context);
+ if(!r) {
+ return 0;
+ }
+
+ switch(op) {
+ case SAPI_HEADER_DELETE_ALL:
+ clear_table(r->headers_out);
+ return 0;
+
+ case SAPI_HEADER_DELETE:
+ table_unset(r->headers_out, sapi_header->header);
+ return 0;
+
+ case SAPI_HEADER_ADD:
+ case SAPI_HEADER_REPLACE:
+ header_name = sapi_header->header;
+
+ header_content = p = strchr(header_name, ':');
+ if (!p) {
+ return 0;
+ }
+
+ *p = 0;
+ do {
+ header_content++;
+ } while (*header_content==' ');
+
+ if (!strcasecmp(header_name, "Content-Type")) {
+ r->content_type = pstrdup(r->pool, header_content);
+ } else if (!strcasecmp(header_name, "Content-Length")) {
+ ap_set_content_length(r, strtol(header_content, (char **)NULL, 10));
+ } else if (!strcasecmp(header_name, "Set-Cookie")) {
+ table_add(r->headers_out, header_name, header_content);
+ } else if (op == SAPI_HEADER_REPLACE) {
+ table_set(r->headers_out, header_name, header_content);
+ } else {
+ table_add(r->headers_out, header_name, header_content);
+ }
+
+ *p = ':'; /* a well behaved header handler shouldn't change its original arguments */
+
+ return SAPI_HEADER_ADD;
+
+ default:
+ return 0;
+ }
+}
+/* }}} */
+
+/* {{{ sapi_apache_send_headers
+ */
+static int sapi_apache_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ request_rec *r = SG(server_context);
+ const char *sline = SG(sapi_headers).http_status_line;
+ int sline_len;
+
+ if(r == NULL) { /* server_context is not here anymore */
+ return SAPI_HEADER_SEND_FAILED;
+ }
+
+ r->status = SG(sapi_headers).http_response_code;
+
+ /* httpd requires that r->status_line is set to the first digit of
+ * the status-code: */
+ if (sline && ((sline_len = strlen(sline)) > 12) && strncmp(sline, "HTTP/1.", 7) == 0 && sline[8] == ' ' && sline[12] == ' ') {
+ if ((sline_len - 9) > MAX_STATUS_LENGTH) {
+ r->status_line = ap_pstrndup(r->pool, sline + 9, MAX_STATUS_LENGTH);
+ } else {
+ r->status_line = ap_pstrndup(r->pool, sline + 9, sline_len - 9);
+ }
+ }
+
+ if(r->status==304) {
+ send_error_response(r,0);
+ } else {
+ send_http_header(r);
+ }
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+/* }}} */
+
+/* {{{ sapi_apache_register_server_variables
+ */
+static void sapi_apache_register_server_variables(zval *track_vars_array TSRMLS_DC)
+{
+ register int i;
+ array_header *arr = table_elts(((request_rec *) SG(server_context))->subprocess_env);
+ table_entry *elts = (table_entry *) arr->elts;
+ zval **path_translated;
+ HashTable *symbol_table;
+ unsigned int new_val_len;
+
+ for (i = 0; i < arr->nelts; i++) {
+ char *val;
+ int val_len;
+
+ if (elts[i].val) {
+ val = elts[i].val;
+ } else {
+ val = "";
+ }
+ val_len = strlen(val);
+ if (sapi_module.input_filter(PARSE_SERVER, elts[i].key, &val, val_len, &new_val_len TSRMLS_CC)) {
+ php_register_variable_safe(elts[i].key, val, new_val_len, track_vars_array TSRMLS_CC);
+ }
+ }
+
+ /* If PATH_TRANSLATED doesn't exist, copy it from SCRIPT_FILENAME */
+ if (track_vars_array) {
+ symbol_table = track_vars_array->value.ht;
+ } else {
+ symbol_table = NULL;
+ }
+ if (symbol_table
+ && !zend_hash_exists(symbol_table, "PATH_TRANSLATED", sizeof("PATH_TRANSLATED"))
+ && zend_hash_find(symbol_table, "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME"), (void **) &path_translated)==SUCCESS) {
+ php_register_variable("PATH_TRANSLATED", Z_STRVAL_PP(path_translated), track_vars_array TSRMLS_CC);
+ }
+
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &((request_rec *) SG(server_context))->uri, strlen(((request_rec *) SG(server_context))->uri), &new_val_len TSRMLS_CC)) {
+ php_register_variable("PHP_SELF", ((request_rec *) SG(server_context))->uri, track_vars_array TSRMLS_CC);
+ }
+}
+/* }}} */
+
+/* {{{ php_apache_startup
+ */
+static int php_apache_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &apache_module_entry, 1) == FAILURE) {
+ return FAILURE;
+ } else {
+ return SUCCESS;
+ }
+}
+/* }}} */
+
+/* {{{ php_apache_log_message
+ */
+static void php_apache_log_message(char *message TSRMLS_DC)
+{
+ if (SG(server_context)) {
+#if MODULE_MAGIC_NUMBER >= 19970831
+ aplog_error(NULL, 0, APLOG_ERR | APLOG_NOERRNO, ((request_rec *) SG(server_context))->server, "%s", message);
+#else
+ log_error(message, ((request_rec *) SG(server_context))->server);
+#endif
+ } else {
+ fprintf(stderr, "%s\n", message);
+ }
+}
+/* }}} */
+
+/* {{{ php_apache_request_shutdown
+ */
+static void php_apache_request_shutdown(void *dummy)
+{
+ TSRMLS_FETCH();
+
+ php_output_set_status(PHP_OUTPUT_DISABLED TSRMLS_CC);
+ if (AP(in_request)) {
+ AP(in_request) = 0;
+ php_request_shutdown(dummy);
+ }
+ SG(server_context) = NULL;
+ /*
+ * The server context (request) is NOT invalid by the time
+ * run_cleanups() is called
+ */
+}
+/* }}} */
+
+/* {{{ php_apache_sapi_activate
+ */
+static int php_apache_sapi_activate(TSRMLS_D)
+{
+ request_rec *r = (request_rec *) SG(server_context);
+
+ /*
+ * For the Apache module version, this bit of code registers a cleanup
+ * function that gets triggered when our request pool is destroyed.
+ * We need this because at any point in our code we can be interrupted
+ * and that may happen before we have had time to free our memory.
+ * The php_request_shutdown function needs to free all outstanding allocated
+ * memory.
+ */
+ block_alarms();
+ register_cleanup(r->pool, NULL, php_apache_request_shutdown, php_request_shutdown_for_exec);
+ AP(in_request)=1;
+ unblock_alarms();
+
+ /* Override the default headers_only value - sometimes "GET" requests should actually only
+ * send headers.
+ */
+ SG(request_info).headers_only = r->header_only;
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ php_apache_get_stat
+ */
+static struct stat *php_apache_get_stat(TSRMLS_D)
+{
+ return &((request_rec *) SG(server_context))->finfo;
+}
+/* }}} */
+
+/* {{{ php_apache_getenv
+ */
+static char *php_apache_getenv(char *name, size_t name_len TSRMLS_DC)
+{
+ if (SG(server_context) == NULL) {
+ return NULL;
+ }
+
+ return (char *) table_get(((request_rec *) SG(server_context))->subprocess_env, name);
+}
+/* }}} */
+
+/* {{{ sapi_apache_get_fd
+ */
+static int sapi_apache_get_fd(int *nfd TSRMLS_DC)
+{
+#if PHP_APACHE_HAVE_CLIENT_FD
+ request_rec *r = SG(server_context);
+ int fd;
+
+ fd = r->connection->client->fd;
+
+ if (fd >= 0) {
+ if (nfd) *nfd = fd;
+ return SUCCESS;
+ }
+#endif
+ return FAILURE;
+}
+/* }}} */
+
+/* {{{ sapi_apache_force_http_10
+ */
+static int sapi_apache_force_http_10(TSRMLS_D)
+{
+ request_rec *r = SG(server_context);
+
+ r->proto_num = HTTP_VERSION(1,0);
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ sapi_apache_get_target_uid
+ */
+static int sapi_apache_get_target_uid(uid_t *obj TSRMLS_DC)
+{
+ *obj = ap_user_id;
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ sapi_apache_get_target_gid
+ */
+static int sapi_apache_get_target_gid(gid_t *obj TSRMLS_DC)
+{
+ *obj = ap_group_id;
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ php_apache_get_request_time
+ */
+static double php_apache_get_request_time(TSRMLS_D)
+{
+ return (double) ((request_rec *)SG(server_context))->request_time;
+}
+/* }}} */
+
+/* {{{ sapi_apache_child_terminate
+ */
+static void sapi_apache_child_terminate(TSRMLS_D)
+{
+#ifndef MULTITHREAD
+ ap_child_terminate((request_rec *)SG(server_context));
+#endif
+}
+/* }}} */
+
+/* {{{ sapi_module_struct apache_sapi_module
+ */
+static sapi_module_struct apache_sapi_module = {
+ "apache", /* name */
+ "Apache", /* pretty name */
+
+ php_apache_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ php_apache_sapi_activate, /* activate */
+ NULL, /* deactivate */
+
+ sapi_apache_ub_write, /* unbuffered write */
+ sapi_apache_flush, /* flush */
+ php_apache_get_stat, /* get uid */
+ php_apache_getenv, /* getenv */
+
+ php_error, /* error handler */
+
+ sapi_apache_header_handler, /* header handler */
+ sapi_apache_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ sapi_apache_read_post, /* read POST data */
+ sapi_apache_read_cookies, /* read Cookies */
+
+ sapi_apache_register_server_variables, /* register server variables */
+ php_apache_log_message, /* Log message */
+ php_apache_get_request_time, /* Get request time */
+ sapi_apache_child_terminate,
+
+ NULL, /* php.ini path override */
+
+#ifdef PHP_WIN32
+ NULL,
+ NULL,
+#else
+ block_alarms, /* Block interruptions */
+ unblock_alarms, /* Unblock interruptions */
+#endif
+
+ NULL, /* default post reader */
+ NULL, /* treat data */
+ NULL, /* exe location */
+ 0, /* ini ignore */
+ 0, /* ini ignore cwd */
+ sapi_apache_get_fd,
+ sapi_apache_force_http_10,
+ sapi_apache_get_target_uid,
+ sapi_apache_get_target_gid
+};
+/* }}} */
+
+/* {{{ php_restore_umask
+ */
+static void php_restore_umask(void)
+{
+ umask(saved_umask);
+}
+/* }}} */
+
+/* {{{ init_request_info
+ */
+static void init_request_info(TSRMLS_D)
+{
+ request_rec *r = ((request_rec *) SG(server_context));
+ char *content_length = (char *) table_get(r->subprocess_env, "CONTENT_LENGTH");
+ const char *authorization=NULL;
+ char *tmp, *tmp_user;
+
+ SG(request_info).query_string = r->args;
+ SG(request_info).path_translated = r->filename;
+ SG(request_info).request_uri = r->uri;
+ SG(request_info).request_method = (char *)r->method;
+ SG(request_info).content_type = (char *) table_get(r->subprocess_env, "CONTENT_TYPE");
+ SG(request_info).content_length = (content_length ? atol(content_length) : 0);
+ SG(sapi_headers).http_response_code = r->status;
+ SG(request_info).proto_num = r->proto_num;
+
+ if (r->headers_in) {
+ authorization = table_get(r->headers_in, "Authorization");
+ }
+
+ SG(request_info).auth_user = NULL;
+ SG(request_info).auth_password = NULL;
+ SG(request_info).auth_digest = NULL;
+
+ if (authorization) {
+ char *p = getword(r->pool, &authorization, ' ');
+ if (!strcasecmp(p, "Basic")) {
+ tmp = uudecode(r->pool, authorization);
+ tmp_user = getword_nulls_nc(r->pool, &tmp, ':');
+ if (tmp_user) {
+ r->connection->user = pstrdup(r->connection->pool, tmp_user);
+ r->connection->ap_auth_type = "Basic";
+ SG(request_info).auth_user = estrdup(tmp_user);
+ }
+ if (tmp) {
+ SG(request_info).auth_password = estrdup(tmp);
+ }
+ } else if (!strcasecmp(p, "Digest")) {
+ r->connection->ap_auth_type = "Digest";
+ SG(request_info).auth_digest = estrdup(authorization);
+ }
+ }
+}
+/* }}} */
+
+/* {{{ php_apache_alter_ini_entries
+ */
+static int php_apache_alter_ini_entries(php_per_dir_entry *per_dir_entry TSRMLS_DC)
+{
+ zend_alter_ini_entry(per_dir_entry->key, per_dir_entry->key_length+1, per_dir_entry->value, per_dir_entry->value_length, per_dir_entry->type, per_dir_entry->htaccess?PHP_INI_STAGE_HTACCESS:PHP_INI_STAGE_ACTIVATE);
+ return 0;
+}
+/* }}} */
+
+/* {{{ php_apache_get_default_mimetype
+ */
+static char *php_apache_get_default_mimetype(request_rec *r TSRMLS_DC)
+{
+
+ char *mimetype;
+ if (SG(default_mimetype) || SG(default_charset)) {
+ /* Assume output will be of the default MIME type. Individual
+ scripts may change this later. */
+ char *tmpmimetype;
+ tmpmimetype = sapi_get_default_content_type(TSRMLS_C);
+ mimetype = pstrdup(r->pool, tmpmimetype);
+ efree(tmpmimetype);
+ } else {
+ mimetype = SAPI_DEFAULT_MIMETYPE "; charset=" SAPI_DEFAULT_CHARSET;
+ }
+ return mimetype;
+}
+/* }}} */
+
+/* {{{ send_php
+ */
+static int send_php(request_rec *r, int display_source_mode, char *filename)
+{
+ int retval;
+ HashTable *per_dir_conf;
+ TSRMLS_FETCH();
+
+ if (AP(in_request)) {
+ zend_file_handle fh;
+
+ fh.filename = r->filename;
+ fh.opened_path = NULL;
+ fh.free_filename = 0;
+ fh.type = ZEND_HANDLE_FILENAME;
+
+ zend_execute_scripts(ZEND_INCLUDE TSRMLS_CC, NULL, 1, &fh);
+ return OK;
+ }
+
+ SG(server_context) = r;
+
+ zend_first_try {
+
+ /* Make sure file exists */
+ if (filename == NULL && r->finfo.st_mode == 0) {
+ return DECLINED;
+ }
+
+ per_dir_conf = (HashTable *) get_module_config(r->per_dir_config, &php5_module);
+ if (per_dir_conf) {
+ zend_hash_apply((HashTable *) per_dir_conf, (apply_func_t) php_apache_alter_ini_entries TSRMLS_CC);
+ }
+
+ /* If PHP parser engine has been turned off with an "engine off"
+ * directive, then decline to handle this request
+ */
+ if (!AP(engine)) {
+ r->content_type = php_apache_get_default_mimetype(r TSRMLS_CC);
+ zend_try {
+ zend_ini_deactivate(TSRMLS_C);
+ } zend_end_try();
+ return DECLINED;
+ }
+ if (filename == NULL) {
+ filename = r->filename;
+ }
+
+ /* Apache 1.2 has a more complex mechanism for reading POST data */
+#if MODULE_MAGIC_NUMBER > 19961007
+ if ((retval = setup_client_block(r, REQUEST_CHUNKED_DECHUNK))) {
+ zend_try {
+ zend_ini_deactivate(TSRMLS_C);
+ } zend_end_try();
+ return retval;
+ }
+#endif
+
+ if (AP(last_modified)) {
+#if MODULE_MAGIC_NUMBER < 19970912
+ if ((retval = set_last_modified(r, r->finfo.st_mtime))) {
+ zend_try {
+ zend_ini_deactivate(TSRMLS_C);
+ } zend_end_try();
+ return retval;
+ }
+#else
+ update_mtime (r, r->finfo.st_mtime);
+ set_last_modified(r);
+ set_etag(r);
+#endif
+ }
+ /* Assume output will be of the default MIME type. Individual
+ scripts may change this later in the request. */
+ r->content_type = php_apache_get_default_mimetype(r TSRMLS_CC);
+
+ /* Init timeout */
+ hard_timeout("send", r);
+
+ php_save_umask();
+ add_common_vars(r);
+ add_cgi_vars(r);
+
+ init_request_info(TSRMLS_C);
+ apache_php_module_main(r, display_source_mode TSRMLS_CC);
+
+ /* Done, restore umask, turn off timeout, close file and return */
+ php_restore_umask();
+ kill_timeout(r);
+ } zend_end_try();
+
+ return OK;
+}
+/* }}} */
+
+/* {{{ send_parsed_php
+ */
+static int send_parsed_php(request_rec * r)
+{
+ int result = send_php(r, 0, NULL);
+ TSRMLS_FETCH();
+
+ ap_table_setn(r->notes, "mod_php_memory_usage",
+ ap_psprintf(r->pool, "%lu", zend_memory_peak_usage(1 TSRMLS_CC)));
+
+ return result;
+}
+/* }}} */
+
+/* {{{ send_parsed_php_source
+ */
+static int send_parsed_php_source(request_rec * r)
+{
+ return send_php(r, 1, NULL);
+}
+/* }}} */
+
+/* {{{ destroy_per_dir_entry
+ */
+static void destroy_per_dir_entry(php_per_dir_entry *per_dir_entry)
+{
+ free(per_dir_entry->key);
+ free(per_dir_entry->value);
+}
+/* }}} */
+
+/* {{{ copy_per_dir_entry
+ */
+static void copy_per_dir_entry(php_per_dir_entry *per_dir_entry)
+{
+ php_per_dir_entry tmp = *per_dir_entry;
+
+ per_dir_entry->key = (char *) malloc(tmp.key_length+1);
+ memcpy(per_dir_entry->key, tmp.key, tmp.key_length);
+ per_dir_entry->key[per_dir_entry->key_length] = 0;
+
+ per_dir_entry->value = (char *) malloc(tmp.value_length+1);
+ memcpy(per_dir_entry->value, tmp.value, tmp.value_length);
+ per_dir_entry->value[per_dir_entry->value_length] = 0;
+}
+/* }}} */
+
+/* {{{ should_overwrite_per_dir_entry
+ */
+static zend_bool should_overwrite_per_dir_entry(HashTable *target_ht, php_per_dir_entry *new_per_dir_entry, zend_hash_key *hash_key, void *pData)
+{
+ php_per_dir_entry *orig_per_dir_entry;
+
+ if (zend_hash_find(target_ht, hash_key->arKey, hash_key->nKeyLength, (void **) &orig_per_dir_entry)==FAILURE) {
+ return 1; /* does not exist in dest, copy from source */
+ }
+
+ if (orig_per_dir_entry->type==PHP_INI_SYSTEM
+ && new_per_dir_entry->type!=PHP_INI_SYSTEM) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+/* }}} */
+
+/* {{{ php_destroy_per_dir_info
+ */
+static void php_destroy_per_dir_info(HashTable *per_dir_info)
+{
+ zend_hash_destroy(per_dir_info);
+ free(per_dir_info);
+}
+/* }}} */
+
+/* {{{ php_create_dir
+ */
+static void *php_create_dir(pool *p, char *dummy)
+{
+ HashTable *per_dir_info;
+
+ per_dir_info = (HashTable *) malloc(sizeof(HashTable));
+ zend_hash_init_ex(per_dir_info, 5, NULL, (void (*)(void *)) destroy_per_dir_entry, 1, 0);
+ register_cleanup(p, (void *) per_dir_info, (void (*)(void *)) php_destroy_per_dir_info, (void (*)(void *)) zend_hash_destroy);
+
+ return per_dir_info;
+}
+/* }}} */
+
+/* {{{ php_merge_dir
+ */
+static void *php_merge_dir(pool *p, void *basev, void *addv)
+{
+ /* This function *must* not modify addv or basev */
+ HashTable *new;
+
+ /* need a copy of addv to merge */
+ new = php_create_dir(p, "php_merge_dir");
+ zend_hash_copy(new, (HashTable *) basev, (copy_ctor_func_t) copy_per_dir_entry, NULL, sizeof(php_per_dir_entry));
+
+ zend_hash_merge_ex(new, (HashTable *) addv, (copy_ctor_func_t) copy_per_dir_entry, sizeof(php_per_dir_entry), (merge_checker_func_t) should_overwrite_per_dir_entry, NULL);
+ return new;
+}
+/* }}} */
+
+/* {{{ php_apache_value_handler_ex
+ */
+static CONST_PREFIX char *php_apache_value_handler_ex(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2, int mode)
+{
+ php_per_dir_entry per_dir_entry;
+
+ if (!apache_php_initialized) {
+ apache_php_initialized = 1;
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+#endif
+ sapi_startup(&apache_sapi_module);
+ php_apache_startup(&apache_sapi_module);
+ }
+ per_dir_entry.type = mode;
+ per_dir_entry.htaccess = ((cmd->override & (RSRC_CONF|ACCESS_CONF)) == 0);
+
+ if (strcasecmp(arg2, "none") == 0) {
+ arg2 = "";
+ }
+
+ per_dir_entry.key_length = strlen(arg1);
+ per_dir_entry.value_length = strlen(arg2);
+
+ per_dir_entry.key = (char *) malloc(per_dir_entry.key_length+1);
+ memcpy(per_dir_entry.key, arg1, per_dir_entry.key_length);
+ per_dir_entry.key[per_dir_entry.key_length] = 0;
+
+ per_dir_entry.value = (char *) malloc(per_dir_entry.value_length+1);
+ memcpy(per_dir_entry.value, arg2, per_dir_entry.value_length);
+ per_dir_entry.value[per_dir_entry.value_length] = 0;
+
+ zend_hash_update(conf, per_dir_entry.key, per_dir_entry.key_length, &per_dir_entry, sizeof(php_per_dir_entry), NULL);
+ return NULL;
+}
+/* }}} */
+
+/* {{{ php_apache_value_handler
+ */
+static CONST_PREFIX char *php_apache_value_handler(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2)
+{
+ return php_apache_value_handler_ex(cmd, conf, arg1, arg2, PHP_INI_PERDIR);
+}
+/* }}} */
+
+/* {{{ php_apache_admin_value_handler
+ */
+static CONST_PREFIX char *php_apache_admin_value_handler(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2)
+{
+ return php_apache_value_handler_ex(cmd, conf, arg1, arg2, PHP_INI_SYSTEM);
+}
+/* }}} */
+
+/* {{{ php_apache_flag_handler_ex
+ */
+static CONST_PREFIX char *php_apache_flag_handler_ex(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2, int mode)
+{
+ char bool_val[2];
+
+ if (!strcasecmp(arg2, "On") || (arg2[0] == '1' && arg2[1] == '\0')) {
+ bool_val[0] = '1';
+ } else {
+ bool_val[0] = '0';
+ }
+ bool_val[1] = 0;
+
+ return php_apache_value_handler_ex(cmd, conf, arg1, bool_val, mode);
+}
+/* }}} */
+
+/* {{{ php_apache_flag_handler
+ */
+static CONST_PREFIX char *php_apache_flag_handler(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2)
+{
+ return php_apache_flag_handler_ex(cmd, conf, arg1, arg2, PHP_INI_PERDIR);
+}
+/* }}} */
+
+/* {{{ php_apache_admin_flag_handler
+ */
+static CONST_PREFIX char *php_apache_admin_flag_handler(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2)
+{
+ return php_apache_flag_handler_ex(cmd, conf, arg1, arg2, PHP_INI_SYSTEM);
+}
+/* }}} */
+
+/* {{{ php_apache_phpini_set
+ */
+static CONST_PREFIX char *php_apache_phpini_set(cmd_parms *cmd, HashTable *conf, char *arg)
+{
+ if (apache_sapi_module.php_ini_path_override) {
+ return "Only first PHPINIDir directive honored per configuration tree - subsequent ones ignored";
+ }
+ apache_sapi_module.php_ini_path_override = ap_server_root_relative(cmd->pool, arg);
+ return NULL;
+}
+/* }}} */
+
+/* {{{ int php_xbithack_handler(request_rec * r)
+ */
+static int php_xbithack_handler(request_rec * r)
+{
+ HashTable *per_dir_conf;
+ TSRMLS_FETCH();
+
+ if (!(r->finfo.st_mode & S_IXUSR)) {
+ return DECLINED;
+ }
+ per_dir_conf = (HashTable *) get_module_config(r->per_dir_config, &php5_module);
+ if (per_dir_conf) {
+ zend_hash_apply((HashTable *) per_dir_conf, (apply_func_t) php_apache_alter_ini_entries TSRMLS_CC);
+ }
+ if(!AP(xbithack)) {
+ zend_try {
+ zend_ini_deactivate(TSRMLS_C);
+ } zend_end_try();
+ return DECLINED;
+ }
+ return send_parsed_php(r);
+}
+/* }}} */
+
+/* {{{ apache_php_module_shutdown_wrapper
+ */
+static void apache_php_module_shutdown_wrapper(void)
+{
+ apache_php_initialized = 0;
+ apache_sapi_module.shutdown(&apache_sapi_module);
+
+#if MODULE_MAGIC_NUMBER >= 19970728
+ /* This function is only called on server exit if the apache API
+ * child_exit handler exists, so shutdown globally
+ */
+ sapi_shutdown();
+#endif
+
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+}
+/* }}} */
+
+#if MODULE_MAGIC_NUMBER >= 19970728
+/* {{{ php_child_exit_handler
+ */
+static void php_child_exit_handler(server_rec *s, pool *p)
+{
+/* apache_php_initialized = 0; */
+ apache_sapi_module.shutdown(&apache_sapi_module);
+
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+}
+/* }}} */
+#endif
+
+/* {{{ void php_init_handler(server_rec *s, pool *p)
+ */
+static void php_init_handler(server_rec *s, pool *p)
+{
+ register_cleanup(p, NULL, (void (*)(void *))apache_php_module_shutdown_wrapper, (void (*)(void *))php_module_shutdown_for_exec);
+ if (!apache_php_initialized) {
+ apache_php_initialized = 1;
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+#endif
+ sapi_startup(&apache_sapi_module);
+ php_apache_startup(&apache_sapi_module);
+ }
+#if MODULE_MAGIC_NUMBER >= 19980527
+ {
+ TSRMLS_FETCH();
+ if (PG(expose_php)) {
+ ap_add_version_component("PHP/" PHP_VERSION);
+ }
+ }
+#endif
+}
+/* }}} */
+
+/* {{{ handler_rec php_handlers[]
+ */
+handler_rec php_handlers[] =
+{
+ {"application/x-httpd-php", send_parsed_php},
+ {"application/x-httpd-php-source", send_parsed_php_source},
+ {"text/html", php_xbithack_handler},
+ {NULL}
+};
+/* }}} */
+
+/* {{{ command_rec php_commands[]
+ */
+command_rec php_commands[] =
+{
+ {"php_value", php_apache_value_handler, NULL, OR_OPTIONS, TAKE2, "PHP Value Modifier"},
+ {"php_flag", php_apache_flag_handler, NULL, OR_OPTIONS, TAKE2, "PHP Flag Modifier"},
+ {"php_admin_value", php_apache_admin_value_handler, NULL, ACCESS_CONF|RSRC_CONF, TAKE2, "PHP Value Modifier (Admin)"},
+ {"php_admin_flag", php_apache_admin_flag_handler, NULL, ACCESS_CONF|RSRC_CONF, TAKE2, "PHP Flag Modifier (Admin)"},
+ {"PHPINIDir", php_apache_phpini_set, NULL, RSRC_CONF, TAKE1, "Directory containing the php.ini file"},
+ {NULL}
+};
+/* }}} */
+
+/* {{{ odule MODULE_VAR_EXPORT php5_module
+ */
+module MODULE_VAR_EXPORT php5_module =
+{
+ STANDARD_MODULE_STUFF,
+ php_init_handler, /* initializer */
+ php_create_dir, /* per-directory config creator */
+ php_merge_dir, /* dir merger */
+ NULL, /* per-server config creator */
+ NULL, /* merge server config */
+ php_commands, /* command table */
+ php_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+#if MODULE_MAGIC_NUMBER >= 19970103
+ , NULL /* header parser */
+#endif
+#if MODULE_MAGIC_NUMBER >= 19970719
+ , NULL /* child_init */
+#endif
+#if MODULE_MAGIC_NUMBER >= 19970728
+ , php_child_exit_handler /* child_exit */
+#endif
+#if MODULE_MAGIC_NUMBER >= 19970902
+ , NULL /* post read-request */
+#endif
+};
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/apache/mod_php5.exp b/sapi/apache/mod_php5.exp
new file mode 100644
index 0000000..9ad0f0a
--- /dev/null
+++ b/sapi/apache/mod_php5.exp
@@ -0,0 +1 @@
+php5_module
diff --git a/sapi/apache/mod_php5.h b/sapi/apache/mod_php5.h
new file mode 100644
index 0000000..bdbdd47
--- /dev/null
+++ b/sapi/apache/mod_php5.h
@@ -0,0 +1,60 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Rasmus Lerdorf <rasmus@php.net> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#ifndef MOD_PHP5_H
+#define MOD_PHP5_H
+
+#if !defined(WIN32) && !defined(WINNT)
+#ifndef MODULE_VAR_EXPORT
+#define MODULE_VAR_EXPORT
+#endif
+#endif
+
+typedef struct {
+ long engine;
+ long last_modified;
+ long xbithack;
+ long terminate_child;
+ zend_bool in_request;
+} php_apache_info_struct;
+
+extern zend_module_entry apache_module_entry;
+
+#ifdef ZTS
+extern int php_apache_info_id;
+#define AP(v) TSRMG(php_apache_info_id, php_apache_info_struct *, v)
+#else
+extern php_apache_info_struct php_apache_info;
+#define AP(v) (php_apache_info.v)
+#endif
+
+/* fix for gcc4 visibility patch */
+#ifndef PHP_WIN32
+# undef MODULE_VAR_EXPORT
+# define MODULE_VAR_EXPORT PHPAPI
+#endif
+
+#endif /* MOD_PHP5_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/sapi/apache/php.sym b/sapi/apache/php.sym
new file mode 100644
index 0000000..9ad0f0a
--- /dev/null
+++ b/sapi/apache/php.sym
@@ -0,0 +1 @@
+php5_module
diff --git a/sapi/apache/php5apache.dsp b/sapi/apache/php5apache.dsp
new file mode 100644
index 0000000..fbdb761
--- /dev/null
+++ b/sapi/apache/php5apache.dsp
@@ -0,0 +1,151 @@
+# Microsoft Developer Studio Project File - Name="php5apache" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=php5apache - Win32 Release_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "php5apache.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "php5apache.mak" CFG="php5apache - Win32 Release_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "php5apache - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5apache - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5apache - Win32 Release_TS_inline" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "php5apache - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APACHEPHP5_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\..\php_build\includes" /I "..\..\main" /I "..\..\TSRM" /I "..\..\regex" /I "C:\Program Files\Apache Group\Apache\include" /D ZEND_DEBUG=0 /D "NDEBUG" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "APACHEPHP5_EXPORTS" /D "WIN32" /D "_MBCS" /D "APACHE_READDIR_H" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 php5ts.lib ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x60000000" /version:4.0 /dll /machine:I386 /libpath:"..\..\..\php_build\release" /libpath:"..\..\Release_TS" /libpath:"..\..\TSRM\Release_TS" /libpath:"..\..\Zend\Release_TS" /libpath:"C:\Program Files\Apache Group\Apache\libexec"
+
+!ELSEIF "$(CFG)" == "php5apache - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APACHEPHP5_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\..\php_build\includes" /I "..\..\main" /I "..\..\TSRM" /I "..\..\regex" /I "C:\Program Files\Apache Group\Apache\include" /D "_DEBUG" /D ZEND_DEBUG=1 /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "APACHEPHP5_EXPORTS" /D "WIN32" /D "_MBCS" /D "APACHE_READDIR_H" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 php5ts_debug.lib ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x60000000" /version:4.0 /dll /incremental:yes /debug /machine:I386 /pdbtype:sept /libpath:"..\..\..\php_build\release" /libpath:"..\..\Debug_TS" /libpath:"..\..\TSRM\Debug_TS" /libpath:"..\..\Zend\Debug_TS" /libpath:"C:\Program Files\Apache Group\Apache\libexec"
+
+!ELSEIF "$(CFG)" == "php5apache - Win32 Release_TS_inline"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS_inline"
+# PROP BASE Intermediate_Dir "Release_TS_inline"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS_inline"
+# PROP Intermediate_Dir "Release_TS_inline"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APACHEPHP5_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\..\php_build\includes" /I "..\..\main" /I "..\..\TSRM" /I "..\..\regex" /I "C:\Program Files\Apache Group\Apache\include" /D ZEND_DEBUG=0 /D "ZEND_WIN32_FORCE_INLINE" /D "NDEBUG" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "APACHEPHP5_EXPORTS" /D "WIN32" /D "_MBCS" /D "APACHE_READDIR_H" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 php5ts.lib ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\..\php_build\release" /libpath:"..\..\Release_TS_inline" /libpath:"..\..\TSRM\Release_TS_inline" /libpath:"..\..\Zend\Release_TS_inline" /libpath:"C:\Program Files\Apache Group\Apache\libexec"
+
+!ENDIF
+
+# Begin Target
+
+# Name "php5apache - Win32 Release_TS"
+# Name "php5apache - Win32 Debug_TS"
+# Name "php5apache - Win32 Release_TS_inline"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\mod_php5.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\php_apache.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sapi_apache.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\mod_php5.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\php_apache_http.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/sapi/apache/php_apache.c b/sapi/apache/php_apache.c
new file mode 100644
index 0000000..3745197
--- /dev/null
+++ b/sapi/apache/php_apache.c
@@ -0,0 +1,607 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
+ | Stig Sæther Bakken <ssb@php.net> |
+ | David Sklar <sklar@student.net> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#include "php_apache_http.h"
+
+#if defined(PHP_WIN32) || defined(NETWARE)
+#include "zend.h"
+#include "ap_compat.h"
+#endif
+
+#ifdef ZTS
+int php_apache_info_id;
+#else
+php_apache_info_struct php_apache_info;
+#endif
+
+#define SECTION(name) PUTS("<h2>" name "</h2>\n")
+
+#ifndef PHP_WIN32
+extern module *top_module;
+extern module **ap_loaded_modules;
+#else
+extern __declspec(dllimport) module *top_module;
+extern __declspec(dllimport) module **ap_loaded_modules;
+#endif
+
+PHP_FUNCTION(virtual);
+PHP_FUNCTION(apache_request_headers);
+PHP_FUNCTION(apache_response_headers);
+PHP_FUNCTION(apachelog);
+PHP_FUNCTION(apache_note);
+PHP_FUNCTION(apache_lookup_uri);
+PHP_FUNCTION(apache_child_terminate);
+PHP_FUNCTION(apache_setenv);
+PHP_FUNCTION(apache_get_version);
+PHP_FUNCTION(apache_get_modules);
+PHP_FUNCTION(apache_reset_timeout);
+
+PHP_MINFO_FUNCTION(apache);
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache_child_terminate, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache_note, 0, 0, 1)
+ ZEND_ARG_INFO(0, note_name)
+ ZEND_ARG_INFO(0, note_value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache_virtual, 0, 0, 1)
+ ZEND_ARG_INFO(0, filename)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache_request_headers, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache_response_headers, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache_setenv, 0, 0, 2)
+ ZEND_ARG_INFO(0, variable)
+ ZEND_ARG_INFO(0, value)
+ ZEND_ARG_INFO(0, walk_to_top)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache_lookup_uri, 0, 0, 1)
+ ZEND_ARG_INFO(0, uri)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache_get_version, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache_get_modules, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache_reset_timeout, 0)
+ZEND_END_ARG_INFO()
+
+
+
+const zend_function_entry apache_functions[] = {
+ PHP_FE(virtual, arginfo_apache_virtual)
+ PHP_FE(apache_request_headers, arginfo_apache_request_headers)
+ PHP_FE(apache_note, arginfo_apache_note)
+ PHP_FE(apache_lookup_uri, arginfo_apache_lookup_uri)
+ PHP_FE(apache_child_terminate, arginfo_apache_child_terminate)
+ PHP_FE(apache_setenv, arginfo_apache_setenv)
+ PHP_FE(apache_response_headers, arginfo_apache_response_headers)
+ PHP_FE(apache_get_version, arginfo_apache_get_version)
+ PHP_FE(apache_get_modules, arginfo_apache_get_modules)
+ PHP_FE(apache_reset_timeout, arginfo_apache_reset_timeout)
+ PHP_FALIAS(getallheaders, apache_request_headers, arginfo_apache_request_headers)
+ {NULL, NULL, NULL}
+};
+
+
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY("xbithack", "0", PHP_INI_ALL, OnUpdateLong, xbithack, php_apache_info_struct, php_apache_info)
+ STD_PHP_INI_ENTRY("engine", "1", PHP_INI_ALL, OnUpdateLong, engine, php_apache_info_struct, php_apache_info)
+ STD_PHP_INI_ENTRY("last_modified", "0", PHP_INI_ALL, OnUpdateLong, last_modified, php_apache_info_struct, php_apache_info)
+ STD_PHP_INI_ENTRY("child_terminate", "0", PHP_INI_ALL, OnUpdateLong, terminate_child, php_apache_info_struct, php_apache_info)
+PHP_INI_END()
+
+
+
+static void php_apache_globals_ctor(php_apache_info_struct *apache_globals TSRMLS_DC)
+{
+ apache_globals->in_request = 0;
+}
+
+
+static PHP_MINIT_FUNCTION(apache)
+{
+#ifdef ZTS
+ ts_allocate_id(&php_apache_info_id, sizeof(php_apache_info_struct), (ts_allocate_ctor) php_apache_globals_ctor, NULL);
+#else
+ php_apache_globals_ctor(&php_apache_info TSRMLS_CC);
+#endif
+ REGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+
+
+static PHP_MSHUTDOWN_FUNCTION(apache)
+{
+ UNREGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+
+zend_module_entry apache_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "apache",
+ apache_functions,
+ PHP_MINIT(apache),
+ PHP_MSHUTDOWN(apache),
+ NULL,
+ NULL,
+ PHP_MINFO(apache),
+ NO_VERSION_YET,
+ STANDARD_MODULE_PROPERTIES
+};
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(apache)
+{
+ char *apv = (char *) ap_get_server_version();
+ module *modp = NULL;
+ char output_buf[128];
+#if !defined(WIN32) && !defined(WINNT)
+ char name[64];
+ char modulenames[1024];
+ char *p;
+#endif
+ server_rec *serv;
+ extern char server_root[MAX_STRING_LEN];
+ extern uid_t user_id;
+ extern char *user_name;
+ extern gid_t group_id;
+ extern int max_requests_per_child;
+
+ serv = ((request_rec *) SG(server_context))->server;
+
+
+ php_info_print_table_start();
+
+#ifdef PHP_WIN32
+ php_info_print_table_row(1, "Apache for Windows 95/NT");
+ php_info_print_table_end();
+ php_info_print_table_start();
+#elif defined(NETWARE)
+ php_info_print_table_row(1, "Apache for NetWare");
+ php_info_print_table_end();
+ php_info_print_table_start();
+#else
+ php_info_print_table_row(2, "APACHE_INCLUDE", PHP_APACHE_INCLUDE);
+ php_info_print_table_row(2, "APACHE_TARGET", PHP_APACHE_TARGET);
+#endif
+
+ if (apv && *apv) {
+ php_info_print_table_row(2, "Apache Version", apv);
+ }
+
+#ifdef APACHE_RELEASE
+ snprintf(output_buf, sizeof(output_buf), "%d", APACHE_RELEASE);
+ php_info_print_table_row(2, "Apache Release", output_buf);
+#endif
+ snprintf(output_buf, sizeof(output_buf), "%d", MODULE_MAGIC_NUMBER);
+ php_info_print_table_row(2, "Apache API Version", output_buf);
+ snprintf(output_buf, sizeof(output_buf), "%s:%u", serv->server_hostname, serv->port);
+ php_info_print_table_row(2, "Hostname:Port", output_buf);
+#if !defined(WIN32) && !defined(WINNT)
+ snprintf(output_buf, sizeof(output_buf), "%s(%d)/%d", user_name, (int)user_id, (int)group_id);
+ php_info_print_table_row(2, "User/Group", output_buf);
+ snprintf(output_buf, sizeof(output_buf), "Per Child: %d - Keep Alive: %s - Max Per Connection: %d", max_requests_per_child, serv->keep_alive ? "on":"off", serv->keep_alive_max);
+ php_info_print_table_row(2, "Max Requests", output_buf);
+#endif
+ snprintf(output_buf, sizeof(output_buf), "Connection: %d - Keep-Alive: %d", serv->timeout, serv->keep_alive_timeout);
+ php_info_print_table_row(2, "Timeouts", output_buf);
+#if !defined(WIN32) && !defined(WINNT)
+/*
+ This block seems to be working on NetWare; But it seems to be showing
+ all modules instead of just the loaded ones
+*/
+ php_info_print_table_row(2, "Server Root", server_root);
+
+ strcpy(modulenames, "");
+ for(modp = top_module; modp; modp = modp->next) {
+ strlcpy(name, modp->name, sizeof(name));
+ if ((p = strrchr(name, '.'))) {
+ *p='\0'; /* Cut off ugly .c extensions on module names */
+ }
+ strlcat(modulenames, name, sizeof(modulenames));
+ if (modp->next) {
+ strlcat(modulenames, ", ", sizeof(modulenames));
+ }
+ }
+ php_info_print_table_row(2, "Loaded Modules", modulenames);
+#endif
+
+ php_info_print_table_end();
+
+ DISPLAY_INI_ENTRIES();
+
+ {
+ register int i;
+ array_header *arr;
+ table_entry *elts;
+ request_rec *r;
+
+ r = ((request_rec *) SG(server_context));
+ arr = table_elts(r->subprocess_env);
+ elts = (table_entry *)arr->elts;
+
+ SECTION("Apache Environment");
+ php_info_print_table_start();
+ php_info_print_table_header(2, "Variable", "Value");
+ for (i=0; i < arr->nelts; i++) {
+ php_info_print_table_row(2, elts[i].key, elts[i].val);
+ }
+ php_info_print_table_end();
+ }
+
+ {
+ array_header *env_arr;
+ table_entry *env;
+ int i;
+ request_rec *r;
+
+ r = ((request_rec *) SG(server_context));
+ SECTION("HTTP Headers Information");
+ php_info_print_table_start();
+ php_info_print_table_colspan_header(2, "HTTP Request Headers");
+ php_info_print_table_row(2, "HTTP Request", r->the_request);
+ env_arr = table_elts(r->headers_in);
+ env = (table_entry *)env_arr->elts;
+ for (i = 0; i < env_arr->nelts; ++i) {
+ if (env[i].key) {
+ php_info_print_table_row(2, env[i].key, env[i].val);
+ }
+ }
+ php_info_print_table_colspan_header(2, "HTTP Response Headers");
+ env_arr = table_elts(r->headers_out);
+ env = (table_entry *)env_arr->elts;
+ for(i = 0; i < env_arr->nelts; ++i) {
+ if (env[i].key) {
+ php_info_print_table_row(2, env[i].key, env[i].val);
+ }
+ }
+ php_info_print_table_end();
+ }
+}
+/* }}} */
+
+/* {{{ proto bool apache_child_terminate(void)
+ Terminate apache process after this request */
+PHP_FUNCTION(apache_child_terminate)
+{
+#ifndef MULTITHREAD
+ if (AP(terminate_child)) {
+ ap_child_terminate( ((request_rec *)SG(server_context)) );
+ RETURN_TRUE;
+ } else { /* tell them to get lost! */
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "This function is disabled");
+ RETURN_FALSE;
+ }
+#else
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "This function is not supported in this build");
+ RETURN_FALSE;
+#endif
+}
+/* }}} */
+
+/* {{{ proto string apache_note(string note_name [, string note_value])
+ Get and set Apache request notes */
+PHP_FUNCTION(apache_note)
+{
+ char *note_name, *note_val = NULL;
+ int note_name_len, note_val_len;
+ char *old_val;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &note_name, &note_name_len, &note_val, &note_val_len) == FAILURE) {
+ return;
+ }
+
+ old_val = (char *) table_get(((request_rec *)SG(server_context))->notes, note_name);
+
+ if (note_val) {
+ table_set(((request_rec *)SG(server_context))->notes, note_name, note_val);
+ }
+
+ if (old_val) {
+ RETURN_STRING(old_val, 1);
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto bool virtual(string filename)
+ Perform an Apache sub-request */
+/* This function is equivalent to <!--#include virtual...-->
+ * in mod_include. It does an Apache sub-request. It is useful
+ * for including CGI scripts or .shtml files, or anything else
+ * that you'd parse through Apache (for .phtml files, you'd probably
+ * want to use <?Include>. This only works when PHP is compiled
+ * as an Apache module, since it uses the Apache API for doing
+ * sub requests.
+ */
+PHP_FUNCTION(virtual)
+{
+ char *filename;
+ int filename_len;
+ request_rec *rr = NULL;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) {
+ return;
+ }
+
+ if (!(rr = sub_req_lookup_uri (filename, ((request_rec *) SG(server_context))))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - URI lookup failed", filename);
+ if (rr)
+ destroy_sub_req (rr);
+ RETURN_FALSE;
+ }
+
+ if (rr->status != 200) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - error finding URI", filename);
+ if (rr)
+ destroy_sub_req (rr);
+ RETURN_FALSE;
+ }
+
+ php_output_end_all(TSRMLS_C);
+ php_header(TSRMLS_C);
+
+ if (run_sub_req(rr)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - request execution failed", filename);
+ if (rr)
+ destroy_sub_req (rr);
+ RETURN_FALSE;
+ }
+
+ if (rr)
+ destroy_sub_req (rr);
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto array getallheaders(void)
+ Alias for apache_request_headers() */
+/* }}} */
+
+/* {{{ proto array apache_request_headers(void)
+ Fetch all HTTP request headers */
+PHP_FUNCTION(apache_request_headers)
+{
+ array_header *env_arr;
+ table_entry *tenv;
+ int i;
+
+ array_init(return_value);
+ env_arr = table_elts(((request_rec *) SG(server_context))->headers_in);
+ tenv = (table_entry *)env_arr->elts;
+ for (i = 0; i < env_arr->nelts; ++i) {
+ if (!tenv[i].key) {
+ continue;
+ }
+ if (add_assoc_string(return_value, tenv[i].key, (tenv[i].val==NULL) ? "" : tenv[i].val, 1)==FAILURE) {
+ RETURN_FALSE;
+ }
+ }
+}
+/* }}} */
+
+/* {{{ proto array apache_response_headers(void)
+ Fetch all HTTP response headers */
+PHP_FUNCTION(apache_response_headers)
+{
+ array_header *env_arr;
+ table_entry *tenv;
+ int i;
+
+ array_init(return_value);
+ env_arr = table_elts(((request_rec *) SG(server_context))->headers_out);
+ tenv = (table_entry *)env_arr->elts;
+ for (i = 0; i < env_arr->nelts; ++i) {
+ if (!tenv[i].key) continue;
+ if (add_assoc_string(return_value, tenv[i].key, (tenv[i].val==NULL) ? "" : tenv[i].val, 1)==FAILURE) {
+ RETURN_FALSE;
+ }
+ }
+}
+/* }}} */
+
+/* {{{ proto bool apache_setenv(string variable, string value [, bool walk_to_top])
+ Set an Apache subprocess_env variable */
+PHP_FUNCTION(apache_setenv)
+{
+ int var_len, val_len;
+ zend_bool top=0;
+ char *var = NULL, *val = NULL;
+ request_rec *r = (request_rec *) SG(server_context);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &var, &var_len, &val, &val_len, &top) == FAILURE) {
+ return;
+ }
+
+ while(top) {
+ if(r->prev) r = r->prev;
+ else break;
+ }
+
+ ap_table_setn(r->subprocess_env, ap_pstrndup(r->pool, var, var_len), ap_pstrndup(r->pool, val, val_len));
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto object apache_lookup_uri(string URI)
+ Perform a partial request of the given URI to obtain information about it */
+PHP_FUNCTION(apache_lookup_uri)
+{
+ char *filename;
+ int filename_len;
+ request_rec *rr=NULL;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
+ return;
+ }
+
+ if (!(rr = sub_req_lookup_uri(filename, ((request_rec *) SG(server_context))))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "URI lookup failed '%s'", filename);
+ RETURN_FALSE;
+ }
+
+ object_init(return_value);
+ add_property_long(return_value,"status", rr->status);
+
+ if (rr->the_request) {
+ add_property_string(return_value,"the_request", rr->the_request, 1);
+ }
+ if (rr->status_line) {
+ add_property_string(return_value,"status_line", (char *)rr->status_line, 1);
+ }
+ if (rr->method) {
+ add_property_string(return_value,"method", (char *)rr->method, 1);
+ }
+ if (rr->content_type) {
+ add_property_string(return_value,"content_type", (char *)rr->content_type, 1);
+ }
+ if (rr->handler) {
+ add_property_string(return_value,"handler", (char *)rr->handler, 1);
+ }
+ if (rr->uri) {
+ add_property_string(return_value,"uri", rr->uri, 1);
+ }
+ if (rr->filename) {
+ add_property_string(return_value,"filename", rr->filename, 1);
+ }
+ if (rr->path_info) {
+ add_property_string(return_value,"path_info", rr->path_info, 1);
+ }
+ if (rr->args) {
+ add_property_string(return_value,"args", rr->args, 1);
+ }
+ if (rr->boundary) {
+ add_property_string(return_value,"boundary", rr->boundary, 1);
+ }
+
+ add_property_long(return_value,"no_cache", rr->no_cache);
+ add_property_long(return_value,"no_local_copy", rr->no_local_copy);
+ add_property_long(return_value,"allowed", rr->allowed);
+ add_property_long(return_value,"sent_bodyct", rr->sent_bodyct);
+ add_property_long(return_value,"bytes_sent", rr->bytes_sent);
+ add_property_long(return_value,"byterange", rr->byterange);
+ add_property_long(return_value,"clength", rr->clength);
+
+#if MODULE_MAGIC_NUMBER >= 19980324
+ if (rr->unparsed_uri) {
+ add_property_string(return_value,"unparsed_uri", rr->unparsed_uri, 1);
+ }
+ if(rr->mtime) {
+ add_property_long(return_value,"mtime", rr->mtime);
+ }
+#endif
+ if(rr->request_time) {
+ add_property_long(return_value,"request_time", rr->request_time);
+ }
+
+ destroy_sub_req(rr);
+}
+/* }}} */
+
+
+#if 0
+/*
+This function is most likely a bad idea. Just playing with it for now.
+*/
+PHP_FUNCTION(apache_exec_uri)
+{
+ char *filename;
+ int filename_len;
+ request_rec *rr=NULL;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
+ return;
+ }
+
+ if(!(rr = ap_sub_req_lookup_uri(filename, ((request_rec *) SG(server_context))))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "URI lookup failed", filename);
+ RETURN_FALSE;
+ }
+
+ RETVAL_LONG(ap_run_sub_req(rr));
+ ap_destroy_sub_req(rr);
+}
+#endif
+
+/* {{{ proto string apache_get_version(void)
+ Fetch Apache version */
+PHP_FUNCTION(apache_get_version)
+{
+ char *apv = (char *) ap_get_server_version();
+
+ if (apv && *apv) {
+ RETURN_STRING(apv, 1);
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto array apache_get_modules(void)
+ Get a list of loaded Apache modules */
+PHP_FUNCTION(apache_get_modules)
+{
+ int n;
+ char *p;
+
+ array_init(return_value);
+
+ for (n = 0; ap_loaded_modules[n]; ++n) {
+ char *s = (char *) ap_loaded_modules[n]->name;
+ if ((p = strchr(s, '.'))) {
+ add_next_index_stringl(return_value, s, (p - s), 1);
+ } else {
+ add_next_index_string(return_value, s, 1);
+ }
+ }
+}
+/* }}} */
+
+/* {{{ proto bool apache_reset_timeout(void)
+ Reset the Apache write timer */
+PHP_FUNCTION(apache_reset_timeout)
+{
+ ap_reset_timeout((request_rec *)SG(server_context));
+ RETURN_TRUE;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/apache/php_apache_http.h b/sapi/apache/php_apache_http.h
new file mode 100644
index 0000000..12788d3
--- /dev/null
+++ b/sapi/apache/php_apache_http.h
@@ -0,0 +1,70 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
+ | Stig Sæther Bakken <ssb@php.net> |
+ | David Sklar <sklar@student.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#define NO_REGEX_EXTRA_H
+
+#ifdef WIN32
+#include <stddef.h>
+#endif
+
+#ifdef NETWARE
+#include <netinet/in.h>
+#endif
+
+#include "zend.h"
+#include "ext/ereg/php_regex.h"
+#include "php_compat.h"
+
+#ifdef HAVE_OPENSSL_EXT
+/* zlib typedefs free_func which causes problems if the SSL includes happen
+ * after zlib.h is included */
+# include <openssl/ssl.h>
+#endif
+
+#ifdef regex_t
+#undef regex_t
+#endif
+
+#include "httpd.h"
+#include "http_config.h"
+
+#if MODULE_MAGIC_NUMBER > 19980712
+# include "ap_compat.h"
+#else
+# if MODULE_MAGIC_NUMBER > 19980324
+# include "compat.h"
+# endif
+#endif
+
+#include "http_core.h"
+#include "http_main.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "http_log.h"
+#include "util_script.h"
+
+#include "php_variables.h"
+#include "php_main.h"
+#include "php_ini.h"
+#include "ext/standard/php_standard.h"
+
+#include "mod_php5.h"
diff --git a/sapi/apache/sapi_apache.c b/sapi/apache/sapi_apache.c
new file mode 100644
index 0000000..88c9985
--- /dev/null
+++ b/sapi/apache/sapi_apache.c
@@ -0,0 +1,73 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@php.net> |
+ | (with helpful hints from Dean Gaudet <dgaudet@arctic.org> |
+ | PHP 4.0 patches by: |
+ | Zeev Suraski <zeev@zend.com> |
+ | Stig Bakken <ssb@php.net> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#include "php_apache_http.h"
+
+/* {{{ apache_php_module_main
+ */
+int apache_php_module_main(request_rec *r, int display_source_mode TSRMLS_DC)
+{
+ int retval = OK;
+ zend_file_handle file_handle;
+
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ return FAILURE;
+ }
+ /* sending a file handle to another dll is not working
+ so let zend open it. */
+
+ if (display_source_mode) {
+ zend_syntax_highlighter_ini syntax_highlighter_ini;
+
+ php_get_highlight_struct(&syntax_highlighter_ini);
+ if (highlight_file(SG(request_info).path_translated, &syntax_highlighter_ini TSRMLS_CC) != SUCCESS) {
+ retval = NOT_FOUND;
+ }
+ } else {
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.handle.fd = 0;
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.opened_path = NULL;
+ file_handle.free_filename = 0;
+
+ (void) php_execute_script(&file_handle TSRMLS_CC);
+ }
+
+ AP(in_request) = 0;
+
+ zend_try {
+ php_request_shutdown(NULL);
+ } zend_end_try();
+
+ return retval;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/apache2filter/CREDITS b/sapi/apache2filter/CREDITS
new file mode 100644
index 0000000..c298a9b
--- /dev/null
+++ b/sapi/apache2filter/CREDITS
@@ -0,0 +1,2 @@
+Apache 2.0 Filter
+Sascha Schumann, Aaron Bannert
diff --git a/sapi/apache2filter/EXPERIMENTAL b/sapi/apache2filter/EXPERIMENTAL
new file mode 100644
index 0000000..293159a
--- /dev/null
+++ b/sapi/apache2filter/EXPERIMENTAL
@@ -0,0 +1,5 @@
+this module is experimental,
+its functions may change their names
+or move to extension all together
+so do not rely to much on them
+you have been warned!
diff --git a/sapi/apache2filter/README b/sapi/apache2filter/README
new file mode 100644
index 0000000..3054e20
--- /dev/null
+++ b/sapi/apache2filter/README
@@ -0,0 +1,71 @@
+WHAT IS THIS?
+
+ This module exploits the layered I/O support in Apache 2.0.
+
+HOW DOES IT WORK?
+
+ In Apache 2.0, you have handlers which generate content (like
+ reading a script from disk). The content goes then through
+ a chain of filters. PHP can be such a filter, so that it processes
+ your script and hands the output to the next filter (which will
+ usually cause a write to the network).
+
+DOES IT WORK?
+
+ It is experimental as interfaces in Apache 2.0 might change in the
+ future.
+
+HOW TO INSTALL
+
+ This SAPI module is known to work with Apache 2.0.40.
+
+ $ cd apache-2.x
+ $ cd src
+ $ ./configure --enable-so
+ $ make install
+
+ For testing purposes, you might want to use --with-mpm=prefork.
+ (Albeit PHP also works with threaded MPMs.)
+
+ Configure PHP 4:
+
+ $ cd php-4.x
+ $ ./configure --with-apxs2=/path/to/apache-2.0/bin/apxs
+ $ make install
+
+ At the end of conf/httpd.conf, add:
+
+ AddType application/x-httpd-php .php
+
+ If you would like to enable source code highlighting functionality add:
+
+ AddType application/x-httpd-php-source .phps
+
+ That's it. Now start bin/httpd.
+
+HOW TO CONFIGURE
+
+ The Apache 2.0 PHP module supports a new configuration directive that
+ allows an admin to override the php.ini search path. For example,
+ place your php.ini file in Apache's ServerRoot/conf directory and
+ add this to your httpd.conf file:
+
+ PHPINIDir "conf"
+
+DEBUGGING APACHE AND PHP
+
+ To debug Apache, we recommened:
+
+ 1. Use the Prefork MPM (Apache 1.3-like process model) by
+ configuring Apache with '--with-mpm=prefork'.
+ 2. Start httpd using -DONE_PROCESS (e.g. (gdb) r -DONE_PROCESS).
+
+ If you want to debug a part of the PHP startup procedure, set a
+ breakpoint on 'load_module'. Step through it until apr_dso_load() is
+ done. Then you can set a breakpoint on any PHP-related symbol.
+
+TODO
+
+ PHP functions like apache_sub_req (see php_functions.c)
+ Protocol handlers
+ Passing script data to engine without temporary file
diff --git a/sapi/apache2filter/apache_config.c b/sapi/apache2filter/apache_config.c
new file mode 100644
index 0000000..333c7b8
--- /dev/null
+++ b/sapi/apache2filter/apache_config.c
@@ -0,0 +1,218 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
+
+#include "php.h"
+#include "php_ini.h"
+#include "php_apache.h"
+
+#include "apr_strings.h"
+#include "ap_config.h"
+#include "util_filter.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_script.h"
+#include "http_core.h"
+
+#ifdef PHP_AP_DEBUG
+#define phpapdebug(a) fprintf a
+#else
+#define phpapdebug(a)
+#endif
+
+typedef struct {
+ HashTable config;
+} php_conf_rec;
+
+typedef struct {
+ char *value;
+ size_t value_len;
+ char status;
+ char htaccess;
+} php_dir_entry;
+
+static const char *real_value_hnd(cmd_parms *cmd, void *dummy, const char *name, const char *value, int status)
+{
+ php_conf_rec *d = dummy;
+ php_dir_entry e;
+
+ phpapdebug((stderr, "Getting %s=%s for %p (%d)\n", name, value, dummy, zend_hash_num_elements(&d->config)));
+
+ if (!strncasecmp(value, "none", sizeof("none"))) {
+ value = "";
+ }
+
+ e.value = apr_pstrdup(cmd->pool, value);
+ e.value_len = strlen(value);
+ e.status = status;
+ e.htaccess = ((cmd->override & (RSRC_CONF|ACCESS_CONF)) == 0);
+
+ zend_hash_update(&d->config, (char *) name, strlen(name) + 1, &e, sizeof(e), NULL);
+ return NULL;
+}
+
+static const char *php_apache_value_handler(cmd_parms *cmd, void *dummy, const char *name, const char *value)
+{
+ return real_value_hnd(cmd, dummy, name, value, PHP_INI_PERDIR);
+}
+
+static const char *php_apache_admin_value_handler(cmd_parms *cmd, void *dummy, const char *name, const char *value)
+{
+ return real_value_hnd(cmd, dummy, name, value, PHP_INI_SYSTEM);
+}
+
+static const char *real_flag_hnd(cmd_parms *cmd, void *dummy, const char *arg1, const char *arg2, int status)
+{
+ char bool_val[2];
+
+ if (!strcasecmp(arg2, "On") || (arg2[0] == '1' && arg2[1] == '\0')) {
+ bool_val[0] = '1';
+ } else {
+ bool_val[0] = '0';
+ }
+ bool_val[1] = 0;
+
+ return real_value_hnd(cmd, dummy, arg1, bool_val, status);
+}
+
+static const char *php_apache_flag_handler(cmd_parms *cmd, void *dummy, const char *name, const char *value)
+{
+ return real_flag_hnd(cmd, dummy, name, value, PHP_INI_PERDIR);
+}
+
+static const char *php_apache_admin_flag_handler(cmd_parms *cmd, void *dummy, const char *name, const char *value)
+{
+ return real_flag_hnd(cmd, dummy, name, value, PHP_INI_SYSTEM);
+}
+
+static const char *php_apache_phpini_set(cmd_parms *cmd, void *mconfig, const char *arg)
+{
+ if (apache2_php_ini_path_override) {
+ return "Only first PHPINIDir directive honored per configuration tree - subsequent ones ignored";
+ }
+ apache2_php_ini_path_override = ap_server_root_relative(cmd->pool, arg);
+ return NULL;
+}
+
+
+void *merge_php_config(apr_pool_t *p, void *base_conf, void *new_conf)
+{
+ php_conf_rec *d = base_conf, *e = new_conf, *n = NULL;
+ php_dir_entry *pe;
+ php_dir_entry *data;
+ char *str;
+ uint str_len;
+ ulong num_index;
+
+ n = create_php_config(p, "merge_php_config");
+ zend_hash_copy(&n->config, &e->config, NULL, NULL, sizeof(php_dir_entry));
+
+ phpapdebug((stderr, "Merge dir (%p)+(%p)=(%p)\n", base_conf, new_conf, n));
+ for (zend_hash_internal_pointer_reset(&d->config);
+ zend_hash_get_current_key_ex(&d->config, &str, &str_len,
+ &num_index, 0, NULL) == HASH_KEY_IS_STRING;
+ zend_hash_move_forward(&d->config)) {
+ pe = NULL;
+ zend_hash_get_current_data(&d->config, (void **) &data);
+ if (zend_hash_find(&n->config, str, str_len, (void **) &pe) == SUCCESS) {
+ if (pe->status >= data->status) continue;
+ }
+ zend_hash_update(&n->config, str, str_len, data, sizeof(*data), NULL);
+ phpapdebug((stderr, "ADDING/OVERWRITING %s (%d vs. %d)\n", str, data->status, pe?pe->status:-1));
+ }
+
+ return n;
+}
+
+char *get_php_config(void *conf, char *name, size_t name_len)
+{
+ php_conf_rec *d = conf;
+ php_dir_entry *pe;
+
+ if (zend_hash_find(&d->config, name, name_len, (void **) &pe) == SUCCESS) {
+ return pe->value;
+ }
+
+ return "";
+}
+
+void apply_config(void *dummy)
+{
+ php_conf_rec *d = dummy;
+ char *str;
+ uint str_len;
+ php_dir_entry *data;
+
+ for (zend_hash_internal_pointer_reset(&d->config);
+ zend_hash_get_current_key_ex(&d->config, &str, &str_len, NULL, 0,
+ NULL) == HASH_KEY_IS_STRING;
+ zend_hash_move_forward(&d->config)) {
+ zend_hash_get_current_data(&d->config, (void **) &data);
+ phpapdebug((stderr, "APPLYING (%s)(%s)\n", str, data->value));
+ if (zend_alter_ini_entry(str, str_len, data->value, data->value_len, data->status, data->htaccess?PHP_INI_STAGE_HTACCESS:PHP_INI_STAGE_ACTIVATE) == FAILURE) {
+ phpapdebug((stderr, "..FAILED\n"));
+ }
+ }
+}
+
+const command_rec php_dir_cmds[] =
+{
+ AP_INIT_TAKE2("php_value", php_apache_value_handler, NULL, OR_OPTIONS, "PHP Value Modifier"),
+ AP_INIT_TAKE2("php_flag", php_apache_flag_handler, NULL, OR_OPTIONS, "PHP Flag Modifier"),
+ AP_INIT_TAKE2("php_admin_value", php_apache_admin_value_handler, NULL, ACCESS_CONF|RSRC_CONF, "PHP Value Modifier (Admin)"),
+ AP_INIT_TAKE2("php_admin_flag", php_apache_admin_flag_handler, NULL, ACCESS_CONF|RSRC_CONF, "PHP Flag Modifier (Admin)"),
+ AP_INIT_TAKE1("PHPINIDir", php_apache_phpini_set, NULL, RSRC_CONF, "Directory containing the php.ini file"),
+ {NULL}
+};
+
+static apr_status_t destroy_php_config(void *data)
+{
+ php_conf_rec *d = data;
+
+ phpapdebug((stderr, "Destroying config %p\n", data));
+ zend_hash_destroy(&d->config);
+
+ return APR_SUCCESS;
+}
+
+void *create_php_config(apr_pool_t *p, char *dummy)
+{
+ php_conf_rec *newx = (php_conf_rec *) apr_pcalloc(p, sizeof(*newx));
+
+ phpapdebug((stderr, "Creating new config (%p) for %s\n", newx, dummy));
+ zend_hash_init(&newx->config, 0, NULL, NULL, 1);
+ apr_pool_cleanup_register(p, newx, destroy_php_config, apr_pool_cleanup_null);
+ return (void *) newx;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/apache2filter/config.m4 b/sapi/apache2filter/config.m4
new file mode 100644
index 0000000..c49488d
--- /dev/null
+++ b/sapi/apache2filter/config.m4
@@ -0,0 +1,139 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(apxs2filter,,
+[ --with-apxs2filter[=FILE]
+ EXPERIMENTAL: Build shared Apache 2.0 Filter module. FILE is the optional
+ pathname to the Apache apxs tool [apxs]], no, no)
+
+AC_MSG_CHECKING([for Apache 2.0 filter-module support via DSO through APXS])
+
+if test "$PHP_APXS2FILTER" != "no"; then
+ if test "$PHP_APXS2FILTER" = "yes"; then
+ APXS=apxs
+ $APXS -q CFLAGS >/dev/null 2>&1
+ if test "$?" != "0" && test -x /usr/sbin/apxs; then
+ APXS=/usr/sbin/apxs
+ fi
+ else
+ PHP_EXPAND_PATH($PHP_APXS2FILTER, APXS)
+ fi
+
+ $APXS -q CFLAGS >/dev/null 2>&1
+ if test "$?" != "0"; then
+ AC_MSG_RESULT()
+ AC_MSG_RESULT()
+ AC_MSG_RESULT([Sorry, I cannot run apxs. Possible reasons follow:])
+ AC_MSG_RESULT()
+ AC_MSG_RESULT([1. Perl is not installed])
+ AC_MSG_RESULT([2. apxs was not found. Try to pass the path using --with-apxs2filter=/path/to/apxs])
+ AC_MSG_RESULT([3. Apache was not built using --enable-so (the apxs usage page is displayed)])
+ AC_MSG_RESULT()
+ AC_MSG_RESULT([The output of $APXS follows:])
+ $APXS -q CFLAGS
+ AC_MSG_ERROR([Aborting])
+ fi
+
+ APXS_INCLUDEDIR=`$APXS -q INCLUDEDIR`
+ APXS_BINDIR=`$APXS -q BINDIR`
+ APXS_HTTPD=`$APXS -q SBINDIR`/`$APXS -q TARGET`
+ APXS_CFLAGS=`$APXS -q CFLAGS`
+ APU_BINDIR=`$APXS -q APU_BINDIR`
+ APR_BINDIR=`$APXS -q APR_BINDIR`
+
+ # Pick up ap[ru]-N-config if using httpd >=2.1
+ APR_CONFIG=`$APXS -q APR_CONFIG 2>/dev/null ||
+ echo $APR_BINDIR/apr-config`
+ APU_CONFIG=`$APXS -q APU_CONFIG 2>/dev/null ||
+ echo $APU_BINDIR/apu-config`
+
+ APR_CFLAGS="`$APR_CONFIG --cppflags --includes`"
+ APU_CFLAGS="`$APU_CONFIG --includes`"
+
+ for flag in $APXS_CFLAGS; do
+ case $flag in
+ -D*) APACHE_CPPFLAGS="$APACHE_CPPFLAGS $flag";;
+ esac
+ done
+
+ APACHE_CFLAGS="$APACHE_CPPFLAGS -I$APXS_INCLUDEDIR $APR_CFLAGS $APU_CFLAGS"
+
+ # Test that we're trying to configure with apache 2.x
+ PHP_AP_EXTRACT_VERSION($APXS_HTTPD)
+ if test "$APACHE_VERSION" -le 2000000; then
+ AC_MSG_ERROR([You have enabled Apache 2 support while your server is Apache 1.3. Please use the appropiate switch --with-apxs (without the 2)])
+ elif test "$APACHE_VERSION" -lt 2000040; then
+ AC_MSG_ERROR([Please note that Apache version >= 2.0.40 is required])
+ fi
+
+ APXS_LIBEXECDIR='$(INSTALL_ROOT)'`$APXS -q LIBEXECDIR`
+ if test -z `$APXS -q SYSCONFDIR`; then
+ INSTALL_IT="\$(mkinstalldirs) '$APXS_LIBEXECDIR' && \
+ $APXS -S LIBEXECDIR='$APXS_LIBEXECDIR' \
+ -i -n php5"
+ else
+ APXS_SYSCONFDIR='$(INSTALL_ROOT)'`$APXS -q SYSCONFDIR`
+ INSTALL_IT="\$(mkinstalldirs) '$APXS_LIBEXECDIR' && \
+ \$(mkinstalldirs) '$APXS_SYSCONFDIR' && \
+ $APXS -S LIBEXECDIR='$APXS_LIBEXECDIR' \
+ -S SYSCONFDIR='$APXS_SYSCONFDIR' \
+ -i -a -n php5"
+ fi
+
+ case $host_alias in
+ *aix*)
+ EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-brtl -Wl,-bI:$APXS_LIBEXECDIR/httpd.exp"
+ PHP_SELECT_SAPI(apache2filter, shared, sapi_apache2.c apache_config.c php_functions.c, $APACHE_CFLAGS)
+ INSTALL_IT="$INSTALL_IT $SAPI_LIBTOOL"
+ ;;
+ *darwin*)
+ dnl When using bundles on Darwin, we must resolve all symbols. However,
+ dnl the linker does not recursively look at the bundle loader and
+ dnl pull in its dependencies. Therefore, we must pull in the APR
+ dnl and APR-util libraries.
+ if test -x "$APR_CONFIG"; then
+ MH_BUNDLE_FLAGS="`$APR_CONFIG --ldflags --link-ld --libs`"
+ fi
+ if test -x "$APU_CONFIG"; then
+ MH_BUNDLE_FLAGS="`$APU_CONFIG --ldflags --link-ld --libs` $MH_BUNDLE_FLAGS"
+ fi
+ MH_BUNDLE_FLAGS="-bundle -bundle_loader $APXS_HTTPD $MH_BUNDLE_FLAGS"
+ PHP_SUBST(MH_BUNDLE_FLAGS)
+ PHP_SELECT_SAPI(apache2filter, bundle, sapi_apache2.c apache_config.c php_functions.c, $APACHE_CFLAGS)
+ SAPI_SHARED=libs/libphp5.so
+ INSTALL_IT="$INSTALL_IT $SAPI_SHARED"
+ ;;
+ *beos*)
+ if test -f _APP_; then `rm _APP_`; fi
+ `ln -s $APXS_BINDIR/httpd _APP_`
+ EXTRA_LIBS="$EXTRA_LIBS _APP_"
+ PHP_SELECT_SAPI(apache2filter, shared, sapi_apache2.c apache_config.c php_functions.c, $APACHE_CFLAGS)
+ INSTALL_IT="$INSTALL_IT $SAPI_LIBTOOL"
+ ;;
+ *)
+ PHP_SELECT_SAPI(apache2filter, shared, sapi_apache2.c apache_config.c php_functions.c, $APACHE_CFLAGS)
+ INSTALL_IT="$INSTALL_IT $SAPI_LIBTOOL"
+ ;;
+ esac
+
+ if test "$APACHE_VERSION" -lt 2004001; then
+ APXS_MPM=`$APXS -q MPM_NAME`
+ if test "$APXS_MPM" != "prefork" && test "$APXS_MPM" != "peruser" && test "$APXS_MPM" != "itk"; then
+ PHP_BUILD_THREAD_SAFE
+ fi
+ else
+ APACHE_THREADED_MPM=`$APXS_HTTPD -V | grep 'threaded:.*yes'`
+ if test -n "$APACHE_THREADED_MPM"; then
+ PHP_BUILD_THREAD_SAFE
+ fi
+ fi
+ AC_MSG_RESULT(yes)
+ PHP_SUBST(APXS)
+else
+ AC_MSG_RESULT(no)
+fi
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/apache2filter/config.w32 b/sapi/apache2filter/config.w32
new file mode 100755
index 0000000..361d031
--- /dev/null
+++ b/sapi/apache2filter/config.w32
@@ -0,0 +1,39 @@
+// vim:ft=javascript
+// $Id$
+
+ARG_ENABLE('apache2filter', 'Build Apache 2.x filter', 'no');
+
+if (PHP_APACHE2FILTER != "no") {
+ if (PHP_ZTS == "no") {
+ WARNING("Apache2 module requires an --enable-zts build of PHP on windows");
+ } else if (CHECK_HEADER_ADD_INCLUDE("httpd.h", "CFLAGS_APACHE2FILTER", PHP_PHP_BUILD + "\\include\\apache2") &&
+ CHECK_LIB("libhttpd.lib", "apache2filter", PHP_PHP_BUILD + "\\lib\\apache2") &&
+ CHECK_LIB("libapr.lib", "apache2filter", PHP_PHP_BUILD + "\\lib\\apache2") &&
+ CHECK_LIB("libaprutil.lib", "apache2filter", PHP_PHP_BUILD + "\\lib\\apache2")
+ ) {
+ SAPI('apache2filter', 'sapi_apache2.c apache_config.c php_functions.c',
+ 'php' + PHP_VERSION + 'apache2_filter.dll',
+ '/D PHP_APACHE2_EXPORTS /I win32');
+ } else {
+ WARNING("Could not find apache2 filter libraries/headers");
+ }
+}
+
+ARG_ENABLE('apache2-2filter', 'Build Apache 2.2.x filter', 'no');
+
+if (PHP_APACHE2_2FILTER != "no") {
+ if (PHP_ZTS == "no") {
+ WARNING("Apache2 module requires an --enable-zts build of PHP on windows");
+ } else if (CHECK_HEADER_ADD_INCLUDE("httpd.h", "CFLAGS_APACHE2_2FILTER", PHP_PHP_BUILD + "\\include\\apache2_2") &&
+ CHECK_LIB("libhttpd.lib", "apache2_2filter", PHP_PHP_BUILD + "\\lib\\apache2_2") &&
+ CHECK_LIB("libapr-1.lib", "apache2_2filter", PHP_PHP_BUILD + "\\lib\\apache2_2") &&
+ CHECK_LIB("libaprutil-1.lib", "apache2_2filter", PHP_PHP_BUILD + "\\lib\\apache2_2")
+ ) {
+ SAPI('apache2_2filter', 'sapi_apache2.c apache_config.c php_functions.c',
+ 'php' + PHP_VERSION + 'apache2_2_filter.dll',
+ '/D PHP_APACHE2_EXPORTS /I win32',
+ 'sapi\\apache2_2filter');
+ } else {
+ WARNING("Could not find apache2.2 filter libraries/headers");
+ }
+}
diff --git a/sapi/apache2filter/php.sym b/sapi/apache2filter/php.sym
new file mode 100644
index 0000000..9ad0f0a
--- /dev/null
+++ b/sapi/apache2filter/php.sym
@@ -0,0 +1 @@
+php5_module
diff --git a/sapi/apache2filter/php_apache.h b/sapi/apache2filter/php_apache.h
new file mode 100644
index 0000000..4ee3c0a
--- /dev/null
+++ b/sapi/apache2filter/php_apache.h
@@ -0,0 +1,81 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#ifndef PHP_APACHE_H
+#define PHP_APACHE_H
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+
+/* Declare this so we can get to it from outside the sapi_apache2.c file */
+extern module AP_MODULE_DECLARE_DATA php5_module;
+
+/* A way to specify the location of the php.ini dir in an apache directive */
+extern char *apache2_php_ini_path_override;
+
+/* The server_context used by PHP */
+typedef struct php_struct {
+ int state;
+ request_rec *r;
+ ap_filter_t *f; /* downstream output filters after the PHP filter. */
+ /* Length of post_data buffer */
+ int post_len;
+ /* Index for reading from buffer */
+ int post_idx;
+ /* stat structure of the current file */
+ struct stat finfo;
+ /* Buffer for request body filter */
+ char *post_data;
+ /* Whether or not we've processed PHP in the output filters yet. */
+ int request_processed;
+} php_struct;
+
+typedef struct _php_apr_bucket_brigade {
+ apr_bucket_brigade *bb;
+} php_apr_bucket_brigade;
+
+void *merge_php_config(apr_pool_t *p, void *base_conf, void *new_conf);
+void *create_php_config(apr_pool_t *p, char *dummy);
+char *get_php_config(void *conf, char *name, size_t name_len);
+void apply_config(void *);
+extern const command_rec php_dir_cmds[];
+
+static size_t php_apache_read_stream(void *, char *, size_t TSRMLS_DC);
+static size_t php_apache_fsizer_stream(void * TSRMLS_DC);
+
+#define APR_ARRAY_FOREACH_OPEN(arr, key, val) \
+{ \
+ apr_table_entry_t *elts; \
+ int i; \
+ elts = (apr_table_entry_t *) arr->elts; \
+ for (i = 0; i < arr->nelts; i++) { \
+ key = elts[i].key; \
+ val = elts[i].val;
+
+#define APR_ARRAY_FOREACH_CLOSE() }}
+
+/* fix for gcc4 visibility patch */
+#ifndef PHP_WIN32
+# undef AP_MODULE_DECLARE_DATA
+# define AP_MODULE_DECLARE_DATA PHPAPI
+#endif
+
+#endif /* PHP_APACHE_H */
diff --git a/sapi/apache2filter/php_functions.c b/sapi/apache2filter/php_functions.c
new file mode 100644
index 0000000..e96ceda
--- /dev/null
+++ b/sapi/apache2filter/php_functions.c
@@ -0,0 +1,425 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+
+#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
+
+#include "php.h"
+#include "ext/standard/php_smart_str.h"
+#include "ext/standard/info.h"
+#include "SAPI.h"
+
+#define CORE_PRIVATE
+#include "apr_strings.h"
+#include "apr_time.h"
+#include "ap_config.h"
+#include "util_filter.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_script.h"
+#include "http_core.h"
+
+#include "php_apache.h"
+
+static request_rec *php_apache_lookup_uri(char *filename TSRMLS_DC)
+{
+ php_struct *ctx;
+
+ if (!filename) {
+ return NULL;
+ }
+
+ ctx = SG(server_context);
+ return ap_sub_req_lookup_uri(filename, ctx->f->r, ctx->f->next);
+}
+
+/* {{{ proto bool virtual(string uri)
+ Perform an apache sub-request */
+PHP_FUNCTION(virtual)
+{
+ char *filename;
+ int filename_len;
+ request_rec *rr;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) {
+ return;
+ }
+
+ if (!(rr = php_apache_lookup_uri(filename TSRMLS_CC))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - URI lookup failed", filename);
+ RETURN_FALSE;
+ }
+
+ if (rr->status == HTTP_OK) {
+ if (ap_run_sub_req(rr)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - request execution failed", filename);
+ ap_destroy_sub_req(rr);
+ RETURN_FALSE;
+ }
+ ap_destroy_sub_req(rr);
+ RETURN_TRUE;
+ }
+
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - error finding URI", filename);
+ ap_destroy_sub_req(rr);
+ RETURN_FALSE;
+}
+/* }}} */
+
+#define ADD_LONG(name) \
+ add_property_long(return_value, #name, rr->name)
+#define ADD_TIME(name) \
+ add_property_long(return_value, #name, apr_time_sec(rr->name));
+#define ADD_STRING(name) \
+ if (rr->name) add_property_string(return_value, #name, (char *) rr->name, 1)
+
+PHP_FUNCTION(apache_lookup_uri)
+{
+ request_rec *rr;
+ char *filename;
+ int filename_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) {
+ return;
+ }
+
+ if (!(rr = php_apache_lookup_uri(filename TSRMLS_CC))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - URI lookup failed", filename);
+ RETURN_FALSE;
+ }
+
+ if (rr->status == HTTP_OK) {
+ object_init(return_value);
+
+ ADD_LONG(status);
+ ADD_STRING(the_request);
+ ADD_STRING(status_line);
+ ADD_STRING(method);
+ ADD_TIME(mtime);
+ ADD_LONG(clength);
+#if MODULE_MAGIC_NUMBER < 20020506
+ ADD_STRING(boundary);
+#endif
+ ADD_STRING(range);
+ ADD_LONG(chunked);
+ ADD_STRING(content_type);
+ ADD_STRING(handler);
+ ADD_LONG(no_cache);
+ ADD_LONG(no_local_copy);
+ ADD_STRING(unparsed_uri);
+ ADD_STRING(uri);
+ ADD_STRING(filename);
+ ADD_STRING(path_info);
+ ADD_STRING(args);
+ ADD_LONG(allowed);
+ ADD_LONG(sent_bodyct);
+ ADD_LONG(bytes_sent);
+ ADD_LONG(mtime);
+ ADD_TIME(request_time);
+
+ ap_destroy_sub_req(rr);
+ return;
+ }
+
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - error finding URI", filename);
+ ap_destroy_sub_req(rr);
+ RETURN_FALSE;
+}
+
+/* {{{ proto array getallheaders(void)
+ Fetch all HTTP request headers */
+PHP_FUNCTION(apache_request_headers)
+{
+ php_struct *ctx;
+ const apr_array_header_t *arr;
+ char *key, *val;
+
+ array_init(return_value);
+
+ ctx = SG(server_context);
+ arr = apr_table_elts(ctx->f->r->headers_in);
+
+ APR_ARRAY_FOREACH_OPEN(arr, key, val)
+ if (!val) val = "";
+ add_assoc_string(return_value, key, val, 1);
+ APR_ARRAY_FOREACH_CLOSE()
+}
+/* }}} */
+
+/* {{{ proto array apache_response_headers(void)
+ Fetch all HTTP response headers */
+PHP_FUNCTION(apache_response_headers)
+{
+ php_struct *ctx;
+ const apr_array_header_t *arr;
+ char *key, *val;
+
+ array_init(return_value);
+
+ ctx = SG(server_context);
+ arr = apr_table_elts(ctx->f->r->headers_out);
+
+ APR_ARRAY_FOREACH_OPEN(arr, key, val)
+ if (!val) val = "";
+ add_assoc_string(return_value, key, val, 1);
+ APR_ARRAY_FOREACH_CLOSE()
+}
+/* }}} */
+
+/* {{{ proto string apache_note(string note_name [, string note_value])
+ Get and set Apache request notes */
+PHP_FUNCTION(apache_note)
+{
+ php_struct *ctx;
+ char *note_name, *note_val = NULL;
+ int note_name_len, note_val_len;
+ char *old_note_val=NULL;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &note_name, &note_name_len, &note_val, &note_val_len) == FAILURE) {
+ return;
+ }
+
+ ctx = SG(server_context);
+
+ old_note_val = (char *) apr_table_get(ctx->r->notes, note_name);
+
+ if (note_val) {
+ apr_table_set(ctx->r->notes, note_name, note_val);
+ }
+
+ if (old_note_val) {
+ RETURN_STRING(old_note_val, 1);
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+
+/* {{{ proto bool apache_setenv(string variable, string value [, bool walk_to_top])
+ Set an Apache subprocess_env variable */
+PHP_FUNCTION(apache_setenv)
+{
+ php_struct *ctx;
+ char *variable=NULL, *string_val=NULL;
+ int variable_len, string_val_len;
+ zend_bool walk_to_top = 0;
+ int arg_count = ZEND_NUM_ARGS();
+
+ if (zend_parse_parameters(arg_count TSRMLS_CC, "ss|b", &variable, &variable_len, &string_val, &string_val_len, &walk_to_top) == FAILURE) {
+ return;
+ }
+
+ ctx = SG(server_context);
+
+ if (arg_count == 3 && walk_to_top) {
+ while(ctx->f->r->prev) {
+ ctx->f->r = ctx->f->r->prev;
+ }
+ }
+
+ apr_table_set(ctx->r->subprocess_env, variable, string_val);
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool apache_getenv(string variable [, bool walk_to_top])
+ Get an Apache subprocess_env variable */
+PHP_FUNCTION(apache_getenv)
+{
+ php_struct *ctx;
+ char *variable=NULL;
+ int variable_len;
+ zend_bool walk_to_top = 0;
+ int arg_count = ZEND_NUM_ARGS();
+ char *env_val=NULL;
+
+ if (zend_parse_parameters(arg_count TSRMLS_CC, "s|b", &variable, &variable_len, &walk_to_top) == FAILURE) {
+ return;
+ }
+
+ ctx = SG(server_context);
+
+ if (arg_count == 2 && walk_to_top) {
+ while(ctx->f->r->prev) {
+ ctx->f->r = ctx->f->r->prev;
+ }
+ }
+
+ env_val = (char*) apr_table_get(ctx->r->subprocess_env, variable);
+ if (env_val != NULL) {
+ RETURN_STRING(env_val, 1);
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+static char *php_apache_get_version()
+{
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20060905
+ return (char *) ap_get_server_banner();
+#else
+ return (char *) ap_get_server_version();
+#endif
+}
+
+/* {{{ proto string apache_get_version(void)
+ Fetch Apache version */
+PHP_FUNCTION(apache_get_version)
+{
+ char *apv = php_apache_get_version();
+
+ if (apv && *apv) {
+ RETURN_STRING(apv, 1);
+ } else {
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ proto array apache_get_modules(void)
+ Get a list of loaded Apache modules */
+PHP_FUNCTION(apache_get_modules)
+{
+ int n;
+ char *p;
+
+ array_init(return_value);
+
+ for (n = 0; ap_loaded_modules[n]; ++n) {
+ char *s = (char *) ap_loaded_modules[n]->name;
+ if ((p = strchr(s, '.'))) {
+ add_next_index_stringl(return_value, s, (p - s), 1);
+ } else {
+ add_next_index_string(return_value, s, 1);
+ }
+ }
+}
+/* }}} */
+
+PHP_MINFO_FUNCTION(apache)
+{
+ char *apv = php_apache_get_version();
+ smart_str tmp1 = {0};
+ int n;
+ char *p;
+
+ for (n = 0; ap_loaded_modules[n]; ++n) {
+ char *s = (char *) ap_loaded_modules[n]->name;
+ if ((p = strchr(s, '.'))) {
+ smart_str_appendl(&tmp1, s, (p - s));
+ } else {
+ smart_str_appends(&tmp1, s);
+ }
+ smart_str_appendc(&tmp1, ' ');
+ }
+ if ((tmp1.len - 1) >= 0) {
+ tmp1.c[tmp1.len - 1] = '\0';
+ }
+
+ php_info_print_table_start();
+ if (apv && *apv) {
+ php_info_print_table_row(2, "Apache Version", apv);
+ }
+ php_info_print_table_row(2, "Loaded Modules", tmp1.c);
+ smart_str_free(&tmp1);
+ php_info_print_table_end();
+}
+
+/* {{{ arginfo */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2filter_lookup_uri, 0, 0, 1)
+ ZEND_ARG_INFO(0, filename)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2filter_virtual, 0, 0, 1)
+ ZEND_ARG_INFO(0, uri)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache2filter_getallheaders, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache2filter_response_headers, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2filter_note, 0, 0, 1)
+ ZEND_ARG_INFO(0, note_name)
+ ZEND_ARG_INFO(0, note_value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2filter_setenv, 0, 0, 2)
+ ZEND_ARG_INFO(0, variable)
+ ZEND_ARG_INFO(0, value)
+ ZEND_ARG_INFO(0, walk_to_top)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2filter_getenv, 0, 0, 1)
+ ZEND_ARG_INFO(0, variable)
+ ZEND_ARG_INFO(0, walk_to_top)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache2filter_get_version, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache2filter_get_modules, 0)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+static const zend_function_entry apache_functions[] = {
+ PHP_FE(apache_lookup_uri, arginfo_apache2filter_lookup_uri)
+ PHP_FE(virtual, arginfo_apache2filter_virtual)
+ PHP_FE(apache_request_headers, arginfo_apache2filter_getallheaders)
+ PHP_FE(apache_response_headers, arginfo_apache2filter_response_headers)
+ PHP_FE(apache_setenv, arginfo_apache2filter_setenv)
+ PHP_FE(apache_getenv, arginfo_apache2filter_getenv)
+ PHP_FE(apache_note, arginfo_apache2filter_note)
+ PHP_FE(apache_get_version, arginfo_apache2filter_get_version)
+ PHP_FE(apache_get_modules, arginfo_apache2filter_get_modules)
+ PHP_FALIAS(getallheaders, apache_request_headers, arginfo_apache2filter_getallheaders)
+ {NULL, NULL, NULL}
+};
+
+zend_module_entry php_apache_module = {
+ STANDARD_MODULE_HEADER,
+ "apache2filter",
+ apache_functions,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ PHP_MINFO(apache),
+ NULL,
+ STANDARD_MODULE_PROPERTIES
+};
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/apache2filter/sapi_apache2.c b/sapi/apache2filter/sapi_apache2.c
new file mode 100644
index 0000000..8ce490e
--- /dev/null
+++ b/sapi/apache2filter/sapi_apache2.c
@@ -0,0 +1,764 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Sascha Schumann <sascha@schumann.cx> |
+ | Parts based on Apache 1.3 SAPI module by |
+ | Rasmus Lerdorf and Zeev Suraski |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include <fcntl.h>
+
+#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
+
+#include "php.h"
+#include "php_main.h"
+#include "php_ini.h"
+#include "php_variables.h"
+#include "SAPI.h"
+
+#include "ext/standard/php_smart_str.h"
+#ifndef NETWARE
+#include "ext/standard/php_standard.h"
+#else
+#include "ext/standard/basic_functions.h"
+#endif
+
+#include "apr_strings.h"
+#include "ap_config.h"
+#include "apr_buckets.h"
+#include "util_filter.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_script.h"
+#include "http_core.h"
+#include "ap_mpm.h"
+
+#include "php_apache.h"
+
+/* UnixWare and Netware define shutdown to _shutdown, which causes problems later
+ * on when using a structure member named shutdown. Since this source
+ * file does not use the system call shutdown, it is safe to #undef it.
+ */
+#undef shutdown
+
+/* A way to specify the location of the php.ini dir in an apache directive */
+char *apache2_php_ini_path_override = NULL;
+
+static int
+php_apache_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ apr_bucket *b;
+ apr_bucket_brigade *bb;
+ apr_bucket_alloc_t *ba;
+ ap_filter_t *f; /* remaining output filters */
+ php_struct *ctx;
+
+ ctx = SG(server_context);
+ f = ctx->f;
+
+ if (str_length == 0) return 0;
+
+ ba = f->c->bucket_alloc;
+ bb = apr_brigade_create(ctx->r->pool, ba);
+
+ b = apr_bucket_transient_create(str, str_length, ba);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+
+ if (ap_pass_brigade(f->next, bb) != APR_SUCCESS || ctx->r->connection->aborted) {
+ php_handle_aborted_connection();
+ }
+
+ return str_length; /* we always consume all the data passed to us. */
+}
+
+static int
+php_apache_sapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ php_struct *ctx;
+ ap_filter_t *f;
+ char *val, *ptr;
+
+ ctx = SG(server_context);
+ f = ctx->r->output_filters;
+
+ switch(op) {
+ case SAPI_HEADER_DELETE:
+ apr_table_unset(ctx->r->headers_out, sapi_header->header);
+ return 0;
+
+ case SAPI_HEADER_DELETE_ALL:
+ apr_table_clear(ctx->r->headers_out);
+ return 0;
+
+ case SAPI_HEADER_ADD:
+ case SAPI_HEADER_REPLACE:
+ val = strchr(sapi_header->header, ':');
+
+ if (!val) {
+ sapi_free_header(sapi_header);
+ return 0;
+ }
+ ptr = val;
+
+ *val = '\0';
+
+ do {
+ val++;
+ } while (*val == ' ');
+
+ if (!strcasecmp(sapi_header->header, "content-type"))
+ ctx->r->content_type = apr_pstrdup(ctx->r->pool, val);
+ else if (!strcasecmp(sapi_header->header, "content-length"))
+ ap_set_content_length(ctx->r, strtol(val, (char **)NULL, 10));
+ else if (op == SAPI_HEADER_REPLACE)
+ apr_table_set(ctx->r->headers_out, sapi_header->header, val);
+ else
+ apr_table_add(ctx->r->headers_out, sapi_header->header, val);
+
+ *ptr = ':';
+ return SAPI_HEADER_ADD;
+
+ default:
+ return 0;
+ }
+}
+
+static int
+php_apache_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ php_struct *ctx = SG(server_context);
+
+ ctx->r->status = SG(sapi_headers).http_response_code;
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+static int
+php_apache_sapi_read_post(char *buf, uint count_bytes TSRMLS_DC)
+{
+ int n;
+ int to_read;
+ php_struct *ctx = SG(server_context);
+
+ to_read = ctx->post_len - ctx->post_idx;
+ n = MIN(to_read, count_bytes);
+
+ if (n > 0) {
+ memcpy(buf, ctx->post_data + ctx->post_idx, n);
+ ctx->post_idx += n;
+ } else {
+ if (ctx->post_data) free(ctx->post_data);
+ ctx->post_data = NULL;
+ }
+
+ return n;
+}
+
+static struct stat*
+php_apache_sapi_get_stat(TSRMLS_D)
+{
+ php_struct *ctx = SG(server_context);
+
+ ctx->finfo.st_uid = ctx->r->finfo.user;
+ ctx->finfo.st_gid = ctx->r->finfo.group;
+ ctx->finfo.st_dev = ctx->r->finfo.device;
+ ctx->finfo.st_ino = ctx->r->finfo.inode;
+#ifdef NETWARE
+ ctx->finfo.st_atime.tv_sec = apr_time_sec(ctx->r->finfo.atime);
+ ctx->finfo.st_mtime.tv_sec = apr_time_sec(ctx->r->finfo.mtime);
+ ctx->finfo.st_ctime.tv_sec = apr_time_sec(ctx->r->finfo.ctime);
+#else
+ ctx->finfo.st_atime = apr_time_sec(ctx->r->finfo.atime);
+ ctx->finfo.st_mtime = apr_time_sec(ctx->r->finfo.mtime);
+ ctx->finfo.st_ctime = apr_time_sec(ctx->r->finfo.ctime);
+#endif
+
+ ctx->finfo.st_size = ctx->r->finfo.size;
+ ctx->finfo.st_nlink = ctx->r->finfo.nlink;
+
+ return &ctx->finfo;
+}
+
+static char *
+php_apache_sapi_read_cookies(TSRMLS_D)
+{
+ php_struct *ctx = SG(server_context);
+ const char *http_cookie;
+
+ http_cookie = apr_table_get(ctx->r->headers_in, "cookie");
+
+ /* The SAPI interface should use 'const char *' */
+ return (char *) http_cookie;
+}
+
+static char *
+php_apache_sapi_getenv(char *name, size_t name_len TSRMLS_DC)
+{
+ php_struct *ctx = SG(server_context);
+ const char *env_var;
+
+ env_var = apr_table_get(ctx->r->subprocess_env, name);
+
+ return (char *) env_var;
+}
+
+static void
+php_apache_sapi_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ php_struct *ctx = SG(server_context);
+ const apr_array_header_t *arr = apr_table_elts(ctx->r->subprocess_env);
+ char *key, *val;
+ unsigned int new_val_len;
+
+ APR_ARRAY_FOREACH_OPEN(arr, key, val)
+ if (!val) {
+ val = "";
+ }
+ if (sapi_module.input_filter(PARSE_SERVER, key, &val, strlen(val), &new_val_len TSRMLS_CC)) {
+ php_register_variable_safe(key, val, new_val_len, track_vars_array TSRMLS_CC);
+ }
+ APR_ARRAY_FOREACH_CLOSE()
+
+ php_register_variable("PHP_SELF", ctx->r->uri, track_vars_array TSRMLS_CC);
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &ctx->r->uri, strlen(ctx->r->uri), &new_val_len TSRMLS_CC)) {
+ php_register_variable_safe("PHP_SELF", ctx->r->uri, new_val_len, track_vars_array TSRMLS_CC);
+ }
+}
+
+static void
+php_apache_sapi_flush(void *server_context)
+{
+ php_struct *ctx;
+ apr_bucket_brigade *bb;
+ apr_bucket_alloc_t *ba;
+ apr_bucket *b;
+ ap_filter_t *f; /* output filters */
+ TSRMLS_FETCH();
+
+ ctx = server_context;
+
+ /* If we haven't registered a server_context yet,
+ * then don't bother flushing. */
+ if (!server_context)
+ return;
+
+ sapi_send_headers(TSRMLS_C);
+
+ ctx->r->status = SG(sapi_headers).http_response_code;
+ SG(headers_sent) = 1;
+
+ f = ctx->f;
+
+ /* Send a flush bucket down the filter chain. The current default
+ * handler seems to act on the first flush bucket, but ignores
+ * all further flush buckets.
+ */
+
+ ba = ctx->r->connection->bucket_alloc;
+ bb = apr_brigade_create(ctx->r->pool, ba);
+ b = apr_bucket_flush_create(ba);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ if (ap_pass_brigade(f->next, bb) != APR_SUCCESS || ctx->r->connection->aborted) {
+ php_handle_aborted_connection();
+ }
+}
+
+static void php_apache_sapi_log_message(char *msg TSRMLS_DC)
+{
+ php_struct *ctx;
+
+ ctx = SG(server_context);
+
+ if (ctx == NULL) { /* we haven't initialized our ctx yet, oh well */
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL, "%s", msg);
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ctx->r->server, "%s", msg);
+ }
+}
+
+static int
+php_apache_disable_caching(ap_filter_t *f)
+{
+ /* Identify PHP scripts as non-cacheable, thus preventing
+ * Apache from sending a 304 status when the browser sends
+ * If-Modified-Since header.
+ */
+ f->r->no_local_copy = 1;
+
+ return OK;
+}
+
+static double php_apache_sapi_get_request_time(TSRMLS_D)
+{
+ php_struct *ctx = SG(server_context);
+ return ((double) apr_time_as_msec(ctx->r->request_time)) / 1000.0;
+}
+
+extern zend_module_entry php_apache_module;
+
+static int php_apache2_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &php_apache_module, 1)==FAILURE) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static sapi_module_struct apache2_sapi_module = {
+ "apache2filter",
+ "Apache 2.0 Filter",
+
+ php_apache2_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ php_apache_sapi_ub_write, /* unbuffered write */
+ php_apache_sapi_flush, /* flush */
+ php_apache_sapi_get_stat, /* get uid */
+ php_apache_sapi_getenv, /* getenv */
+
+ php_error, /* error handler */
+
+ php_apache_sapi_header_handler, /* header handler */
+ php_apache_sapi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ php_apache_sapi_read_post, /* read POST data */
+ php_apache_sapi_read_cookies, /* read Cookies */
+
+ php_apache_sapi_register_variables,
+ php_apache_sapi_log_message, /* Log message */
+ php_apache_sapi_get_request_time, /* Get Request Time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+static int php_input_filter(ap_filter_t *f, apr_bucket_brigade *bb,
+ ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
+{
+ php_struct *ctx;
+ long old_index;
+ apr_bucket *b;
+ const char *str;
+ apr_size_t n;
+ apr_status_t rv;
+ TSRMLS_FETCH();
+
+ if (f->r->proxyreq) {
+ return ap_get_brigade(f->next, bb, mode, block, readbytes);
+ }
+
+ ctx = SG(server_context);
+ if (ctx == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
+ "php failed to get server context");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ if ((rv = ap_get_brigade(f->next, bb, mode, block, readbytes)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
+ apr_bucket_read(b, &str, &n, APR_NONBLOCK_READ);
+ if (n > 0) {
+ old_index = ctx->post_len;
+ ctx->post_len += n;
+ ctx->post_data = realloc(ctx->post_data, ctx->post_len + 1);
+ memcpy(ctx->post_data + old_index, str, n);
+ }
+ }
+ return APR_SUCCESS;
+}
+
+static void php_apache_request_ctor(ap_filter_t *f, php_struct *ctx TSRMLS_DC)
+{
+ char *content_type;
+ char *content_length;
+ const char *auth;
+
+ PG(during_request_startup) = 0;
+ SG(sapi_headers).http_response_code = !f->r->status ? HTTP_OK : f->r->status;
+ SG(request_info).content_type = apr_table_get(f->r->headers_in, "Content-Type");
+#undef safe_strdup
+#define safe_strdup(x) ((x)?strdup((x)):NULL)
+ SG(request_info).query_string = safe_strdup(f->r->args);
+ SG(request_info).request_method = f->r->method;
+ SG(request_info).proto_num = f->r->proto_num;
+ SG(request_info).request_uri = safe_strdup(f->r->uri);
+ SG(request_info).path_translated = safe_strdup(f->r->filename);
+ f->r->no_local_copy = 1;
+ content_type = sapi_get_default_content_type(TSRMLS_C);
+ f->r->content_type = apr_pstrdup(f->r->pool, content_type);
+ SG(request_info).post_data = ctx->post_data;
+ SG(request_info).post_data_length = ctx->post_len;
+
+ efree(content_type);
+
+ content_length = (char *) apr_table_get(f->r->headers_in, "Content-Length");
+ SG(request_info).content_length = (content_length ? atol(content_length) : 0);
+
+ apr_table_unset(f->r->headers_out, "Content-Length");
+ apr_table_unset(f->r->headers_out, "Last-Modified");
+ apr_table_unset(f->r->headers_out, "Expires");
+ apr_table_unset(f->r->headers_out, "ETag");
+
+ auth = apr_table_get(f->r->headers_in, "Authorization");
+ php_handle_auth_data(auth TSRMLS_CC);
+
+ if (SG(request_info).auth_user == NULL && f->r->user) {
+ SG(request_info).auth_user = estrdup(f->r->user);
+ }
+
+ ctx->r->user = apr_pstrdup(ctx->r->pool, SG(request_info).auth_user);
+
+ php_request_startup(TSRMLS_C);
+}
+
+static void php_apache_request_dtor(ap_filter_t *f TSRMLS_DC)
+{
+ php_apr_bucket_brigade *pbb = (php_apr_bucket_brigade *)f->ctx;
+
+ php_request_shutdown(NULL);
+
+ if (SG(request_info).query_string) {
+ free(SG(request_info).query_string);
+ }
+ if (SG(request_info).request_uri) {
+ free(SG(request_info).request_uri);
+ }
+ if (SG(request_info).path_translated) {
+ free(SG(request_info).path_translated);
+ }
+
+ apr_brigade_destroy(pbb->bb);
+}
+
+static int php_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+ php_struct *ctx;
+ void *conf = ap_get_module_config(f->r->per_dir_config, &php5_module);
+ char *p = get_php_config(conf, "engine", sizeof("engine"));
+ zend_file_handle zfd;
+ php_apr_bucket_brigade *pbb;
+ apr_bucket *b;
+ TSRMLS_FETCH();
+
+ if (f->r->proxyreq) {
+ zend_try {
+ zend_ini_deactivate(TSRMLS_C);
+ } zend_end_try();
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ /* handle situations where user turns the engine off */
+ if (*p == '0') {
+ zend_try {
+ zend_ini_deactivate(TSRMLS_C);
+ } zend_end_try();
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ if(f->ctx) {
+ pbb = (php_apr_bucket_brigade *)f->ctx;
+ } else {
+ pbb = f->ctx = apr_palloc(f->r->pool, sizeof(*pbb));
+ pbb->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ }
+
+ if(ap_save_brigade(NULL, &pbb->bb, &bb, f->r->pool) != APR_SUCCESS) {
+ /* Bad */
+ }
+
+ apr_brigade_cleanup(bb);
+
+ /* Check to see if the last bucket in this brigade, it not
+ * we have to wait until then. */
+ if(!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(pbb->bb))) {
+ return 0;
+ }
+
+ /* Setup the CGI variables if this is the main request.. */
+ if (f->r->main == NULL ||
+ /* .. or if the sub-request envinronment differs from the main-request. */
+ f->r->subprocess_env != f->r->main->subprocess_env
+ ) {
+ /* setup standard CGI variables */
+ ap_add_common_vars(f->r);
+ ap_add_cgi_vars(f->r);
+ }
+
+ ctx = SG(server_context);
+ if (ctx == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
+ "php failed to get server context");
+ zend_try {
+ zend_ini_deactivate(TSRMLS_C);
+ } zend_end_try();
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ ctx->f = f->next; /* save whatever filters are after us in the chain. */
+
+ if (ctx->request_processed) {
+ zend_try {
+ zend_ini_deactivate(TSRMLS_C);
+ } zend_end_try();
+ return ap_pass_brigade(f->next, bb);
+ }
+
+ apply_config(conf);
+ php_apache_request_ctor(f, ctx TSRMLS_CC);
+
+ /* It'd be nice if we could highlight based of a zend_file_handle here....
+ * ...but we can't. */
+
+ zfd.type = ZEND_HANDLE_STREAM;
+
+ zfd.handle.stream.handle = pbb;
+ zfd.handle.stream.reader = php_apache_read_stream;
+ zfd.handle.stream.closer = NULL;
+ zfd.handle.stream.fsizer = php_apache_fsizer_stream;
+ zfd.handle.stream.isatty = 0;
+
+ zfd.filename = f->r->filename;
+ zfd.opened_path = NULL;
+ zfd.free_filename = 0;
+
+ php_execute_script(&zfd TSRMLS_CC);
+
+ apr_table_set(ctx->r->notes, "mod_php_memory_usage",
+ apr_psprintf(ctx->r->pool, "%u", zend_memory_peak_usage(1 TSRMLS_CC)));
+
+ php_apache_request_dtor(f TSRMLS_CC);
+
+ if (!f->r->main) {
+ ctx->request_processed = 1;
+ }
+
+ b = apr_bucket_eos_create(f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(pbb->bb, b);
+
+ /* Pass whatever is left on the brigade. */
+ return ap_pass_brigade(f->next, pbb->bb);
+}
+
+static apr_status_t
+php_apache_server_shutdown(void *tmp)
+{
+ apache2_sapi_module.shutdown(&apache2_sapi_module);
+ sapi_shutdown();
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ return APR_SUCCESS;
+}
+
+static void php_apache_add_version(apr_pool_t *p)
+{
+ TSRMLS_FETCH();
+ if (PG(expose_php)) {
+ ap_add_version_component(p, "PHP/" PHP_VERSION);
+ }
+}
+
+static int php_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
+{
+#ifndef ZTS
+ int threaded_mpm;
+
+ ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm);
+ if(threaded_mpm) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, 0, 0, "Apache is running a threaded MPM, but your PHP Module is not compiled to be threadsafe. You need to recompile PHP.");
+ return DONE;
+ }
+#endif
+ /* When this is NULL, apache won't override the hard-coded default
+ * php.ini path setting. */
+ apache2_php_ini_path_override = NULL;
+ return OK;
+}
+
+static int
+php_apache_server_startup(apr_pool_t *pconf, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ void *data = NULL;
+ const char *userdata_key = "apache2filter_post_config";
+
+ /* Apache will load, unload and then reload a DSO module. This
+ * prevents us from starting PHP until the second load. */
+ apr_pool_userdata_get(&data, userdata_key, s->process->pool);
+ if (data == NULL) {
+ /* We must use set() here and *not* setn(), otherwise the
+ * static string pointed to by userdata_key will be mapped
+ * to a different location when the DSO is reloaded and the
+ * pointers won't match, causing get() to return NULL when
+ * we expected it to return non-NULL. */
+ apr_pool_userdata_set((const void *)1, userdata_key,
+ apr_pool_cleanup_null, s->process->pool);
+ return OK;
+ }
+
+ /* Set up our overridden path. */
+ if (apache2_php_ini_path_override) {
+ apache2_sapi_module.php_ini_path_override = apache2_php_ini_path_override;
+ }
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+#endif
+ sapi_startup(&apache2_sapi_module);
+ apache2_sapi_module.startup(&apache2_sapi_module);
+ apr_pool_cleanup_register(pconf, NULL, php_apache_server_shutdown, apr_pool_cleanup_null);
+ php_apache_add_version(pconf);
+
+ return OK;
+}
+
+static void php_add_filter(request_rec *r, ap_filter_t *f)
+{
+ int output = (f == r->output_filters);
+
+ /* for those who still have Set*Filter PHP configured */
+ while (f) {
+ if (strcmp(f->frec->name, "PHP") == 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING,
+ 0, r->server,
+ "\"Set%sFilter PHP\" already configured for %s",
+ output ? "Output" : "Input", r->uri);
+ return;
+ }
+ f = f->next;
+ }
+
+ if (output) {
+ ap_add_output_filter("PHP", NULL, r, r->connection);
+ } else {
+ ap_add_input_filter("PHP", NULL, r, r->connection);
+ }
+}
+
+static void php_insert_filter(request_rec *r)
+{
+ int content_type_len = strlen("application/x-httpd-php");
+
+ if (r->content_type && !strncmp(r->content_type, "application/x-httpd-php", content_type_len-1)) {
+ if (r->content_type[content_type_len] == '\0' || !strncmp(r->content_type+content_type_len, "-source", sizeof("-source"))) {
+ php_add_filter(r, r->output_filters);
+ php_add_filter(r, r->input_filters);
+ }
+ }
+}
+
+static apr_status_t php_server_context_cleanup(void *data_)
+{
+ void **data = data_;
+ *data = NULL;
+ return APR_SUCCESS;
+}
+
+static int php_post_read_request(request_rec *r)
+{
+ php_struct *ctx;
+ TSRMLS_FETCH();
+
+ /* Initialize filter context */
+ SG(server_context) = ctx = apr_pcalloc(r->pool, sizeof(*ctx));
+
+ /* register a cleanup so we clear out the SG(server_context)
+ * after each request. Note: We pass in the pointer to the
+ * server_context in case this is handled by a different thread. */
+ apr_pool_cleanup_register(r->pool, (void *)&SG(server_context),
+ php_server_context_cleanup,
+ apr_pool_cleanup_null);
+
+ /* Save the entire request, so we can get the input or output
+ * filters if we need them. */
+ ctx->r = r;
+
+ return OK;
+}
+
+static void php_register_hook(apr_pool_t *p)
+{
+ ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_insert_filter(php_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_post_read_request(php_post_read_request, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_register_output_filter("PHP", php_output_filter, php_apache_disable_caching, AP_FTYPE_RESOURCE);
+ ap_register_input_filter("PHP", php_input_filter, php_apache_disable_caching, AP_FTYPE_RESOURCE);
+}
+
+static size_t php_apache_read_stream(void *handle, char *buf, size_t wantlen TSRMLS_DC)
+{
+ php_apr_bucket_brigade *pbb = (php_apr_bucket_brigade *)handle;
+ apr_bucket_brigade *rbb;
+ apr_size_t readlen;
+ apr_bucket *b = NULL;
+
+ rbb = pbb->bb;
+
+ if((apr_brigade_partition(pbb->bb, wantlen, &b) == APR_SUCCESS) && b){
+ pbb->bb = apr_brigade_split(rbb, b);
+ }
+
+ readlen = wantlen;
+ apr_brigade_flatten(rbb, buf, &readlen);
+ apr_brigade_cleanup(rbb);
+
+ return readlen;
+}
+
+static size_t php_apache_fsizer_stream(void *handle TSRMLS_DC)
+{
+ php_apr_bucket_brigade *pbb = (php_apr_bucket_brigade *)handle;
+ apr_off_t actual = 0;
+
+ if (apr_brigade_length(pbb->bb, 1, &actual) == APR_SUCCESS) {
+ return actual;
+ }
+
+ return 0;
+}
+
+AP_MODULE_DECLARE_DATA module php5_module = {
+ STANDARD20_MODULE_STUFF,
+ create_php_config, /* create per-directory config structure */
+ merge_php_config, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ php_dir_cmds, /* command apr_table_t */
+ php_register_hook /* register hooks */
+};
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/apache2handler/CREDITS b/sapi/apache2handler/CREDITS
new file mode 100644
index 0000000..4a78480
--- /dev/null
+++ b/sapi/apache2handler/CREDITS
@@ -0,0 +1,2 @@
+Apache 2.0 Handler
+Ian Holsman, Justin Erenkrantz (based on Apache 2.0 Filter code)
diff --git a/sapi/apache2handler/README b/sapi/apache2handler/README
new file mode 100644
index 0000000..5cbd1b9
--- /dev/null
+++ b/sapi/apache2handler/README
@@ -0,0 +1,76 @@
+WHAT IS THIS?
+
+ This module exploits the layered I/O support in Apache 2.0.
+
+HOW DOES IT WORK?
+
+ In Apache 2.0, you have handlers which generate content (like
+ reading a script from disk). The content goes then through
+ a chain of filters. PHP can be such a filter, so that it processes
+ your script and hands the output to the next filter (which will
+ usually cause a write to the network).
+
+DOES IT WORK?
+
+ Currently the issues with the module are:
+ * Thread safety of external PHP modules
+ * The lack of re-entrancy of PHP. due to this I have disabled the 'virtual'
+ function, and tried to stop any method where a php script can run another php
+ script while it is being run.
+
+
+HOW TO INSTALL
+
+ This SAPI module is known to work with Apache 2.0.44.
+
+ $ cd apache-2.x
+ $ cd src
+ $ ./configure --enable-so
+ $ make install
+
+ For testing purposes, you might want to use --with-mpm=prefork.
+ (Albeit PHP also works with threaded MPMs. See Thread Safety note above)
+
+ Configure PHP 4:
+
+ $ cd php-4.x
+ $ ./configure --with-apxs2=/path/to/apache-2.0/bin/apxs
+ $ make install
+
+ At the end of conf/httpd.conf, add:
+
+ AddType application/x-httpd-php .php
+
+ If you would like to enable source code highlighting functionality add:
+
+ AddType application/x-httpd-php-source .phps
+
+ That's it. Now start bin/httpd.
+
+HOW TO CONFIGURE
+
+ The Apache 2.0 PHP module supports a new configuration directive that
+ allows an admin to override the php.ini search path. For example,
+ place your php.ini file in Apache's ServerRoot/conf directory and
+ add this to your httpd.conf file:
+
+ PHPINIDir "conf"
+
+DEBUGGING APACHE AND PHP
+
+ To debug Apache, we recommened:
+
+ 1. Use the Prefork MPM (Apache 1.3-like process model) by
+ configuring Apache with '--with-mpm=prefork'.
+ 2. Start httpd using -DONE_PROCESS (e.g. (gdb) r -DONE_PROCESS).
+
+ If you want to debug a part of the PHP startup procedure, set a
+ breakpoint on 'load_module'. Step through it until apr_dso_load() is
+ done. Then you can set a breakpoint on any PHP-related symbol.
+
+TODO
+
+ PHP functions like apache_sub_req (see php_functions.c)
+ Source Code Highlighting
+ Protocol handlers
+
diff --git a/sapi/apache2handler/apache_config.c b/sapi/apache2handler/apache_config.c
new file mode 100644
index 0000000..2b51939
--- /dev/null
+++ b/sapi/apache2handler/apache_config.c
@@ -0,0 +1,241 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
+
+#include "php.h"
+#include "php_ini.h"
+#include "php_apache.h"
+
+#include "apr_strings.h"
+#include "ap_config.h"
+#include "util_filter.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_script.h"
+#include "http_core.h"
+
+#ifdef PHP_AP_DEBUG
+#define phpapdebug(a) fprintf a
+#else
+#define phpapdebug(a)
+#endif
+
+typedef struct {
+ HashTable config;
+} php_conf_rec;
+
+typedef struct {
+ char *value;
+ size_t value_len;
+ char status;
+ char htaccess;
+} php_dir_entry;
+
+static const char *real_value_hnd(cmd_parms *cmd, void *dummy, const char *name, const char *value, int status)
+{
+ php_conf_rec *d = dummy;
+ php_dir_entry e;
+
+ phpapdebug((stderr, "Getting %s=%s for %p (%d)\n", name, value, dummy, zend_hash_num_elements(&d->config)));
+
+ if (!strncasecmp(value, "none", sizeof("none"))) {
+ value = "";
+ }
+
+ e.value = apr_pstrdup(cmd->pool, value);
+ e.value_len = strlen(value);
+ e.status = status;
+ e.htaccess = ((cmd->override & (RSRC_CONF|ACCESS_CONF)) == 0);
+
+ zend_hash_update(&d->config, (char *) name, strlen(name) + 1, &e, sizeof(e), NULL);
+ return NULL;
+}
+
+static const char *php_apache_value_handler(cmd_parms *cmd, void *dummy, const char *name, const char *value)
+{
+ return real_value_hnd(cmd, dummy, name, value, PHP_INI_PERDIR);
+}
+
+static const char *php_apache_admin_value_handler(cmd_parms *cmd, void *dummy, const char *name, const char *value)
+{
+ return real_value_hnd(cmd, dummy, name, value, PHP_INI_SYSTEM);
+}
+
+static const char *real_flag_hnd(cmd_parms *cmd, void *dummy, const char *arg1, const char *arg2, int status)
+{
+ char bool_val[2];
+
+ if (!strcasecmp(arg2, "On") || (arg2[0] == '1' && arg2[1] == '\0')) {
+ bool_val[0] = '1';
+ } else {
+ bool_val[0] = '0';
+ }
+ bool_val[1] = 0;
+
+ return real_value_hnd(cmd, dummy, arg1, bool_val, status);
+}
+
+static const char *php_apache_flag_handler(cmd_parms *cmd, void *dummy, const char *name, const char *value)
+{
+ return real_flag_hnd(cmd, dummy, name, value, PHP_INI_PERDIR);
+}
+
+static const char *php_apache_admin_flag_handler(cmd_parms *cmd, void *dummy, const char *name, const char *value)
+{
+ return real_flag_hnd(cmd, dummy, name, value, PHP_INI_SYSTEM);
+}
+
+static const char *php_apache_phpini_set(cmd_parms *cmd, void *mconfig, const char *arg)
+{
+ if (apache2_php_ini_path_override) {
+ return "Only first PHPINIDir directive honored per configuration tree - subsequent ones ignored";
+ }
+ apache2_php_ini_path_override = ap_server_root_relative(cmd->pool, arg);
+ return NULL;
+}
+
+static zend_bool should_overwrite_per_dir_entry(HashTable *target_ht, php_dir_entry *new_per_dir_entry, zend_hash_key *hash_key, void *pData)
+{
+ php_dir_entry *orig_per_dir_entry;
+
+ if (zend_hash_find(target_ht, hash_key->arKey, hash_key->nKeyLength, (void **) &orig_per_dir_entry)==FAILURE) {
+ return 1; /* does not exist in dest, copy from source */
+ }
+
+ if (new_per_dir_entry->status >= orig_per_dir_entry->status) {
+ /* use new entry */
+ phpapdebug((stderr, "ADDING/OVERWRITING %s (%d vs. %d)\n", hash_key->arKey, new_per_dir_entry->status, orig_per_dir_entry->status));
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+void *merge_php_config(apr_pool_t *p, void *base_conf, void *new_conf)
+{
+ php_conf_rec *d = base_conf, *e = new_conf, *n = NULL;
+#if STAS_0
+ php_dir_entry *pe;
+ php_dir_entry *data;
+ char *str;
+ uint str_len;
+ ulong num_index;
+#endif
+
+ n = create_php_config(p, "merge_php_config");
+ /* copy old config */
+ zend_hash_copy(&n->config, &d->config, NULL, NULL, sizeof(php_dir_entry));
+ /* merge new config */
+ phpapdebug((stderr, "Merge dir (%p)+(%p)=(%p)\n", base_conf, new_conf, n));
+ zend_hash_merge_ex(&n->config, &e->config, NULL, sizeof(php_dir_entry), (merge_checker_func_t) should_overwrite_per_dir_entry, NULL);
+#if STAS_0
+ for (zend_hash_internal_pointer_reset(&d->config);
+ zend_hash_get_current_key_ex(&d->config, &str, &str_len,
+ &num_index, 0, NULL) == HASH_KEY_IS_STRING;
+ zend_hash_move_forward(&d->config)) {
+ pe = NULL;
+ zend_hash_get_current_data(&d->config, (void **) &data);
+ if (zend_hash_find(&n->config, str, str_len, (void **) &pe) == SUCCESS) {
+ if (pe->status >= data->status) continue;
+ }
+ phpapdebug((stderr, "ADDING/OVERWRITING %s (%d vs. %d)\n", str, data->status, pe?pe->status:-1));
+ zend_hash_update(&n->config, str, str_len, data, sizeof(*data), NULL);
+ }
+#endif
+ return n;
+}
+
+char *get_php_config(void *conf, char *name, size_t name_len)
+{
+ php_conf_rec *d = conf;
+ php_dir_entry *pe;
+
+ if (zend_hash_find(&d->config, name, name_len, (void **) &pe) == SUCCESS) {
+ return pe->value;
+ }
+
+ return "";
+}
+
+void apply_config(void *dummy)
+{
+ php_conf_rec *d = dummy;
+ char *str;
+ uint str_len;
+ php_dir_entry *data;
+
+ for (zend_hash_internal_pointer_reset(&d->config);
+ zend_hash_get_current_key_ex(&d->config, &str, &str_len, NULL, 0,
+ NULL) == HASH_KEY_IS_STRING;
+ zend_hash_move_forward(&d->config)) {
+ if (zend_hash_get_current_data(&d->config, (void **) &data) == SUCCESS) {
+ phpapdebug((stderr, "APPLYING (%s)(%s)\n", str, data->value));
+ if (zend_alter_ini_entry(str, str_len, data->value, data->value_len, data->status, data->htaccess?PHP_INI_STAGE_HTACCESS:PHP_INI_STAGE_ACTIVATE) == FAILURE) {
+ phpapdebug((stderr, "..FAILED\n"));
+ }
+ }
+ }
+}
+
+const command_rec php_dir_cmds[] =
+{
+ AP_INIT_TAKE2("php_value", php_apache_value_handler, NULL, OR_OPTIONS, "PHP Value Modifier"),
+ AP_INIT_TAKE2("php_flag", php_apache_flag_handler, NULL, OR_OPTIONS, "PHP Flag Modifier"),
+ AP_INIT_TAKE2("php_admin_value", php_apache_admin_value_handler, NULL, ACCESS_CONF|RSRC_CONF, "PHP Value Modifier (Admin)"),
+ AP_INIT_TAKE2("php_admin_flag", php_apache_admin_flag_handler, NULL, ACCESS_CONF|RSRC_CONF, "PHP Flag Modifier (Admin)"),
+ AP_INIT_TAKE1("PHPINIDir", php_apache_phpini_set, NULL, RSRC_CONF, "Directory containing the php.ini file"),
+ {NULL}
+};
+
+static apr_status_t destroy_php_config(void *data)
+{
+ php_conf_rec *d = data;
+
+ phpapdebug((stderr, "Destroying config %p\n", data));
+ zend_hash_destroy(&d->config);
+
+ return APR_SUCCESS;
+}
+
+void *create_php_config(apr_pool_t *p, char *dummy)
+{
+ php_conf_rec *newx = (php_conf_rec *) apr_pcalloc(p, sizeof(*newx));
+
+ phpapdebug((stderr, "Creating new config (%p) for %s\n", newx, dummy));
+ zend_hash_init(&newx->config, 0, NULL, NULL, 1);
+ apr_pool_cleanup_register(p, newx, destroy_php_config, apr_pool_cleanup_null);
+ return (void *) newx;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/apache2handler/config.m4 b/sapi/apache2handler/config.m4
new file mode 100644
index 0000000..702f91f
--- /dev/null
+++ b/sapi/apache2handler/config.m4
@@ -0,0 +1,138 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(apxs2,,
+[ --with-apxs2[=FILE] Build shared Apache 2.0 Handler module. FILE is the optional
+ pathname to the Apache apxs tool [apxs]], no, no)
+
+AC_MSG_CHECKING([for Apache 2.0 handler-module support via DSO through APXS])
+
+if test "$PHP_APXS2" != "no"; then
+ if test "$PHP_APXS2" = "yes"; then
+ APXS=apxs
+ $APXS -q CFLAGS >/dev/null 2>&1
+ if test "$?" != "0" && test -x /usr/sbin/apxs; then
+ APXS=/usr/sbin/apxs
+ fi
+ else
+ PHP_EXPAND_PATH($PHP_APXS2, APXS)
+ fi
+
+ $APXS -q CFLAGS >/dev/null 2>&1
+ if test "$?" != "0"; then
+ AC_MSG_RESULT()
+ AC_MSG_RESULT()
+ AC_MSG_RESULT([Sorry, I cannot run apxs. Possible reasons follow:])
+ AC_MSG_RESULT()
+ AC_MSG_RESULT([1. Perl is not installed])
+ AC_MSG_RESULT([2. apxs was not found. Try to pass the path using --with-apxs2=/path/to/apxs])
+ AC_MSG_RESULT([3. Apache was not built using --enable-so (the apxs usage page is displayed)])
+ AC_MSG_RESULT()
+ AC_MSG_RESULT([The output of $APXS follows:])
+ $APXS -q CFLAGS
+ AC_MSG_ERROR([Aborting])
+ fi
+
+ APXS_INCLUDEDIR=`$APXS -q INCLUDEDIR`
+ APXS_BINDIR=`$APXS -q BINDIR`
+ APXS_HTTPD=`$APXS -q SBINDIR`/`$APXS -q TARGET`
+ APXS_CFLAGS=`$APXS -q CFLAGS`
+ APU_BINDIR=`$APXS -q APU_BINDIR`
+ APR_BINDIR=`$APXS -q APR_BINDIR`
+
+ # Pick up ap[ru]-N-config if using httpd >=2.1
+ APR_CONFIG=`$APXS -q APR_CONFIG 2>/dev/null ||
+ echo $APR_BINDIR/apr-config`
+ APU_CONFIG=`$APXS -q APU_CONFIG 2>/dev/null ||
+ echo $APU_BINDIR/apu-config`
+
+ APR_CFLAGS="`$APR_CONFIG --cppflags --includes`"
+ APU_CFLAGS="`$APU_CONFIG --includes`"
+
+ for flag in $APXS_CFLAGS; do
+ case $flag in
+ -D*) APACHE_CPPFLAGS="$APACHE_CPPFLAGS $flag";;
+ esac
+ done
+
+ APACHE_CFLAGS="$APACHE_CPPFLAGS -I$APXS_INCLUDEDIR $APR_CFLAGS $APU_CFLAGS"
+
+ # Test that we're trying to configure with apache 2.x
+ PHP_AP_EXTRACT_VERSION($APXS_HTTPD)
+ if test "$APACHE_VERSION" -le 2000000; then
+ AC_MSG_ERROR([You have enabled Apache 2 support while your server is Apache 1.3. Please use the appropiate switch --with-apxs (without the 2)])
+ elif test "$APACHE_VERSION" -lt 2000044; then
+ AC_MSG_ERROR([Please note that Apache version >= 2.0.44 is required])
+ fi
+
+ APXS_LIBEXECDIR='$(INSTALL_ROOT)'`$APXS -q LIBEXECDIR`
+ if test -z `$APXS -q SYSCONFDIR`; then
+ INSTALL_IT="\$(mkinstalldirs) '$APXS_LIBEXECDIR' && \
+ $APXS -S LIBEXECDIR='$APXS_LIBEXECDIR' \
+ -i -n php5"
+ else
+ APXS_SYSCONFDIR='$(INSTALL_ROOT)'`$APXS -q SYSCONFDIR`
+ INSTALL_IT="\$(mkinstalldirs) '$APXS_LIBEXECDIR' && \
+ \$(mkinstalldirs) '$APXS_SYSCONFDIR' && \
+ $APXS -S LIBEXECDIR='$APXS_LIBEXECDIR' \
+ -S SYSCONFDIR='$APXS_SYSCONFDIR' \
+ -i -a -n php5"
+ fi
+
+ case $host_alias in
+ *aix*)
+ EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-brtl -Wl,-bI:$APXS_LIBEXECDIR/httpd.exp"
+ PHP_SELECT_SAPI(apache2handler, shared, mod_php5.c sapi_apache2.c apache_config.c php_functions.c, $APACHE_CFLAGS)
+ INSTALL_IT="$INSTALL_IT $SAPI_LIBTOOL"
+ ;;
+ *darwin*)
+ dnl When using bundles on Darwin, we must resolve all symbols. However,
+ dnl the linker does not recursively look at the bundle loader and
+ dnl pull in its dependencies. Therefore, we must pull in the APR
+ dnl and APR-util libraries.
+ if test -x "$APR_CONFIG"; then
+ MH_BUNDLE_FLAGS="`$APR_CONFIG --ldflags --link-ld --libs`"
+ fi
+ if test -x "$APU_CONFIG"; then
+ MH_BUNDLE_FLAGS="`$APU_CONFIG --ldflags --link-ld --libs` $MH_BUNDLE_FLAGS"
+ fi
+ MH_BUNDLE_FLAGS="-bundle -bundle_loader $APXS_HTTPD $MH_BUNDLE_FLAGS"
+ PHP_SUBST(MH_BUNDLE_FLAGS)
+ PHP_SELECT_SAPI(apache2handler, bundle, mod_php5.c sapi_apache2.c apache_config.c php_functions.c, $APACHE_CFLAGS)
+ SAPI_SHARED=libs/libphp5.so
+ INSTALL_IT="$INSTALL_IT $SAPI_SHARED"
+ ;;
+ *beos*)
+ if test -f _APP_; then `rm _APP_`; fi
+ `ln -s $APXS_BINDIR/httpd _APP_`
+ EXTRA_LIBS="$EXTRA_LIBS _APP_"
+ PHP_SELECT_SAPI(apache2handler, shared, mod_php5.c sapi_apache2.c apache_config.c php_functions.c, $APACHE_CFLAGS)
+ INSTALL_IT="$INSTALL_IT $SAPI_LIBTOOL"
+ ;;
+ *)
+ PHP_SELECT_SAPI(apache2handler, shared, mod_php5.c sapi_apache2.c apache_config.c php_functions.c, $APACHE_CFLAGS)
+ INSTALL_IT="$INSTALL_IT $SAPI_LIBTOOL"
+ ;;
+ esac
+
+ if test "$APACHE_VERSION" -lt 2004001; then
+ APXS_MPM=`$APXS -q MPM_NAME`
+ if test "$APXS_MPM" != "prefork" && test "$APXS_MPM" != "peruser" && test "$APXS_MPM" != "itk"; then
+ PHP_BUILD_THREAD_SAFE
+ fi
+ else
+ APACHE_THREADED_MPM=`$APXS_HTTPD -V | grep 'threaded:.*yes'`
+ if test -n "$APACHE_THREADED_MPM"; then
+ PHP_BUILD_THREAD_SAFE
+ fi
+ fi
+ AC_MSG_RESULT(yes)
+ PHP_SUBST(APXS)
+else
+ AC_MSG_RESULT(no)
+fi
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/apache2handler/config.w32 b/sapi/apache2handler/config.w32
new file mode 100644
index 0000000..a754751
--- /dev/null
+++ b/sapi/apache2handler/config.w32
@@ -0,0 +1,58 @@
+// vim:ft=javascript
+// $Id$
+
+ARG_ENABLE('apache2handler', 'Build Apache 2.x handler', 'no');
+
+if (PHP_APACHE2HANDLER != "no") {
+ if (PHP_ZTS == "no") {
+ WARNING("Apache 2.0 module requires an --enable-zts build of PHP on windows");
+ } else if (CHECK_HEADER_ADD_INCLUDE("httpd.h", "CFLAGS_APACHE2HANDLER", PHP_PHP_BUILD + "\\include\\apache2") &&
+ CHECK_LIB("libhttpd.lib", "apache2handler", PHP_PHP_BUILD + "\\lib\\apache2") &&
+ CHECK_LIB("libapr.lib", "apache2handler", PHP_PHP_BUILD + "\\lib\\apache2") &&
+ CHECK_LIB("libaprutil.lib", "apache2handler", PHP_PHP_BUILD + "\\lib\\apache2")
+ ) {
+ SAPI('apache2handler', 'mod_php5.c sapi_apache2.c apache_config.c php_functions.c',
+ 'php' + PHP_VERSION + 'apache2.dll',
+ '/D PHP_APACHE2_EXPORTS /I win32');
+ } else {
+ WARNING("Could not find apache2 libraries/headers");
+ }
+}
+
+ARG_ENABLE('apache2-2handler', 'Build Apache 2.2.x handler', 'no');
+
+if (PHP_APACHE2_2HANDLER != "no") {
+ if (PHP_ZTS == "no") {
+ WARNING("Apache 2.2 module requires an --enable-zts build of PHP on windows");
+ } else if (CHECK_HEADER_ADD_INCLUDE("httpd.h", "CFLAGS_APACHE2_2HANDLER", PHP_PHP_BUILD + "\\include\\apache2_2") &&
+ CHECK_LIB("libhttpd.lib", "apache2_2handler", PHP_PHP_BUILD + "\\lib\\apache2_2") &&
+ CHECK_LIB("libapr-1.lib", "apache2_2handler", PHP_PHP_BUILD + "\\lib\\apache2_2") &&
+ CHECK_LIB("libaprutil-1.lib", "apache2_2handler", PHP_PHP_BUILD + "\\lib\\apache2_2")
+ ) {
+ SAPI('apache2_2handler', 'mod_php5.c sapi_apache2.c apache_config.c php_functions.c',
+ 'php' + PHP_VERSION + 'apache2_2.dll',
+ '/D PHP_APACHE2_EXPORTS /I win32',
+ 'sapi\\apache2_2handler');
+ } else {
+ WARNING("Could not find apache2.2 libraries/headers");
+ }
+}
+
+ARG_ENABLE('apache2-4handler', 'Build Apache 2.4.x handler', 'no');
+if (PHP_APACHE2_4HANDLER != "no") {
+ if (PHP_ZTS == "no") {
+ WARNING("Apache 2.4 module requires an --enable-zts build of PHP on windows");
+ } else if (CHECK_HEADER_ADD_INCLUDE("httpd.h", "CFLAGS_APACHE2_4HANDLER", PHP_PHP_BUILD + "\\include\\apache2_4") &&
+ CHECK_LIB("libhttpd.lib", "apache2_4handler", PHP_PHP_BUILD + "\\lib\\apache2_4") &&
+ CHECK_LIB("libapr-1.lib", "apache2_4handler", PHP_PHP_BUILD + "\\lib\\apache2_4") &&
+ CHECK_LIB("libaprutil-1.lib", "apache2_4handler", PHP_PHP_BUILD + "\\lib\\apache2_4")
+ ) {
+ SAPI('apache2_4handler', 'mod_php5.c sapi_apache2.c apache_config.c php_functions.c',
+ 'php' + PHP_VERSION + 'apache2_4.dll',
+ '/D PHP_APACHE2_EXPORTS /I win32',
+ 'sapi\\apache2handler');
+ } else {
+ WARNING("Could not find apache 2.4 libraries/headers");
+ }
+}
+
diff --git a/sapi/apache2handler/mod_php5.c b/sapi/apache2handler/mod_php5.c
new file mode 100644
index 0000000..56ef1bc
--- /dev/null
+++ b/sapi/apache2handler/mod_php5.c
@@ -0,0 +1,45 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Sascha Schumann <sascha@schumann.cx> |
+ | Parts based on Apache 1.3 SAPI module by |
+ | Rasmus Lerdorf and Zeev Suraski |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
+
+#include "php.h"
+#include "php_apache.h"
+
+AP_MODULE_DECLARE_DATA module php5_module = {
+ STANDARD20_MODULE_STUFF,
+ create_php_config, /* create per-directory config structure */
+ merge_php_config, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ php_dir_cmds, /* command apr_table_t */
+ php_ap2_register_hook /* register hooks */
+};
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/apache2handler/php.sym b/sapi/apache2handler/php.sym
new file mode 100644
index 0000000..9ad0f0a
--- /dev/null
+++ b/sapi/apache2handler/php.sym
@@ -0,0 +1 @@
+php5_module
diff --git a/sapi/apache2handler/php5apache2.dsp b/sapi/apache2handler/php5apache2.dsp
new file mode 100644
index 0000000..40cd58c
--- /dev/null
+++ b/sapi/apache2handler/php5apache2.dsp
@@ -0,0 +1,146 @@
+# Microsoft Developer Studio Project File - Name="php5apache2" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=php5apache2 - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "php5apache2.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "php5apache2.mak" CFG="php5apache2 - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "php5apache2 - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5apache2 - Win32 Release_TS_inline" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5apache2 - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "php5apache2 - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP_APACHE2_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\main" /I "..\..\TSRM" /D ZEND_DEBUG=0 /D "NDEBUG" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x407 /d "NDEBUG"
+# ADD RSC /l 0x407 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 php5ts.lib libhttpd.lib libapr.lib libaprutil.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /libpath:"..\..\Release_TS" /libpath:"..\..\TSRM\Release_TS" /libpath:"..\..\Zend\Release_TS"
+
+!ELSEIF "$(CFG)" == "php5apache2 - Win32 Release_TS_inline"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS_inline"
+# PROP BASE Intermediate_Dir "Release_TS_inline"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS_inline"
+# PROP Intermediate_Dir "Release_TS_inline"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP_APACHE2_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\main" /I "..\..\TSRM" /D ZEND_DEBUG=0 /D "ZEND_WIN32_FORCE_INLINE" /D "NDEBUG" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x407 /d "NDEBUG"
+# ADD RSC /l 0x407 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 php5ts.lib libhttpd.lib libapr.lib libaprutil.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"..\..\Release_TS_inline/php5apache2.dll" /libpath:"..\..\Release_TS_inline" /libpath:"..\..\TSRM\Release_TS_inline" /libpath:"..\..\Zend\Release_TS_inline"
+
+!ELSEIF "$(CFG)" == "php5apache2 - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\..\Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP_APACHE2_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\main" /I "..\..\TSRM" /D "_DEBUG" /D ZEND_DEBUG=1 /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "WIN32" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x407 /d "_DEBUG"
+# ADD RSC /l 0x407 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 php5ts_debug.lib libhttpd.lib libapr.lib libaprutil.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"..\..\Debug_TS" /libpath:"..\..\TSRM\Debug_TS" /libpath:"..\..\Zend\Debug_TS"
+
+!ENDIF
+
+# Begin Target
+
+# Name "php5apache2 - Win32 Release_TS"
+# Name "php5apache2 - Win32 Release_TS_inline"
+# Name "php5apache2 - Win32 Debug_TS"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\apache_config.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\mod_php5.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\php_functions.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sapi_apache2.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\php_apache.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/sapi/apache2handler/php_apache.h b/sapi/apache2handler/php_apache.h
new file mode 100644
index 0000000..8bc4608
--- /dev/null
+++ b/sapi/apache2handler/php_apache.h
@@ -0,0 +1,91 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#ifndef PHP_APACHE_H
+#define PHP_APACHE_H
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+
+/* Declare this so we can get to it from outside the sapi_apache2.c file */
+extern module AP_MODULE_DECLARE_DATA php5_module;
+
+/* A way to specify the location of the php.ini dir in an apache directive */
+extern char *apache2_php_ini_path_override;
+
+/* The server_context used by PHP */
+typedef struct php_struct {
+ int state;
+ request_rec *r;
+ apr_bucket_brigade *brigade;
+ /* stat structure of the current file */
+#if defined(NETWARE) && defined(CLIB_STAT_PATCH)
+ struct stat_libc finfo;
+#else
+ struct stat finfo;
+#endif
+ /* Whether or not we've processed PHP in the output filters yet. */
+ int request_processed;
+ /* final content type */
+ char *content_type;
+} php_struct;
+
+void *merge_php_config(apr_pool_t *p, void *base_conf, void *new_conf);
+void *create_php_config(apr_pool_t *p, char *dummy);
+char *get_php_config(void *conf, char *name, size_t name_len);
+void apply_config(void *);
+extern const command_rec php_dir_cmds[];
+void php_ap2_register_hook(apr_pool_t *p);
+
+#define APR_ARRAY_FOREACH_OPEN(arr, key, val) \
+{ \
+ apr_table_entry_t *elts; \
+ int i; \
+ elts = (apr_table_entry_t *) arr->elts; \
+ for (i = 0; i < arr->nelts; i++) { \
+ key = elts[i].key; \
+ val = elts[i].val;
+
+#define APR_ARRAY_FOREACH_CLOSE() }}
+
+typedef struct {
+ long engine;
+ long xbithack;
+ long last_modified;
+} php_apache2_info_struct;
+
+extern zend_module_entry apache2_module_entry;
+
+#ifdef ZTS
+extern int php_apache2_info_id;
+#define AP2(v) TSRMG(php_apache2_info_id, php_apache2_info_struct *, v)
+#else
+extern php_apache2_info_struct php_apache2_info;
+#define AP2(v) (php_apache2_info.v)
+#endif
+
+/* fix for gcc4 visibility patch */
+#ifndef PHP_WIN32
+# undef AP_MODULE_DECLARE_DATA
+# define AP_MODULE_DECLARE_DATA PHPAPI
+#endif
+
+#endif /* PHP_APACHE_H */
diff --git a/sapi/apache2handler/php_functions.c b/sapi/apache2handler/php_functions.c
new file mode 100644
index 0000000..1f79a53
--- /dev/null
+++ b/sapi/apache2handler/php_functions.c
@@ -0,0 +1,571 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
+
+#include "php.h"
+#include "ext/standard/php_smart_str.h"
+#include "ext/standard/info.h"
+#include "ext/standard/head.h"
+#include "php_ini.h"
+#include "SAPI.h"
+
+#define CORE_PRIVATE
+#include "apr_strings.h"
+#include "apr_time.h"
+#include "ap_config.h"
+#include "util_filter.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_script.h"
+#include "http_core.h"
+#include "ap_mpm.h"
+#if !defined(WIN32) && !defined(WINNT) && !defined(NETWARE)
+#include "unixd.h"
+#endif
+
+#include "php_apache.h"
+
+#ifdef ZTS
+int php_apache2_info_id;
+#else
+php_apache2_info_struct php_apache2_info;
+#endif
+
+#define SECTION(name) PUTS("<h2>" name "</h2>\n")
+
+static request_rec *php_apache_lookup_uri(char *filename TSRMLS_DC)
+{
+ php_struct *ctx = SG(server_context);
+
+ if (!filename || !ctx || !ctx->r) {
+ return NULL;
+ }
+
+ return ap_sub_req_lookup_uri(filename, ctx->r, ctx->r->output_filters);
+}
+
+/* {{{ proto bool virtual(string uri)
+ Perform an apache sub-request */
+PHP_FUNCTION(virtual)
+{
+ char *filename;
+ int filename_len;
+ request_rec *rr;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) {
+ return;
+ }
+
+ if (!(rr = php_apache_lookup_uri(filename TSRMLS_CC))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - URI lookup failed", filename);
+ RETURN_FALSE;
+ }
+
+ if (rr->status != HTTP_OK) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - error finding URI", filename);
+ ap_destroy_sub_req(rr);
+ RETURN_FALSE;
+ }
+
+ /* Flush everything. */
+ php_output_end_all(TSRMLS_C);
+ php_header(TSRMLS_C);
+
+ /* Ensure that the ap_r* layer for the main request is flushed, to
+ * work around http://issues.apache.org/bugzilla/show_bug.cgi?id=17629 */
+ ap_rflush(rr->main);
+
+ if (ap_run_sub_req(rr)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - request execution failed", filename);
+ ap_destroy_sub_req(rr);
+ RETURN_FALSE;
+ }
+ ap_destroy_sub_req(rr);
+ RETURN_TRUE;
+}
+/* }}} */
+
+#define ADD_LONG(name) \
+ add_property_long(return_value, #name, rr->name)
+#define ADD_TIME(name) \
+ add_property_long(return_value, #name, apr_time_sec(rr->name));
+#define ADD_STRING(name) \
+ if (rr->name) add_property_string(return_value, #name, (char *) rr->name, 1)
+
+PHP_FUNCTION(apache_lookup_uri)
+{
+ request_rec *rr;
+ char *filename;
+ int filename_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) {
+ return;
+ }
+
+ if (!(rr = php_apache_lookup_uri(filename TSRMLS_CC))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - URI lookup failed", filename);
+ RETURN_FALSE;
+ }
+
+ if (rr->status == HTTP_OK) {
+ object_init(return_value);
+
+ ADD_LONG(status);
+ ADD_STRING(the_request);
+ ADD_STRING(status_line);
+ ADD_STRING(method);
+ ADD_TIME(mtime);
+ ADD_LONG(clength);
+#if MODULE_MAGIC_NUMBER < 20020506
+ ADD_STRING(boundary);
+#endif
+ ADD_STRING(range);
+ ADD_LONG(chunked);
+ ADD_STRING(content_type);
+ ADD_STRING(handler);
+ ADD_LONG(no_cache);
+ ADD_LONG(no_local_copy);
+ ADD_STRING(unparsed_uri);
+ ADD_STRING(uri);
+ ADD_STRING(filename);
+ ADD_STRING(path_info);
+ ADD_STRING(args);
+ ADD_LONG(allowed);
+ ADD_LONG(sent_bodyct);
+ ADD_LONG(bytes_sent);
+ ADD_LONG(mtime);
+ ADD_TIME(request_time);
+
+ ap_destroy_sub_req(rr);
+ return;
+ }
+
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include '%s' - error finding URI", filename);
+ ap_destroy_sub_req(rr);
+ RETURN_FALSE;
+}
+
+/* {{{ proto array getallheaders(void)
+ Fetch all HTTP request headers */
+PHP_FUNCTION(apache_request_headers)
+{
+ php_struct *ctx;
+ const apr_array_header_t *arr;
+ char *key, *val;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ array_init(return_value);
+
+ ctx = SG(server_context);
+ arr = apr_table_elts(ctx->r->headers_in);
+
+ APR_ARRAY_FOREACH_OPEN(arr, key, val)
+ if (!val) val = "";
+ add_assoc_string(return_value, key, val, 1);
+ APR_ARRAY_FOREACH_CLOSE()
+}
+/* }}} */
+
+/* {{{ proto array apache_response_headers(void)
+ Fetch all HTTP response headers */
+PHP_FUNCTION(apache_response_headers)
+{
+ php_struct *ctx;
+ const apr_array_header_t *arr;
+ char *key, *val;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ array_init(return_value);
+
+ ctx = SG(server_context);
+ arr = apr_table_elts(ctx->r->headers_out);
+
+ APR_ARRAY_FOREACH_OPEN(arr, key, val)
+ if (!val) val = "";
+ add_assoc_string(return_value, key, val, 1);
+ APR_ARRAY_FOREACH_CLOSE()
+}
+/* }}} */
+
+/* {{{ proto string apache_note(string note_name [, string note_value])
+ Get and set Apache request notes */
+PHP_FUNCTION(apache_note)
+{
+ php_struct *ctx;
+ char *note_name, *note_val = NULL;
+ int note_name_len, note_val_len;
+ char *old_note_val=NULL;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &note_name, &note_name_len, &note_val, &note_val_len) == FAILURE) {
+ return;
+ }
+
+ ctx = SG(server_context);
+
+ old_note_val = (char *) apr_table_get(ctx->r->notes, note_name);
+
+ if (note_val) {
+ apr_table_set(ctx->r->notes, note_name, note_val);
+ }
+
+ if (old_note_val) {
+ RETURN_STRING(old_note_val, 1);
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+
+/* {{{ proto bool apache_setenv(string variable, string value [, bool walk_to_top])
+ Set an Apache subprocess_env variable */
+/*
+ * XXX this doesn't look right. shouldn't it be the parent ?*/
+PHP_FUNCTION(apache_setenv)
+{
+ php_struct *ctx;
+ char *variable=NULL, *string_val=NULL;
+ int variable_len, string_val_len;
+ zend_bool walk_to_top = 0;
+ int arg_count = ZEND_NUM_ARGS();
+ request_rec *r;
+
+ if (zend_parse_parameters(arg_count TSRMLS_CC, "ss|b", &variable, &variable_len, &string_val, &string_val_len, &walk_to_top) == FAILURE) {
+ return;
+ }
+
+ ctx = SG(server_context);
+
+ r = ctx->r;
+ if (arg_count == 3) {
+ if (walk_to_top) {
+ while(r->prev) {
+ r = r->prev;
+ }
+ }
+ }
+
+ apr_table_set(r->subprocess_env, variable, string_val);
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool apache_getenv(string variable [, bool walk_to_top])
+ Get an Apache subprocess_env variable */
+/*
+ * XXX: shouldn't this be the parent not the 'prev'
+ */
+PHP_FUNCTION(apache_getenv)
+{
+ php_struct *ctx;
+ char *variable=NULL;
+ int variable_len;
+ zend_bool walk_to_top = 0;
+ int arg_count = ZEND_NUM_ARGS();
+ char *env_val=NULL;
+ request_rec *r;
+
+ if (zend_parse_parameters(arg_count TSRMLS_CC, "s|b", &variable, &variable_len, &walk_to_top) == FAILURE) {
+ return;
+ }
+
+ ctx = SG(server_context);
+
+ r = ctx->r;
+ if (arg_count == 2) {
+ if (walk_to_top) {
+ while(r->prev) {
+ r = r->prev;
+ }
+ }
+ }
+
+ env_val = (char*) apr_table_get(r->subprocess_env, variable);
+
+ if (env_val != NULL) {
+ RETURN_STRING(env_val, 1);
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+static char *php_apache_get_version()
+{
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20060905
+ return (char *) ap_get_server_banner();
+#else
+ return (char *) ap_get_server_version();
+#endif
+}
+
+/* {{{ proto string apache_get_version(void)
+ Fetch Apache version */
+PHP_FUNCTION(apache_get_version)
+{
+ char *apv = php_apache_get_version();
+
+ if (apv && *apv) {
+ RETURN_STRING(apv, 1);
+ } else {
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ proto array apache_get_modules(void)
+ Get a list of loaded Apache modules */
+PHP_FUNCTION(apache_get_modules)
+{
+ int n;
+ char *p;
+
+ array_init(return_value);
+
+ for (n = 0; ap_loaded_modules[n]; ++n) {
+ char *s = (char *) ap_loaded_modules[n]->name;
+ if ((p = strchr(s, '.'))) {
+ add_next_index_stringl(return_value, s, (p - s), 1);
+ } else {
+ add_next_index_string(return_value, s, 1);
+ }
+ }
+}
+/* }}} */
+
+PHP_MINFO_FUNCTION(apache)
+{
+ char *apv = php_apache_get_version();
+ smart_str tmp1 = {0};
+ char tmp[1024];
+ int n, max_requests;
+ char *p;
+ server_rec *serv = ((php_struct *) SG(server_context))->r->server;
+#if !defined(WIN32) && !defined(WINNT) && !defined(NETWARE)
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20081201
+ AP_DECLARE_DATA extern unixd_config_rec ap_unixd_config;
+#else
+ AP_DECLARE_DATA extern unixd_config_rec unixd_config;
+#endif
+#endif
+
+ for (n = 0; ap_loaded_modules[n]; ++n) {
+ char *s = (char *) ap_loaded_modules[n]->name;
+ if ((p = strchr(s, '.'))) {
+ smart_str_appendl(&tmp1, s, (p - s));
+ } else {
+ smart_str_appends(&tmp1, s);
+ }
+ smart_str_appendc(&tmp1, ' ');
+ }
+ if ((tmp1.len - 1) >= 0) {
+ tmp1.c[tmp1.len - 1] = '\0';
+ }
+
+ php_info_print_table_start();
+ if (apv && *apv) {
+ php_info_print_table_row(2, "Apache Version", apv);
+ }
+ snprintf(tmp, sizeof(tmp), "%d", MODULE_MAGIC_NUMBER);
+ php_info_print_table_row(2, "Apache API Version", tmp);
+
+ if (serv->server_admin && *(serv->server_admin)) {
+ php_info_print_table_row(2, "Server Administrator", serv->server_admin);
+ }
+
+ snprintf(tmp, sizeof(tmp), "%s:%u", serv->server_hostname, serv->port);
+ php_info_print_table_row(2, "Hostname:Port", tmp);
+
+#if !defined(WIN32) && !defined(WINNT) && !defined(NETWARE)
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20081201
+ snprintf(tmp, sizeof(tmp), "%s(%d)/%d", ap_unixd_config.user_name, ap_unixd_config.user_id, ap_unixd_config.group_id);
+#else
+ snprintf(tmp, sizeof(tmp), "%s(%d)/%d", unixd_config.user_name, unixd_config.user_id, unixd_config.group_id);
+#endif
+ php_info_print_table_row(2, "User/Group", tmp);
+#endif
+
+ ap_mpm_query(AP_MPMQ_MAX_REQUESTS_DAEMON, &max_requests);
+ snprintf(tmp, sizeof(tmp), "Per Child: %d - Keep Alive: %s - Max Per Connection: %d", max_requests, (serv->keep_alive ? "on":"off"), serv->keep_alive_max);
+ php_info_print_table_row(2, "Max Requests", tmp);
+
+ apr_snprintf(tmp, sizeof tmp,
+ "Connection: %" APR_TIME_T_FMT " - Keep-Alive: %" APR_TIME_T_FMT,
+ apr_time_sec(serv->timeout), apr_time_sec(serv->keep_alive_timeout));
+ php_info_print_table_row(2, "Timeouts", tmp);
+
+ php_info_print_table_row(2, "Virtual Server", (serv->is_virtual ? "Yes" : "No"));
+ php_info_print_table_row(2, "Server Root", ap_server_root);
+ php_info_print_table_row(2, "Loaded Modules", tmp1.c);
+
+ smart_str_free(&tmp1);
+ php_info_print_table_end();
+
+ DISPLAY_INI_ENTRIES();
+
+ {
+ const apr_array_header_t *arr = apr_table_elts(((php_struct *) SG(server_context))->r->subprocess_env);
+ char *key, *val;
+
+ SECTION("Apache Environment");
+ php_info_print_table_start();
+ php_info_print_table_header(2, "Variable", "Value");
+ APR_ARRAY_FOREACH_OPEN(arr, key, val)
+ if (!val) {
+ val = "";
+ }
+ php_info_print_table_row(2, key, val);
+ APR_ARRAY_FOREACH_CLOSE()
+
+ php_info_print_table_end();
+
+ SECTION("HTTP Headers Information");
+ php_info_print_table_start();
+ php_info_print_table_colspan_header(2, "HTTP Request Headers");
+ php_info_print_table_row(2, "HTTP Request", ((php_struct *) SG(server_context))->r->the_request);
+
+ arr = apr_table_elts(((php_struct *) SG(server_context))->r->headers_in);
+ APR_ARRAY_FOREACH_OPEN(arr, key, val)
+ if (!val) {
+ val = "";
+ }
+ php_info_print_table_row(2, key, val);
+ APR_ARRAY_FOREACH_CLOSE()
+
+ php_info_print_table_colspan_header(2, "HTTP Response Headers");
+ arr = apr_table_elts(((php_struct *) SG(server_context))->r->headers_out);
+ APR_ARRAY_FOREACH_OPEN(arr, key, val)
+ if (!val) {
+ val = "";
+ }
+ php_info_print_table_row(2, key, val);
+ APR_ARRAY_FOREACH_CLOSE()
+
+ php_info_print_table_end();
+ }
+}
+
+/* {{{ arginfo */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_lookup_uri, 0, 0, 1)
+ ZEND_ARG_INFO(0, filename)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_virtual, 0, 0, 1)
+ ZEND_ARG_INFO(0, uri)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache2handler_response_headers, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache2handler_getallheaders, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_note, 0, 0, 1)
+ ZEND_ARG_INFO(0, note_name)
+ ZEND_ARG_INFO(0, note_value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_setenv, 0, 0, 2)
+ ZEND_ARG_INFO(0, variable)
+ ZEND_ARG_INFO(0, value)
+ ZEND_ARG_INFO(0, walk_to_top)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apache2handler_getenv, 0, 0, 1)
+ ZEND_ARG_INFO(0, variable)
+ ZEND_ARG_INFO(0, walk_to_top)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache2handler_get_version, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apache2handler_get_modules, 0)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+static const zend_function_entry apache_functions[] = {
+ PHP_FE(apache_lookup_uri, arginfo_apache2handler_lookup_uri)
+ PHP_FE(virtual, arginfo_apache2handler_virtual)
+ PHP_FE(apache_request_headers, arginfo_apache2handler_getallheaders)
+ PHP_FE(apache_response_headers, arginfo_apache2handler_response_headers)
+ PHP_FE(apache_setenv, arginfo_apache2handler_setenv)
+ PHP_FE(apache_getenv, arginfo_apache2handler_getenv)
+ PHP_FE(apache_note, arginfo_apache2handler_note)
+ PHP_FE(apache_get_version, arginfo_apache2handler_get_version)
+ PHP_FE(apache_get_modules, arginfo_apache2handler_get_modules)
+ PHP_FALIAS(getallheaders, apache_request_headers, arginfo_apache2handler_getallheaders)
+ {NULL, NULL, NULL}
+};
+
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY("xbithack", "0", PHP_INI_ALL, OnUpdateLong, xbithack, php_apache2_info_struct, php_apache2_info)
+ STD_PHP_INI_ENTRY("engine", "1", PHP_INI_ALL, OnUpdateLong, engine, php_apache2_info_struct, php_apache2_info)
+ STD_PHP_INI_ENTRY("last_modified", "0", PHP_INI_ALL, OnUpdateLong, last_modified, php_apache2_info_struct, php_apache2_info)
+PHP_INI_END()
+
+static PHP_MINIT_FUNCTION(apache)
+{
+#ifdef ZTS
+ ts_allocate_id(&php_apache2_info_id, sizeof(php_apache2_info_struct), (ts_allocate_ctor) NULL, NULL);
+#endif
+ REGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+
+static PHP_MSHUTDOWN_FUNCTION(apache)
+{
+ UNREGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+
+zend_module_entry php_apache_module = {
+ STANDARD_MODULE_HEADER,
+ "apache2handler",
+ apache_functions,
+ PHP_MINIT(apache),
+ PHP_MSHUTDOWN(apache),
+ NULL,
+ NULL,
+ PHP_MINFO(apache),
+ NULL,
+ STANDARD_MODULE_PROPERTIES
+};
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/apache2handler/sapi_apache2.c b/sapi/apache2handler/sapi_apache2.c
new file mode 100644
index 0000000..bcb2443
--- /dev/null
+++ b/sapi/apache2handler/sapi_apache2.c
@@ -0,0 +1,718 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Sascha Schumann <sascha@schumann.cx> |
+ | Parts based on Apache 1.3 SAPI module by |
+ | Rasmus Lerdorf and Zeev Suraski |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
+
+#include "php.h"
+#include "php_main.h"
+#include "php_ini.h"
+#include "php_variables.h"
+#include "SAPI.h"
+
+#include <fcntl.h>
+
+#include "ext/standard/php_smart_str.h"
+#ifndef NETWARE
+#include "ext/standard/php_standard.h"
+#else
+#include "ext/standard/basic_functions.h"
+#endif
+
+#include "apr_strings.h"
+#include "ap_config.h"
+#include "util_filter.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_script.h"
+#include "http_core.h"
+#include "ap_mpm.h"
+
+#include "php_apache.h"
+
+#ifdef PHP_WIN32
+# if _MSC_VER <= 1300
+# include "win32/php_strtoi64.h"
+# endif
+#endif
+
+/* UnixWare and Netware define shutdown to _shutdown, which causes problems later
+ * on when using a structure member named shutdown. Since this source
+ * file does not use the system call shutdown, it is safe to #undef it.K
+ */
+#undef shutdown
+
+#define PHP_MAGIC_TYPE "application/x-httpd-php"
+#define PHP_SOURCE_MAGIC_TYPE "application/x-httpd-php-source"
+#define PHP_SCRIPT "php5-script"
+
+/* A way to specify the location of the php.ini dir in an apache directive */
+char *apache2_php_ini_path_override = NULL;
+
+static int
+php_apache_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ request_rec *r;
+ php_struct *ctx;
+
+ ctx = SG(server_context);
+ r = ctx->r;
+
+ if (ap_rwrite(str, str_length, r) < 0) {
+ php_handle_aborted_connection();
+ }
+
+ return str_length; /* we always consume all the data passed to us. */
+}
+
+static int
+php_apache_sapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ php_struct *ctx;
+ char *val, *ptr;
+
+ ctx = SG(server_context);
+
+ switch (op) {
+ case SAPI_HEADER_DELETE:
+ apr_table_unset(ctx->r->headers_out, sapi_header->header);
+ return 0;
+
+ case SAPI_HEADER_DELETE_ALL:
+ apr_table_clear(ctx->r->headers_out);
+ return 0;
+
+ case SAPI_HEADER_ADD:
+ case SAPI_HEADER_REPLACE:
+ val = strchr(sapi_header->header, ':');
+
+ if (!val) {
+ return 0;
+ }
+ ptr = val;
+
+ *val = '\0';
+
+ do {
+ val++;
+ } while (*val == ' ');
+
+ if (!strcasecmp(sapi_header->header, "content-type")) {
+ if (ctx->content_type) {
+ efree(ctx->content_type);
+ }
+ ctx->content_type = estrdup(val);
+ } else if (!strcasecmp(sapi_header->header, "content-length")) {
+#ifdef PHP_WIN32
+# ifdef APR_HAS_LARGE_FILES
+ ap_set_content_length(ctx->r, (apr_off_t) _strtoui64(val, (char **)NULL, 10));
+# else
+ ap_set_content_length(ctx->r, (apr_off_t) strtol(val, (char **)NULL, 10));
+# endif
+#else
+ ap_set_content_length(ctx->r, (apr_off_t) strtol(val, (char **)NULL, 10));
+#endif
+ } else if (op == SAPI_HEADER_REPLACE) {
+ apr_table_set(ctx->r->headers_out, sapi_header->header, val);
+ } else {
+ apr_table_add(ctx->r->headers_out, sapi_header->header, val);
+ }
+
+ *ptr = ':';
+
+ return SAPI_HEADER_ADD;
+
+ default:
+ return 0;
+ }
+}
+
+static int
+php_apache_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ php_struct *ctx = SG(server_context);
+ const char *sline = SG(sapi_headers).http_status_line;
+
+ ctx->r->status = SG(sapi_headers).http_response_code;
+
+ /* httpd requires that r->status_line is set to the first digit of
+ * the status-code: */
+ if (sline && strlen(sline) > 12 && strncmp(sline, "HTTP/1.", 7) == 0 && sline[8] == ' ') {
+ ctx->r->status_line = apr_pstrdup(ctx->r->pool, sline + 9);
+ ctx->r->proto_num = 1000 + (sline[7]-'0');
+ if ((sline[7]-'0') == 0) {
+ apr_table_set(ctx->r->subprocess_env, "force-response-1.0", "true");
+ }
+ }
+
+ /* call ap_set_content_type only once, else each time we call it,
+ configured output filters for that content type will be added */
+ if (!ctx->content_type) {
+ ctx->content_type = sapi_get_default_content_type(TSRMLS_C);
+ }
+ ap_set_content_type(ctx->r, apr_pstrdup(ctx->r->pool, ctx->content_type));
+ efree(ctx->content_type);
+ ctx->content_type = NULL;
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+static int
+php_apache_sapi_read_post(char *buf, uint count_bytes TSRMLS_DC)
+{
+ apr_size_t len, tlen=0;
+ php_struct *ctx = SG(server_context);
+ request_rec *r;
+ apr_bucket_brigade *brigade;
+
+ r = ctx->r;
+ brigade = ctx->brigade;
+ len = count_bytes;
+
+ /*
+ * This loop is needed because ap_get_brigade() can return us partial data
+ * which would cause premature termination of request read. Therefor we
+ * need to make sure that if data is available we fill the buffer completely.
+ */
+
+ while (ap_get_brigade(r->input_filters, brigade, AP_MODE_READBYTES, APR_BLOCK_READ, len) == APR_SUCCESS) {
+ apr_brigade_flatten(brigade, buf, &len);
+ apr_brigade_cleanup(brigade);
+ tlen += len;
+ if (tlen == count_bytes || !len) {
+ break;
+ }
+ buf += len;
+ len = count_bytes - tlen;
+ }
+
+ return tlen;
+}
+
+static struct stat*
+php_apache_sapi_get_stat(TSRMLS_D)
+{
+ php_struct *ctx = SG(server_context);
+
+ ctx->finfo.st_uid = ctx->r->finfo.user;
+ ctx->finfo.st_gid = ctx->r->finfo.group;
+ ctx->finfo.st_dev = ctx->r->finfo.device;
+ ctx->finfo.st_ino = ctx->r->finfo.inode;
+#if defined(NETWARE) && defined(CLIB_STAT_PATCH)
+ ctx->finfo.st_atime.tv_sec = apr_time_sec(ctx->r->finfo.atime);
+ ctx->finfo.st_mtime.tv_sec = apr_time_sec(ctx->r->finfo.mtime);
+ ctx->finfo.st_ctime.tv_sec = apr_time_sec(ctx->r->finfo.ctime);
+#else
+ ctx->finfo.st_atime = apr_time_sec(ctx->r->finfo.atime);
+ ctx->finfo.st_mtime = apr_time_sec(ctx->r->finfo.mtime);
+ ctx->finfo.st_ctime = apr_time_sec(ctx->r->finfo.ctime);
+#endif
+
+ ctx->finfo.st_size = ctx->r->finfo.size;
+ ctx->finfo.st_nlink = ctx->r->finfo.nlink;
+
+ return &ctx->finfo;
+}
+
+static char *
+php_apache_sapi_read_cookies(TSRMLS_D)
+{
+ php_struct *ctx = SG(server_context);
+ const char *http_cookie;
+
+ http_cookie = apr_table_get(ctx->r->headers_in, "cookie");
+
+ /* The SAPI interface should use 'const char *' */
+ return (char *) http_cookie;
+}
+
+static char *
+php_apache_sapi_getenv(char *name, size_t name_len TSRMLS_DC)
+{
+ php_struct *ctx = SG(server_context);
+ const char *env_var;
+
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ env_var = apr_table_get(ctx->r->subprocess_env, name);
+
+ return (char *) env_var;
+}
+
+static void
+php_apache_sapi_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ php_struct *ctx = SG(server_context);
+ const apr_array_header_t *arr = apr_table_elts(ctx->r->subprocess_env);
+ char *key, *val;
+ int new_val_len;
+
+ APR_ARRAY_FOREACH_OPEN(arr, key, val)
+ if (!val) {
+ val = "";
+ }
+ if (sapi_module.input_filter(PARSE_SERVER, key, &val, strlen(val), &new_val_len TSRMLS_CC)) {
+ php_register_variable_safe(key, val, new_val_len, track_vars_array TSRMLS_CC);
+ }
+ APR_ARRAY_FOREACH_CLOSE()
+
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &ctx->r->uri, strlen(ctx->r->uri), &new_val_len TSRMLS_CC)) {
+ php_register_variable_safe("PHP_SELF", ctx->r->uri, new_val_len, track_vars_array TSRMLS_CC);
+ }
+}
+
+static void
+php_apache_sapi_flush(void *server_context)
+{
+ php_struct *ctx;
+ request_rec *r;
+ TSRMLS_FETCH();
+
+ ctx = server_context;
+
+ /* If we haven't registered a server_context yet,
+ * then don't bother flushing. */
+ if (!server_context) {
+ return;
+ }
+
+ r = ctx->r;
+
+ sapi_send_headers(TSRMLS_C);
+
+ r->status = SG(sapi_headers).http_response_code;
+ SG(headers_sent) = 1;
+
+ if (ap_rflush(r) < 0 || r->connection->aborted) {
+ php_handle_aborted_connection();
+ }
+}
+
+static void php_apache_sapi_log_message(char *msg TSRMLS_DC)
+{
+ php_struct *ctx;
+
+ ctx = SG(server_context);
+
+ if (ctx == NULL) { /* we haven't initialized our ctx yet, oh well */
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, 0, NULL, "%s", msg);
+ } else {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, ctx->r, "%s", msg);
+ }
+}
+
+static void php_apache_sapi_log_message_ex(char *msg, request_rec *r TSRMLS_DC)
+{
+ if (r) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, msg, r->filename);
+ } else {
+ php_apache_sapi_log_message(msg TSRMLS_CC);
+ }
+}
+
+static double php_apache_sapi_get_request_time(TSRMLS_D)
+{
+ php_struct *ctx = SG(server_context);
+ return ((double) apr_time_as_msec(ctx->r->request_time)) / 1000.0;
+}
+
+extern zend_module_entry php_apache_module;
+
+static int php_apache2_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &php_apache_module, 1)==FAILURE) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static sapi_module_struct apache2_sapi_module = {
+ "apache2handler",
+ "Apache 2.0 Handler",
+
+ php_apache2_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ php_apache_sapi_ub_write, /* unbuffered write */
+ php_apache_sapi_flush, /* flush */
+ php_apache_sapi_get_stat, /* get uid */
+ php_apache_sapi_getenv, /* getenv */
+
+ php_error, /* error handler */
+
+ php_apache_sapi_header_handler, /* header handler */
+ php_apache_sapi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ php_apache_sapi_read_post, /* read POST data */
+ php_apache_sapi_read_cookies, /* read Cookies */
+
+ php_apache_sapi_register_variables,
+ php_apache_sapi_log_message, /* Log message */
+ php_apache_sapi_get_request_time, /* Request Time */
+ NULL, /* Child Terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+static apr_status_t php_apache_server_shutdown(void *tmp)
+{
+ apache2_sapi_module.shutdown(&apache2_sapi_module);
+ sapi_shutdown();
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ return APR_SUCCESS;
+}
+
+static apr_status_t php_apache_child_shutdown(void *tmp)
+{
+ apache2_sapi_module.shutdown(&apache2_sapi_module);
+#if defined(ZTS) && !defined(PHP_WIN32)
+ tsrm_shutdown();
+#endif
+ return APR_SUCCESS;
+}
+
+static void php_apache_add_version(apr_pool_t *p)
+{
+ TSRMLS_FETCH();
+ if (PG(expose_php)) {
+ ap_add_version_component(p, "PHP/" PHP_VERSION);
+ }
+}
+
+static int php_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
+{
+#ifndef ZTS
+ int threaded_mpm;
+
+ ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm);
+ if(threaded_mpm) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, 0, 0, "Apache is running a threaded MPM, but your PHP Module is not compiled to be threadsafe. You need to recompile PHP.");
+ return DONE;
+ }
+#endif
+ /* When this is NULL, apache won't override the hard-coded default
+ * php.ini path setting. */
+ apache2_php_ini_path_override = NULL;
+ return OK;
+}
+
+static int
+php_apache_server_startup(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
+{
+ void *data = NULL;
+ const char *userdata_key = "apache2hook_post_config";
+
+ /* Apache will load, unload and then reload a DSO module. This
+ * prevents us from starting PHP until the second load. */
+ apr_pool_userdata_get(&data, userdata_key, s->process->pool);
+ if (data == NULL) {
+ /* We must use set() here and *not* setn(), otherwise the
+ * static string pointed to by userdata_key will be mapped
+ * to a different location when the DSO is reloaded and the
+ * pointers won't match, causing get() to return NULL when
+ * we expected it to return non-NULL. */
+ apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool);
+ return OK;
+ }
+
+ /* Set up our overridden path. */
+ if (apache2_php_ini_path_override) {
+ apache2_sapi_module.php_ini_path_override = apache2_php_ini_path_override;
+ }
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+#endif
+ sapi_startup(&apache2_sapi_module);
+ apache2_sapi_module.startup(&apache2_sapi_module);
+ apr_pool_cleanup_register(pconf, NULL, php_apache_server_shutdown, apr_pool_cleanup_null);
+ php_apache_add_version(pconf);
+
+ return OK;
+}
+
+static apr_status_t php_server_context_cleanup(void *data_)
+{
+ void **data = data_;
+ *data = NULL;
+ return APR_SUCCESS;
+}
+
+static int php_apache_request_ctor(request_rec *r, php_struct *ctx TSRMLS_DC)
+{
+ char *content_length;
+ const char *auth;
+
+ SG(sapi_headers).http_response_code = !r->status ? HTTP_OK : r->status;
+ SG(request_info).content_type = apr_table_get(r->headers_in, "Content-Type");
+ SG(request_info).query_string = apr_pstrdup(r->pool, r->args);
+ SG(request_info).request_method = r->method;
+ SG(request_info).proto_num = r->proto_num;
+ SG(request_info).request_uri = apr_pstrdup(r->pool, r->uri);
+ SG(request_info).path_translated = apr_pstrdup(r->pool, r->filename);
+ r->no_local_copy = 1;
+
+ content_length = (char *) apr_table_get(r->headers_in, "Content-Length");
+ SG(request_info).content_length = (content_length ? atol(content_length) : 0);
+
+ apr_table_unset(r->headers_out, "Content-Length");
+ apr_table_unset(r->headers_out, "Last-Modified");
+ apr_table_unset(r->headers_out, "Expires");
+ apr_table_unset(r->headers_out, "ETag");
+
+ auth = apr_table_get(r->headers_in, "Authorization");
+ php_handle_auth_data(auth TSRMLS_CC);
+
+ if (SG(request_info).auth_user == NULL && r->user) {
+ SG(request_info).auth_user = estrdup(r->user);
+ }
+
+ ctx->r->user = apr_pstrdup(ctx->r->pool, SG(request_info).auth_user);
+
+ return php_request_startup(TSRMLS_C);
+}
+
+static void php_apache_request_dtor(request_rec *r TSRMLS_DC)
+{
+ php_request_shutdown(NULL);
+}
+
+static void php_apache_ini_dtor(request_rec *r, request_rec *p TSRMLS_DC)
+{
+ if (strcmp(r->protocol, "INCLUDED")) {
+ zend_try { zend_ini_deactivate(TSRMLS_C); } zend_end_try();
+ } else {
+typedef struct {
+ HashTable config;
+} php_conf_rec;
+ char *str;
+ uint str_len;
+ php_conf_rec *c = ap_get_module_config(r->per_dir_config, &php5_module);
+
+ for (zend_hash_internal_pointer_reset(&c->config);
+ zend_hash_get_current_key_ex(&c->config, &str, &str_len, NULL, 0, NULL) == HASH_KEY_IS_STRING;
+ zend_hash_move_forward(&c->config)
+ ) {
+ zend_restore_ini_entry(str, str_len, ZEND_INI_STAGE_SHUTDOWN);
+ }
+ }
+ if (p) {
+ ((php_struct *)SG(server_context))->r = p;
+ } else {
+ apr_pool_cleanup_run(r->pool, (void *)&SG(server_context), php_server_context_cleanup);
+ }
+}
+
+static int php_handler(request_rec *r)
+{
+ php_struct * volatile ctx;
+ void *conf;
+ apr_bucket_brigade * volatile brigade;
+ apr_bucket *bucket;
+ apr_status_t rv;
+ request_rec * volatile parent_req = NULL;
+ TSRMLS_FETCH();
+
+#define PHPAP_INI_OFF php_apache_ini_dtor(r, parent_req TSRMLS_CC);
+
+ conf = ap_get_module_config(r->per_dir_config, &php5_module);
+
+ /* apply_config() needs r in some cases, so allocate server_context early */
+ ctx = SG(server_context);
+ if (ctx == NULL || (ctx && ctx->request_processed && !strcmp(r->protocol, "INCLUDED"))) {
+normal:
+ ctx = SG(server_context) = apr_pcalloc(r->pool, sizeof(*ctx));
+ /* register a cleanup so we clear out the SG(server_context)
+ * after each request. Note: We pass in the pointer to the
+ * server_context in case this is handled by a different thread.
+ */
+ apr_pool_cleanup_register(r->pool, (void *)&SG(server_context), php_server_context_cleanup, apr_pool_cleanup_null);
+ ctx->r = r;
+ ctx = NULL; /* May look weird to null it here, but it is to catch the right case in the first_try later on */
+ } else {
+ parent_req = ctx->r;
+ ctx->r = r;
+ }
+ apply_config(conf);
+
+ if (strcmp(r->handler, PHP_MAGIC_TYPE) && strcmp(r->handler, PHP_SOURCE_MAGIC_TYPE) && strcmp(r->handler, PHP_SCRIPT)) {
+ /* Check for xbithack in this case. */
+ if (!AP2(xbithack) || strcmp(r->handler, "text/html") || !(r->finfo.protection & APR_UEXECUTE)) {
+ PHPAP_INI_OFF;
+ return DECLINED;
+ }
+ }
+
+ /* Give a 404 if PATH_INFO is used but is explicitly disabled in
+ * the configuration; default behaviour is to accept. */
+ if (r->used_path_info == AP_REQ_REJECT_PATH_INFO
+ && r->path_info && r->path_info[0]) {
+ PHPAP_INI_OFF;
+ return HTTP_NOT_FOUND;
+ }
+
+ /* handle situations where user turns the engine off */
+ if (!AP2(engine)) {
+ PHPAP_INI_OFF;
+ return DECLINED;
+ }
+
+ if (r->finfo.filetype == 0) {
+ php_apache_sapi_log_message_ex("script '%s' not found or unable to stat", r TSRMLS_CC);
+ PHPAP_INI_OFF;
+ return HTTP_NOT_FOUND;
+ }
+ if (r->finfo.filetype == APR_DIR) {
+ php_apache_sapi_log_message_ex("attempt to invoke directory '%s' as script", r TSRMLS_CC);
+ PHPAP_INI_OFF;
+ return HTTP_FORBIDDEN;
+ }
+
+ /* Setup the CGI variables if this is the main request */
+ if (r->main == NULL ||
+ /* .. or if the sub-request environment differs from the main-request. */
+ r->subprocess_env != r->main->subprocess_env
+ ) {
+ /* setup standard CGI variables */
+ ap_add_common_vars(r);
+ ap_add_cgi_vars(r);
+ }
+
+zend_first_try {
+
+ if (ctx == NULL) {
+ brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ ctx = SG(server_context);
+ ctx->brigade = brigade;
+
+ if (php_apache_request_ctor(r, ctx TSRMLS_CC)!=SUCCESS) {
+ zend_bailout();
+ }
+ } else {
+ if (!parent_req) {
+ parent_req = ctx->r;
+ }
+ if (parent_req && parent_req->handler &&
+ strcmp(parent_req->handler, PHP_MAGIC_TYPE) &&
+ strcmp(parent_req->handler, PHP_SOURCE_MAGIC_TYPE) &&
+ strcmp(parent_req->handler, PHP_SCRIPT)) {
+ if (php_apache_request_ctor(r, ctx TSRMLS_CC)!=SUCCESS) {
+ zend_bailout();
+ }
+ }
+
+ /*
+ * check if comming due to ErrorDocument
+ * We make a special exception of 413 (Invalid POST request) as the invalidity of the request occurs
+ * during processing of the request by PHP during POST processing. Therefor we need to re-use the exiting
+ * PHP instance to handle the request rather then creating a new one.
+ */
+ if (parent_req && parent_req->status != HTTP_OK && parent_req->status != 413 && strcmp(r->protocol, "INCLUDED")) {
+ parent_req = NULL;
+ goto normal;
+ }
+ ctx->r = r;
+ brigade = ctx->brigade;
+ }
+
+ if (AP2(last_modified)) {
+ ap_update_mtime(r, r->finfo.mtime);
+ ap_set_last_modified(r);
+ }
+
+ /* Determine if we need to parse the file or show the source */
+ if (strncmp(r->handler, PHP_SOURCE_MAGIC_TYPE, sizeof(PHP_SOURCE_MAGIC_TYPE) - 1) == 0) {
+ zend_syntax_highlighter_ini syntax_highlighter_ini;
+ php_get_highlight_struct(&syntax_highlighter_ini);
+ highlight_file((char *)r->filename, &syntax_highlighter_ini TSRMLS_CC);
+ } else {
+ zend_file_handle zfd;
+
+ zfd.type = ZEND_HANDLE_FILENAME;
+ zfd.filename = (char *) r->filename;
+ zfd.free_filename = 0;
+ zfd.opened_path = NULL;
+
+ if (!parent_req) {
+ php_execute_script(&zfd TSRMLS_CC);
+ } else {
+ zend_execute_scripts(ZEND_INCLUDE TSRMLS_CC, NULL, 1, &zfd);
+ }
+
+ apr_table_set(r->notes, "mod_php_memory_usage",
+ apr_psprintf(ctx->r->pool, "%zu", zend_memory_peak_usage(1 TSRMLS_CC)));
+ }
+
+} zend_end_try();
+
+ if (!parent_req) {
+ php_apache_request_dtor(r TSRMLS_CC);
+ ctx->request_processed = 1;
+ bucket = apr_bucket_eos_create(r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(brigade, bucket);
+
+ rv = ap_pass_brigade(r->output_filters, brigade);
+ if (rv != APR_SUCCESS || r->connection->aborted) {
+zend_first_try {
+ php_handle_aborted_connection();
+} zend_end_try();
+ }
+ apr_brigade_cleanup(brigade);
+ } else {
+ ctx->r = parent_req;
+ }
+
+ return OK;
+}
+
+static void php_apache_child_init(apr_pool_t *pchild, server_rec *s)
+{
+ apr_pool_cleanup_register(pchild, NULL, php_apache_child_shutdown, apr_pool_cleanup_null);
+}
+
+void php_ap2_register_hook(apr_pool_t *p)
+{
+ ap_hook_pre_config(php_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_post_config(php_apache_server_startup, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_handler(php_handler, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_child_init(php_apache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/apache_hooks/CREDITS b/sapi/apache_hooks/CREDITS
new file mode 100644
index 0000000..86ac27d
--- /dev/null
+++ b/sapi/apache_hooks/CREDITS
@@ -0,0 +1,2 @@
+Apache 1.3 (apache_hooks)
+Rasmus Lerdorf, Zeev Suraski, Stig Bakken, David Sklar, George Schlossnagle, Lukas Schroeder
diff --git a/sapi/apache_hooks/README b/sapi/apache_hooks/README
new file mode 100644
index 0000000..9a5a3e2
--- /dev/null
+++ b/sapi/apache_hooks/README
@@ -0,0 +1,206 @@
+This is very beta documentation. Clearly better stuff can and will follow.
+
+INTRO:
+
+apache_hooks is a full super-set enhancement of the apache 1.3 sapi that allows for
+php code to be run on the apache request object at every stage of the apache
+request. It supports all of the apache 1.3 sapi commands and configurations, and
+additionally supports the following httpd.conf directives:
+
+
+HTTPD.CONF DIRECTIEVS:
+
+phpRequire /path/to/file = requires a file at the beginning of an
+initial apache request
+
+phpUriHandler /path/to/file = registers a hook that will run the
+specified file at the uri translation stage of the apache request
+phpUriHandler Class::Method = registers a hook to run Class::Method at
+the uri translation stage of the apache request
+
+phpPostReadHandler /path/to/file = hook for post-read phase
+phpPostReadHandlerMethod Class::Method
+
+phpHeaderHandler = hook for header parsing phase
+phpHeaderHandlerMethod
+
+phpAuthHandler = hook for authentication phase
+phpAuthHandlerMethod
+
+phpAccessHandler = hook for access control phase
+phpAccessHandlerMethod
+
+phpTypeHandler = hook for Type Checking phase
+phpTypeHandlerMethod
+
+phpFixupHandler = hook for 'fixup' phase
+phpFixupHandlerMethod
+
+phpLoggerHandler = hook for logging phase
+phpLoggerHandlerMethod
+
+AddHandler php-script = set's up a special type handler
+phpResponseHandler /path/to/file = sets file to be called to handle
+response phase
+phpResponseHandlerMethod Class::Method
+
+
+All handlers may be stacked, i.e. you can list multiple handler directives
+in a single scope and they will be run in order.
+
+
+EXAMPLES:
+
+So, to set up a 'hello world' location handler (so that any request to
+/hello/* returns hello world) you can:
+
+phpRequire /tmp/setup.php
+<Location /hello>
+AddHandler php-script
+phpResponseHandlerMethod Hello::World
+</Location>
+
+with
+#/tmp/setup.php
+<?
+class Hello {
+ function World() {
+ global $request;
+ $request->send_http_header();
+ echo "Hello World";
+ }
+}
+?>
+
+$request is the apache request. It is instantiated at all stages
+automatically. The methods of that class are:
+
+getallheaders
+args
+boundary
+content_encoding
+content_type
+filename
+handler
+hostname
+method
+path_info
+protocol
+status_line
+the_request
+unparsed_uri
+uri
+allowed
+bytes_sent
+chunked
+content_length
+header_only
+method_number
+mtime
+no_cache
+no_local_copy
+proto_num
+proxyreq
+read_body
+remaining
+request_time
+status
+headers_in
+headers_out
+err_headers_out
+auth_name
+auth_type
+basic_auth_pw
+discard_request_body
+is_initial_req
+meets_conditions
+remote_host
+satisfies
+server_port
+set_etag
+set_last_modified
+some_auth_required
+update_mtime
+send_http_header
+basic_http_header
+send_header_field
+send_http_trace
+send_http_options
+send_error_response
+set_content_length
+set_keepalive
+rputs
+log_error
+lookup_uri
+lookup_file
+method_uri
+run
+internal_redirect
+
+
+These all wrap the ap_* apache EXPORT_API functions using the same
+semantics (and are also the same as the Apache::Request methods in
+mod_perl if you are familiar with that)
+
+So, a uri handler to redirect all non-local traffic to /404.php (an
+error page) would be
+
+phpUriHandler /tmp/uri.php
+
+#/tmp/uri.php
+<?
+ if($REMOTE_ADDR != '127.0.0.1') {
+ $request->uri('/404.php');
+ }
+ return OK;
+?>
+
+It's important to note that since this is called from the uri
+translations phase, this validation is performed for every request to
+the server, not just for php pages.
+
+Also, scope is shared between all the hooks. So in the above, we could
+merge the two and do something like:
+
+#/tmp/uri.php
+<?
+ if($REMOTE_ADDR != '127.0.0.1') {
+ $whoami = 'Stranger';
+ }
+ else {
+ $whoami = 'Friend';
+ }
+ return DECLINED; # because we're not redirecting, just messing around
+?>
+
+and then:
+
+#/tmp/setup.php
+<?
+class Hello {
+ function World() {
+ global $request;
+ global $whoami;
+ $request->send_http_header();
+ echo "Hello $whoami";
+ }
+}
+?>
+
+These variables are also in the same scope as a script if your script is
+being handled by the standard application/x-httpd-php handler.
+
+This allows you to make decisions and pass data between your handlers
+and scripts at all stages.
+
+The above are clearly trite examples, but hopefully give you a starting
+point.
+
+One note: all handlers can be validly re-entered 'in sub-requests'.
+For this reason you should not define functions/classes here without
+anti-redefinition guards (I would just recommend putting them in an
+include and using include_one). This is not true for phpRequire, which
+is only entered once, at the main request, and so it is safe to make
+function/class declarations there (in fact that's what it's for).
+
+Hope that helps!
diff --git a/sapi/apache_hooks/apMakefile.libdir b/sapi/apache_hooks/apMakefile.libdir
new file mode 100644
index 0000000..7b52540
--- /dev/null
+++ b/sapi/apache_hooks/apMakefile.libdir
@@ -0,0 +1,4 @@
+This is a place-holder which indicates to Configure that it shouldn't
+provide the default targets when building the Makefile in this directory.
+Instead it'll just prepend all the important variable definitions, and
+copy the Makefile.tmpl onto the end.
diff --git a/sapi/apache_hooks/apMakefile.tmpl b/sapi/apache_hooks/apMakefile.tmpl
new file mode 100644
index 0000000..4054e8e
--- /dev/null
+++ b/sapi/apache_hooks/apMakefile.tmpl
@@ -0,0 +1,77 @@
+##
+## Apache 1.3 Makefile template for PHP 4.0 Module
+## [src/modules/php5/Makefile.tmpl]
+##
+
+# the parametrized target
+LIB=libphp5.$(LIBEXT)
+
+# objects for building the static library
+OBJS=mod_php5.o
+OBJS_LIB=libmodphp5.a
+
+# objects for building the shared object library
+SHLIB_OBJS=mod_php5.so-o
+SHLIB_OBJS_LIB=libmodphp5.a
+
+# the general targets
+all: lib
+lib: $(LIB)
+
+# build the static library by merging the object files
+libphp5.a: $(OBJS) $(OBJS_LIB)
+ cp $(OBJS_LIB) $@
+ ar r $@ $(OBJS)
+ $(RANLIB) $@
+
+# ugly hack to support older Apache-1.3 betas that don't set $LIBEXT
+libphp5.: $(OBJS) $(OBJS_LIB)
+ cp $(OBJS_LIB) $@
+ ar r $@ $(OBJS)
+ $(RANLIB) $@
+ cp libphp5. libphp5.a
+
+# build the shared object library by linking the object files
+libphp5.so: $(SHLIB_OBJS) $(SHLIB_OBJS_LIB)
+ rm -f $@
+ $(LD_SHLIB) $(LDFLAGS_SHLIB) -o $@ $(SHLIB_OBJS) $(SHLIB_OBJS_LIB) $(LIBS) $(PHP_LIBS)
+
+# 1. extension .o for shared objects cannot be used here because
+# first these files aren't still shared objects and second we
+# have to use a different name to trigger the different
+# implicit Make rule
+# 2. extension -so.o (as used elsewhere) cannot be used because
+# the suffix feature of Make really wants just .x, so we use
+# extension .so-o
+.SUFFIXES: .o .so-o
+.c.o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $(PHP_CFLAGS) $(CPPFLAGS) $(SPACER) $<
+.c.so-o:
+ $(CC) -c $(INCLUDES) $(CFLAGS) $(CFLAGS_SHLIB) $(PHP_CFLAGS) $(CPPFLAGS) $(SPACER) $< && mv $*.o $*.so-o
+
+# cleanup
+clean:
+ -rm -f $(OBJS) $(SHLIB_OBJS) $(LIB)
+
+# We really don't expect end users to use this rule. It works only with
+# gcc, and rebuilds Makefile.tmpl. You have to re-run Configure after
+# using it.
+depend:
+ cp Makefile.tmpl Makefile.tmpl.bak \
+ && sed -ne '1,/^# DO NOT REMOVE/p' Makefile.tmpl > Makefile.new \
+ && gcc -MM $(INCLUDES) $(CFLAGS) $(PHP_CFLAGS) $(CPPFLAGS) *.c >> Makefile.new \
+ && sed -e '1,$$s: $(INCDIR)/: $$(INCDIR)/:g' Makefile.new \
+ > Makefile.tmpl \
+ && rm Makefile.new
+
+#Dependencies
+
+$(OBJS): Makefile
+
+# DO NOT REMOVE
+mod_php5.o: mod_php5.c $(INCDIR)/httpd.h $(INCDIR)/conf.h \
+ $(INCDIR)/buff.h \
+ $(INCDIR)/http_config.h \
+ $(INCDIR)/http_core.h $(INCDIR)/http_main.h \
+ $(INCDIR)/http_protocol.h $(INCDIR)/http_request.h \
+ $(INCDIR)/http_log.h $(INCDIR)/util_script.h mod_php5.h
diff --git a/sapi/apache_hooks/config.m4 b/sapi/apache_hooks/config.m4
new file mode 100644
index 0000000..4213b7c
--- /dev/null
+++ b/sapi/apache_hooks/config.m4
@@ -0,0 +1,275 @@
+dnl
+dnl $Id$
+dnl
+AC_DEFUN([PHP_APACHE_FD_CHECK], [
+AC_CACHE_CHECK([for member fd in BUFF *],ac_cv_php_fd_in_buff,[
+ save=$CPPFLAGS
+ if test -n "$APXS_INCLUDEDIR"; then
+ CPPFLAGS="$CPPFLAGS -I$APXS_INCLUDEDIR"
+ else
+ CPPFLAGS="$CPPFLAGS $APACHE_INCLUDE"
+ fi
+ AC_TRY_COMPILE([#include <httpd.h>],[conn_rec *c; int fd = c->client->fd;],[
+ ac_cv_php_fd_in_buff=yes],[ac_cv_php_fd_in_buff=no],[ac_cv_php_fd_in_buff=no])
+ CPPFLAGS=$save
+])
+if test "$ac_cv_php_fd_in_buff" = "yes"; then
+ AC_DEFINE(PHP_APACHE_HAVE_CLIENT_FD,1,[ ])
+fi
+])
+
+dnl Apache 1.x shared module
+PHP_ARG_WITH(apache-hooks,,
+[ --with-apache-hooks[=FILE]
+ EXPERIMENTAL: Build shared Apache 1.x module. FILE is the optional
+ pathname to the Apache apxs tool [apxs]], no, no)
+
+AC_MSG_CHECKING([for Apache 1.x (hooks) module support via DSO through APXS])
+
+if test "$PHP_APACHE_HOOKS" != "no"; then
+ if test "$PHP_APACHE_HOOKS" = "yes"; then
+ APXS=apxs
+ $APXS -q CFLAGS >/dev/null 2>&1
+ if test "$?" != "0" && test -x /usr/sbin/apxs; then #SUSE 6.x
+ APXS=/usr/sbin/apxs
+ fi
+ else
+ PHP_EXPAND_PATH($PHP_APACHE_HOOKS, APXS)
+ fi
+
+ $APXS -q CFLAGS >/dev/null 2>&1
+ if test "$?" != "0"; then
+ AC_MSG_RESULT()
+ AC_MSG_RESULT()
+ AC_MSG_RESULT([Sorry, I was not able to successfully run APXS. Possible reasons:])
+ AC_MSG_RESULT()
+ AC_MSG_RESULT([1. Perl is not installed;])
+ AC_MSG_RESULT([2. Apache was not compiled with DSO support (--enable-module=so);])
+ AC_MSG_RESULT([3. 'apxs' is not in your path. Try to use --with-apxs=/path/to/apxs])
+ AC_MSG_RESULT([The output of $APXS follows])
+ $APXS -q CFLAGS
+ AC_MSG_ERROR([Aborting])
+ fi
+
+ APXS_LDFLAGS="@SYBASE_LFLAGS@ @SYBASE_LIBS@ @SYBASE_CT_LFLAGS@ @SYBASE_CT_LIBS@"
+ APXS_INCLUDEDIR=`$APXS -q INCLUDEDIR`
+ APXS_CFLAGS=`$APXS -q CFLAGS`
+ APXS_HTTPD=`$APXS -q SBINDIR`/`$APXS -q TARGET`
+ APACHE_INCLUDE=-I$APXS_INCLUDEDIR
+
+ # Test that we're trying to configure with apache 1.x
+ PHP_AP_EXTRACT_VERSION($APXS_HTTPD)
+ if test "$APACHE_VERSION" -ge 2000000; then
+ AC_MSG_ERROR([You have enabled Apache 1.3 support while your server is Apache 2. Please use the appropiate switch --with-apxs2])
+ fi
+
+ for flag in $APXS_CFLAGS; do
+ case $flag in
+ -D*) APACHE_CPPFLAGS="$APACHE_CPPFLAGS $flag";;
+ esac
+ done
+
+ case $host_alias in
+ *aix*)
+ APXS_LIBEXECDIR=`$APXS -q LIBEXECDIR`
+ EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-brtl -Wl,-bI:$APXS_LIBEXECDIR/httpd.exp"
+ PHP_AIX_LDFLAGS="-Wl,-brtl"
+ build_type=shared
+ ;;
+ *darwin*)
+ MH_BUNDLE_FLAGS="-dynamic -twolevel_namespace -bundle -bundle_loader $APXS_HTTPD"
+ PHP_SUBST(MH_BUNDLE_FLAGS)
+ SAPI_SHARED=libs/libphp5.so
+ build_type=bundle
+ ;;
+ *)
+ build_type=shared
+ ;;
+ esac
+
+ PHP_SELECT_SAPI(apache_hooks, $build_type, sapi_apache.c mod_php5.c php_apache.c, $APACHE_CPPFLAGS -I$APXS_INCLUDEDIR)
+
+ # Test whether apxs support -S option
+ $APXS -q -S CFLAGS="$APXS_CFLAGS" CFLAGS >/dev/null 2>&1
+
+ if test "$?" != "0"; then
+ APACHE_HOOKS_INSTALL="$APXS -i -a -n php5 $SAPI_SHARED" # Old apxs does not have -S option
+ else
+ APXS_LIBEXECDIR='$(INSTALL_ROOT)'`$APXS -q LIBEXECDIR`
+ if test -z `$APXS -q SYSCONFDIR`; then
+ APACHE_HOOKS_INSTALL="\$(mkinstalldirs) '$APXS_LIBEXECDIR' && \
+ $APXS -S LIBEXECDIR='$APXS_LIBEXECDIR' \
+ -i -n php5 $SAPI_SHARED"
+ else
+ APXS_SYSCONFDIR='$(INSTALL_ROOT)'`$APXS -q SYSCONFDIR`
+ APACHE_HOOKS_INSTALL="\$(mkinstalldirs) '$APXS_LIBEXECDIR' && \
+ \$(mkinstalldirs) '$APXS_SYSCONFDIR' && \
+ $APXS -S LIBEXECDIR='$APXS_LIBEXECDIR' \
+ -S SYSCONFDIR='$APXS_SYSCONFDIR' \
+ -i -a -n php5 $SAPI_SHARED"
+ fi
+ fi
+
+ if test -z "`$APXS -q LD_SHLIB`" || test "`$APXS -q LIBEXECDIR`" = "modules"; then
+ PHP_APXS_BROKEN=yes
+ fi
+ STRONGHOLD=
+ AC_DEFINE(HAVE_AP_CONFIG_H,1,[ ])
+ AC_DEFINE(HAVE_AP_COMPAT_H,1,[ ])
+ AC_DEFINE(HAVE_APACHE_HOOKS,1,[ ])
+ AC_MSG_RESULT(yes)
+else
+ AC_MSG_RESULT(no)
+fi
+
+dnl Apache 1.x static module
+PHP_ARG_WITH(apache-hooks-static,,
+[ --with-apache-hooks-static[=DIR]
+ EXPERIMENTAL: Build Apache 1.x module. DIR is the top-level Apache
+ build directory [/usr/local/apache]], no, no)
+
+AC_MSG_CHECKING(for Apache 1.x (hooks) module support)
+
+if test "$PHP_SAPI" != "apache" && test "$PHP_SAPI" != "apache_hooks" && test "$PHP_APACHE_HOOKS_STATIC" != "no"; then
+
+ if test "$PHP_APACHE_HOOKS_STATIC" = "yes"; then
+ # Apache's default directory
+ PHP_APACHE_HOOKS_STATIC=/usr/local/apache
+ fi
+
+ APACHE_HOOKS_INSTALL_FILES="\$(srcdir)/sapi/apache_hooks/mod_php5.* sapi/apache_hooks/libphp5.module"
+
+ AC_DEFINE(HAVE_APACHE,1,[ ])
+ APACHE_HOOKS_MODULE=yes
+ PHP_EXPAND_PATH($PHP_APACHE_HOOKS_STATIC, PHP_APACHE_HOOKS_STATIC)
+ # For Apache 1.2.x
+ if test -f $PHP_APACHE_HOOKS_STATIC/src/httpd.h; then
+ APACHE_INCLUDE=-I$PHP_APACHE_HOOKS_STATIC/src
+ APACHE_TARGET=$PHP_APACHE_HOOKS_STATIC/src
+ PHP_SELECT_SAPI(apache_hooks, static, sapi_apache.c mod_php5.c php_apache.c, $APACHE_INCLUDE)
+ APACHE_HOOKS_INSTALL="mkdir -p $APACHE_TARGET; cp $SAPI_STATIC $APACHE_HOOKS_INSTALL_FILES $APACHE_TARGET"
+ PHP_LIBS="-L. -lphp3"
+ AC_MSG_RESULT([yes - Apache 1.2.x])
+ STRONGHOLD=
+ if test -f $PHP_APACHE_HOOKS_STATIC/src/ap_config.h; then
+ AC_DEFINE(HAVE_AP_CONFIG_H,1,[ ])
+ fi
+ # For Apache 2.0.x
+ elif test -f $PHP_APACHE_HOOKS_STATIC/include/httpd.h && test -f $PHP_APACHE_HOOKS_STATIC/srclib/apr/include/apr_general.h ; then
+ AC_MSG_ERROR([Use --with-apxs2 with Apache 2.x!])
+ # For Apache 1.3.x
+ elif test -f $PHP_APACHE_HOOKS_STATIC/src/main/httpd.h; then
+ APACHE_HAS_REGEX=1
+ APACHE_INCLUDE="-I$PHP_APACHE_HOOKS_STATIC/src/main -I$PHP_APACHE_HOOKS_STATIC/src/os/unix -I$PHP_APACHE_HOOKS_STATIC/src/ap"
+ APACHE_TARGET=$PHP_APACHE_HOOKS_STATIC/src/modules/php5
+ if test ! -d $APACHE_TARGET; then
+ mkdir $APACHE_TARGET
+ fi
+ PHP_SELECT_SAPI(apache_hooks, static, sapi_apache.c mod_php5.c php_apache.c, $APACHE_INCLUDE)
+ APACHE_HOOKS_INSTALL="mkdir -p $APACHE_TARGET; cp $SAPI_STATIC $APACHE_TARGET/libmodphp5.a; cp $APACHE_HOOKS_INSTALL_FILES $APACHE_TARGET; cp $srcdir/sapi/apache_hooks/apMakefile.tmpl $APACHE_TARGET/Makefile.tmpl; cp $srcdir/sapi/apache_hooks/apMakefile.libdir $APACHE_TARGET/Makefile.libdir"
+ PHP_LIBS="-Lmodules/php5 -L../modules/php5 -L../../modules/php5 -lmodphp5"
+ AC_MSG_RESULT([yes - Apache 1.3.x])
+ STRONGHOLD=
+ if test -f $PHP_APACHE_HOOKS_STATIC/src/include/ap_config.h; then
+ AC_DEFINE(HAVE_AP_CONFIG_H, 1, [ ])
+ fi
+ if test -f $PHP_APACHE_HOOKS_STATIC/src/include/ap_compat.h; then
+ AC_DEFINE(HAVE_AP_COMPAT_H, 1, [ ])
+ if test ! -f $PHP_APACHE_HOOKS_STATIC/src/include/ap_config_auto.h; then
+ AC_MSG_ERROR([Please run Apache\'s configure or src/Configure program once and try again])
+ fi
+ elif test -f $PHP_APACHE_HOOKS_STATIC/src/include/compat.h; then
+ AC_DEFINE(HAVE_OLD_COMPAT_H, 1, [ ])
+ fi
+ # Also for Apache 1.3.x
+ elif test -f $PHP_APACHE_HOOKS_STATIC/src/include/httpd.h; then
+ APACHE_HAS_REGEX=1
+ APACHE_INCLUDE="-I$PHP_APACHE_HOOKS_STATIC/src/include -I$PHP_APACHE_HOOKS_STATIC/src/os/unix"
+ APACHE_TARGET=$PHP_APACHE_HOOKS_STATIC/src/modules/php5
+ if test ! -d $APACHE_TARGET; then
+ mkdir $APACHE_TARGET
+ fi
+ PHP_SELECT_SAPI(apache_hooks, static, sapi_apache.c mod_php5.c php_apache.c, $APACHE_INCLUDE)
+ PHP_LIBS="-Lmodules/php5 -L../modules/php5 -L../../modules/php5 -lmodphp5"
+ APACHE_HOOKS_INSTALL="mkdir -p $APACHE_TARGET; cp $SAPI_STATIC $APACHE_TARGET/libmodphp5.a; cp $APACHE_HOOKS_INSTALL_FILES $APACHE_TARGET; cp $srcdir/sapi/apache_hooks/apMakefile.tmpl $APACHE_TARGET/Makefile.tmpl; cp $srcdir/sapi/apache_hooks/apMakefile.libdir $APACHE_TARGET/Makefile.libdir"
+ AC_MSG_RESULT([yes - Apache 1.3.x])
+ STRONGHOLD=
+ if test -f $PHP_APACHE_HOOKS_STATIC/src/include/ap_config.h; then
+ AC_DEFINE(HAVE_AP_CONFIG_H, 1, [ ])
+ fi
+ if test -f $PHP_APACHE_HOOKS_STATIC/src/include/ap_compat.h; then
+ AC_DEFINE(HAVE_AP_COMPAT_H, 1, [ ])
+ if test ! -f $PHP_APACHE_HOOKS_STATIC/src/include/ap_config_auto.h; then
+ AC_MSG_ERROR([Please run Apache\'s configure or src/Configure program once and try again])
+ fi
+ elif test -f $PHP_APACHE_HOOKS_STATIC/src/include/compat.h; then
+ AC_DEFINE(HAVE_OLD_COMPAT_H, 1, [ ])
+ fi
+ # For StrongHold 2.2
+ elif test -f $PHP_APACHE_HOOKS_STATIC/apache/httpd.h; then
+ APACHE_INCLUDE="-I$PHP_APACHE_HOOKS_STATIC/apache -I$PHP_APACHE_HOOKS_STATIC/ssl/include"
+ APACHE_TARGET=$PHP_APACHE_HOOKS_STATIC/apache
+ PHP_SELECT_SAPI(apache_hooks, static, sapi_apache.c mod_php5.c php_apache.c, $APACHE_INCLUDE)
+ PHP_LIBS="-Lmodules/php5 -L../modules/php5 -L../../modules/php5 -lmodphp5"
+ APACHE_HOOKS_INSTALL="mkdir -p $APACHE_TARGET; cp $SAPI_STATIC $APACHE_TARGET/libmodphp5.a; cp $APACHE_HOOKS_INSTALL_FILES $APACHE_TARGET"
+ STRONGHOLD=-DSTRONGHOLD=1
+ AC_MSG_RESULT([yes - StrongHold])
+ if test -f $PHP_APACHE_HOOKS_STATIC/apache/ap_config.h; then
+ AC_DEFINE(HAVE_AP_CONFIG_H, 1, [ ])
+ fi
+ if test -f $PHP_APACHE_HOOKS_STATIC/src/ap_compat.h; then
+ AC_DEFINE(HAVE_AP_COMPAT_H, 1, [ ])
+ if test ! -f $PHP_APACHE_HOOKS_STATIC/src/include/ap_config_auto.h; then
+ AC_MSG_ERROR([Please run Apache\'s configure or src/Configure program once and try again])
+ fi
+ elif test -f $PHP_APACHE_HOOKS_STATIC/src/compat.h; then
+ AC_DEFINE(HAVE_OLD_COMPAT_H, 1, [ ])
+ fi
+ else
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR([Invalid Apache directory - unable to find httpd.h under $PHP_APACHE_HOOKS_STATIC])
+ fi
+else
+ AC_MSG_RESULT(no)
+fi
+
+# compatibility
+if test -z "$enable_mod_charset" && test "$with_mod_charset"; then
+ enable_mod_charset=$with_mod_charset
+fi
+
+PHP_ARG_ENABLE(mod-charset, whether to enable Apache charset compatibility option,
+[ --enable-mod-charset APACHE (hooks): Enable transfer tables for mod_charset (Rus Apache)], no, no)
+
+if test "$PHP_MOD_CHARSET" = "yes"; then
+ AC_DEFINE(USE_TRANSFER_TABLES, 1, [ ])
+fi
+
+dnl Build as static module
+if test "$APACHE_HOOKS_MODULE" = "yes"; then
+ PHP_TARGET_RDYNAMIC
+ $php_shtool mkdir -p sapi/apache_hooks
+ PHP_OUTPUT(sapi/apache_hooks/libphp5.module)
+fi
+
+dnl General
+if test -n "$APACHE_HOOKS_INSTALL"; then
+ if test "x$APXS" != "x" -a "`uname -sv`" = "AIX 4" -a "$GCC" != "yes"; then
+ APXS_EXP=-bE:sapi/apache_hooks/mod_php5.exp
+ fi
+
+ PHP_APACHE_FD_CHECK
+ INSTALL_IT=$APACHE_HOOKS_INSTALL
+
+ PHP_SUBST(APXS_EXP)
+ PHP_SUBST(APACHE_INCLUDE)
+ PHP_SUBST(APACHE_TARGET)
+ PHP_SUBST(APXS)
+ PHP_SUBST(APXS_LDFLAGS)
+ PHP_SUBST(APACHE_HOOKS_INSTALL)
+ PHP_SUBST(STRONGHOLD)
+fi
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/apache_hooks/config.w32 b/sapi/apache_hooks/config.w32
new file mode 100644
index 0000000..85dc624
--- /dev/null
+++ b/sapi/apache_hooks/config.w32
@@ -0,0 +1,21 @@
+// vim:ft=javascript
+// $Id$
+
+ARG_WITH('apache-hooks', 'Build Apache 1.3.x (hooks) version of PHP', 'no');
+
+if (PHP_APACHE_HOOKS != "no") {
+ if (CHECK_HEADER_ADD_INCLUDE("httpd.h", "CFLAGS_APACHE_HOOKS", php_usual_include_suspects +
+ ";" + PROGRAM_FILES + "\\Apache Group\\Apache\\include" +
+ ";" + PHP_PHP_BUILD + "\\apache\\src\\include") &&
+ CHECK_LIB("ApacheCore.lib", "apache_hooks", php_usual_lib_suspects +
+ ';' + PROGRAM_FILES + '\\Apache Group\\Apache\\libexec' +
+ ";" + PHP_PHP_BUILD + "\\apache\\src\\corer")) {
+ // We need to play tricks to get our readdir.h used by apache
+ // headers
+ SAPI('apache_hooks', 'mod_php5.c sapi_apache.c php_apache.c',
+ 'php' + PHP_VERSION + 'apache_hooks.dll',
+ '/D APACHEPHP5_EXPORTS /D APACHE_READDIR_H /I win32');
+ } else {
+ WARNING("Could not find apache libraries/headers");
+ }
+}
diff --git a/sapi/apache_hooks/libphp5.module.in b/sapi/apache_hooks/libphp5.module.in
new file mode 100644
index 0000000..8488181
--- /dev/null
+++ b/sapi/apache_hooks/libphp5.module.in
@@ -0,0 +1,11 @@
+Name: php5_module
+ConfigStart
+ RULE_WANTHSREGEX=no
+ RULE_HIDE=yes
+ PHP_LIBS="@NATIVE_RPATHS@ @PHP_LDFLAGS@ @PHP_LIBS@ @EXTRA_LIBS@ $LIBS"
+ PHP_CFLAGS="$CFLAGS @OPENSSL_INCDIR_OPT@ -I@php_abs_top_builddir@/main -I@php_abs_top_builddir@/Zend -I@php_abs_top_builddir@/TSRM -I@php_abs_top_srcdir@ -I@php_abs_top_srcdir@/sapi/apache -I@php_abs_top_srcdir@/main -I@php_abs_top_srcdir@/Zend -I@php_abs_top_srcdir@/TSRM"
+ my_outfile="Makefile.config"
+ echo "PHP_CFLAGS=$PHP_CFLAGS" >>$my_outfile
+ echo "PHP_LIBS=$PHP_LIBS" >>$my_outfile
+ LIBS=$PHP_LIBS
+ConfigEnd
diff --git a/sapi/apache_hooks/mod_php5.c b/sapi/apache_hooks/mod_php5.c
new file mode 100644
index 0000000..dda6e49
--- /dev/null
+++ b/sapi/apache_hooks/mod_php5.c
@@ -0,0 +1,1493 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@php.net> |
+ | (with helpful hints from Dean Gaudet <dgaudet@arctic.org> |
+ | PHP 4.0 patches by Zeev Suraski <zeev@zend.com> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#include "php_apache_http.h"
+
+#ifdef NETWARE
+#define SIGPIPE SIGINT
+#endif
+
+#undef shutdown
+
+/* {{{ Prototypes
+ */
+int apache_php_module_main(request_rec *r, int display_source_mode TSRMLS_DC);
+static void php_save_umask(void);
+static void php_restore_umask(void);
+static int sapi_apache_read_post(char *buffer, uint count_bytes TSRMLS_DC);
+static char *sapi_apache_read_cookies(TSRMLS_D);
+static int sapi_apache_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC);
+static int sapi_apache_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC);
+static int send_php(request_rec *r, int display_source_mode, char *filename);
+static int send_parsed_php(request_rec * r);
+static int send_parsed_php_source(request_rec * r);
+static int php_xbithack_handler(request_rec * r);
+static void php_init_handler(server_rec *s, pool *p);
+/* }}} */
+
+#if MODULE_MAGIC_NUMBER >= 19970728
+static void php_child_exit_handler(server_rec *s, pool *p);
+#endif
+
+#if MODULE_MAGIC_NUMBER > 19961007
+#define CONST_PREFIX const
+#else
+#define CONST_PREFIX
+#endif
+
+
+typedef struct _sapi_stack {
+ int top, max, persistent;
+ void **elements;
+} sapi_stack;
+
+typedef struct _php_per_dir_config {
+ HashTable *ini_settings;
+ sapi_stack headers_handlers;
+ sapi_stack auth_handlers;
+ sapi_stack access_handlers;
+ sapi_stack type_handlers;
+ sapi_stack fixup_handlers;
+ sapi_stack logger_handlers;
+ sapi_stack post_read_handlers;
+ sapi_stack response_handlers;
+} php_per_dir_config;
+
+typedef struct _php_per_server_config {
+ sapi_stack uri_handlers;
+ sapi_stack requires;
+} php_per_server_config;
+
+
+static CONST_PREFIX char *php_apache_value_handler_ex(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2, int mode);
+static CONST_PREFIX char *php_apache_value_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1, char *arg2);
+static CONST_PREFIX char *php_apache_admin_value_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1, char *arg2);
+static CONST_PREFIX char *php_apache_flag_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1, char *arg2);
+static CONST_PREFIX char *php_apache_flag_handler_ex(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2, int mode);
+static CONST_PREFIX char *php_apache_admin_flag_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1, char *arg2);
+
+/* ### these should be defined in mod_php5.h or somewhere else */
+#define USE_PATH 1
+#define IGNORE_URL 2
+
+module MODULE_VAR_EXPORT php5_module;
+
+int saved_umask;
+/* static int setup_env = 0; */
+static unsigned char apache_php_initialized;
+
+typedef struct _php_per_dir_entry {
+ char *key;
+ char *value;
+ uint key_length;
+ uint value_length;
+ int type;
+} php_per_dir_entry;
+
+/* some systems are missing these from their header files */
+
+/* {{{ zend stack utility functions
+ */
+
+/* This code is ripped part and parcel from zend_stack.[ch]. Assuming that the
+ patch supporting zend_stack_init_ex is applied, all but the bottom two
+ module-specific iterators will be removed
+ */
+
+int sapi_stack_init_ex(sapi_stack *stack, int persistent)
+{
+ stack->top = 0;
+ stack->persistent = persistent;
+ stack->elements = (void **) pemalloc(sizeof(void **) * STACK_BLOCK_SIZE, persistent);
+ if (!stack->elements) {
+ return FAILURE;
+ } else {
+ stack->max = STACK_BLOCK_SIZE;
+ return SUCCESS;
+ }
+}
+int sapi_stack_push(sapi_stack *stack, void *element)
+{
+ if (stack->top >= stack->max) { /* we need to allocate more memory */
+ stack->elements = (void **) perealloc(stack->elements,
+ (sizeof(void **) * (stack->max += STACK_BLOCK_SIZE)), stack->persistent);
+ if (!stack->elements) {
+ return FAILURE;
+ }
+ }
+ stack->elements[stack->top] = (void *) element;
+ return stack->top++;
+}
+void* sapi_stack_pop(sapi_stack *stack) {
+ if(stack->top == 0) {
+ return NULL;
+ }
+ else {
+ return stack->elements[--stack->top];
+ }
+}
+
+int sapi_stack_destroy(sapi_stack *stack)
+{
+ return SUCCESS;
+}
+
+int sapi_stack_apply_with_argument_all(sapi_stack *stack, int type, int (*apply_function)(void *element, void *arg), void *arg)
+{
+ int i, retval;
+
+ switch (type) {
+ case ZEND_STACK_APPLY_TOPDOWN:
+ for (i=stack->top-1; i>=0; i--) {
+ retval = apply_function(stack->elements[i], arg);
+ }
+ break;
+ case ZEND_STACK_APPLY_BOTTOMUP:
+ for (i=0; i<stack->top; i++) {
+ retval = apply_function(stack->elements[i], arg);
+ }
+ break;
+ }
+ return retval;
+}
+
+
+int sapi_stack_apply_with_argument_stop_if_equals(sapi_stack *stack, int type, int (*apply_function)(void *element, void *arg), void *arg, int stopval)
+{
+ int i;
+ int ret = DECLINED;
+ switch (type) {
+ case ZEND_STACK_APPLY_TOPDOWN:
+ for (i=stack->top-1; i>=0; i--) {
+ if ((ret = apply_function(stack->elements[i], arg)) == stopval) {
+ break;
+ }
+ }
+ break;
+ case ZEND_STACK_APPLY_BOTTOMUP:
+ for (i=0; i<stack->top; i++) {
+ if ((ret = apply_function(stack->elements[i], arg)) == stopval) {
+ break;
+ }
+ }
+ break;
+ }
+ return ret;
+}
+
+int sapi_stack_apply_with_argument_stop_if_http_error(sapi_stack *stack, int type, int (*apply_function)(void *element, void *arg), void *arg)
+{
+ int i;
+ int ret = DECLINED;
+ switch (type) {
+ case ZEND_STACK_APPLY_TOPDOWN:
+ for (i=stack->top-1; i>=0; i--) {
+ if ((ret = apply_function(stack->elements[i], arg)) > 0) {
+ break;
+ }
+ }
+ break;
+ case ZEND_STACK_APPLY_BOTTOMUP:
+ for (i=0; i<stack->top; i++) {
+ if ((ret = apply_function(stack->elements[i], arg)) > 0) {
+ break;
+ }
+ }
+ break;
+ }
+ return ret;
+}
+
+void php_handler_stack_destroy(sapi_stack *stack)
+{
+ php_handler *ph;
+ while((ph = (php_handler *)sapi_stack_pop(stack)) != NULL) {
+ free(ph->name);
+ free(ph);
+ }
+}
+/* }}} */
+
+/* {{{ php_save_umask
+ */
+static void php_save_umask(void)
+{
+ saved_umask = umask(077);
+ umask(saved_umask);
+}
+/* }}} */
+
+/* {{{ sapi_apache_ub_write
+ */
+static int sapi_apache_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ int ret=0;
+
+ if (SG(server_context)) {
+ ret = rwrite(str, str_length, (request_rec *) SG(server_context));
+ }
+ if (ret != str_length) {
+ php_handle_aborted_connection();
+ }
+ return ret;
+}
+/* }}} */
+
+/* {{{ sapi_apache_flush
+ */
+static void sapi_apache_flush(void *server_context)
+{
+ if (server_context) {
+#if MODULE_MAGIC_NUMBER > 19970110
+ rflush((request_rec *) server_context);
+#else
+ bflush((request_rec *) server_context->connection->client);
+#endif
+ }
+}
+/* }}} */
+
+/* {{{ sapi_apache_read_post
+ */
+static int sapi_apache_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ uint total_read_bytes=0, read_bytes;
+ request_rec *r = (request_rec *) SG(server_context);
+ void (*handler)(int);
+
+ /*
+ * This handles the situation where the browser sends a Expect: 100-continue header
+ * and needs to recieve confirmation from the server on whether or not it can send
+ * the rest of the request. RFC 2616
+ *
+ */
+ if (!SG(read_post_bytes) && !ap_should_client_block(r)) {
+ return total_read_bytes;
+ }
+
+ handler = signal(SIGPIPE, SIG_IGN);
+ while (total_read_bytes<count_bytes) {
+ hard_timeout("Read POST information", r); /* start timeout timer */
+ read_bytes = get_client_block(r, buffer+total_read_bytes, count_bytes-total_read_bytes);
+ reset_timeout(r);
+ if (read_bytes<=0) {
+ break;
+ }
+ total_read_bytes += read_bytes;
+ }
+ signal(SIGPIPE, handler);
+ return total_read_bytes;
+}
+/* }}} */
+
+/* {{{ sapi_apache_read_cookies
+ */
+static char *sapi_apache_read_cookies(TSRMLS_D)
+{
+ return (char *) table_get(((request_rec *) SG(server_context))->subprocess_env, "HTTP_COOKIE");
+}
+/* }}} */
+
+/* {{{ sapi_apache_header_handler
+ */
+static int sapi_apache_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ char *header_name, *header_content, *p;
+ request_rec *r = (request_rec *) SG(server_context);
+ if(!r) {
+ return 0;
+ }
+
+ switch(op) {
+ case SAPI_HEADER_DELETE_ALL:
+ clear_table(r->headers_out);
+ return 0;
+
+ case SAPI_HEADER_DELETE:
+ table_unset(r->headers_out, sapi_header->header);
+ return 0;
+
+ case SAPI_HEADER_ADD:
+ case SAPI_HEADER_REPLACE:
+ header_name = sapi_header->header;
+
+ header_content = p = strchr(header_name, ':');
+ if (!p) {
+ return 0;
+ }
+
+ *p = 0;
+ do {
+ header_content++;
+ } while (*header_content==' ');
+
+ if (!strcasecmp(header_name, "Content-Type")) {
+ r->content_type = pstrdup(r->pool, header_content);
+ } else if (!strcasecmp(header_name, "Set-Cookie")) {
+ table_add(r->headers_out, header_name, header_content);
+ } else if (op == SAPI_HEADER_REPLACE) {
+ table_set(r->headers_out, header_name, header_content);
+ } else {
+ table_add(r->headers_out, header_name, header_content);
+ }
+
+ *p = ':'; /* a well behaved header handler shouldn't change its original arguments */
+
+ return SAPI_HEADER_ADD;
+
+ default:
+ return 0;
+ }
+}
+/* }}} */
+
+/* {{{ sapi_apache_send_headers
+ */
+static int sapi_apache_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ if(SG(server_context) == NULL) { /* server_context is not here anymore */
+ return SAPI_HEADER_SEND_FAILED;
+ }
+
+ ((request_rec *) SG(server_context))->status = SG(sapi_headers).http_response_code;
+ /* check that we haven't sent headers already, we use our own
+ * headers_sent since we may send headers at anytime
+ */
+ if(!AP(headers_sent)) {
+ send_http_header((request_rec *) SG(server_context));
+ AP(headers_sent) = 1;
+ }
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+/* }}} */
+
+/* {{{ sapi_apache_register_server_variables
+ */
+static void sapi_apache_register_server_variables(zval *track_vars_array TSRMLS_DC)
+{
+ register int i;
+ array_header *arr = table_elts(((request_rec *) SG(server_context))->subprocess_env);
+ table_entry *elts = (table_entry *) arr->elts;
+ zval **path_translated;
+ HashTable *symbol_table;
+
+ for (i = 0; i < arr->nelts; i++) {
+ char *val;
+
+ if (elts[i].val) {
+ val = elts[i].val;
+ } else {
+ val = "";
+ }
+ php_register_variable(elts[i].key, val, track_vars_array TSRMLS_CC);
+ }
+
+ /* If PATH_TRANSLATED doesn't exist, copy it from SCRIPT_FILENAME */
+ if (track_vars_array) {
+ symbol_table = track_vars_array->value.ht;
+ } else {
+ symbol_table = NULL;
+ }
+ if (symbol_table
+ && !zend_hash_exists(symbol_table, "PATH_TRANSLATED", sizeof("PATH_TRANSLATED"))
+ && zend_hash_find(symbol_table, "SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME"), (void **) &path_translated)==SUCCESS) {
+ php_register_variable("PATH_TRANSLATED", Z_STRVAL_PP(path_translated), track_vars_array TSRMLS_CC);
+ }
+
+ php_register_variable("PHP_SELF", ((request_rec *) SG(server_context))->uri, track_vars_array TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ php_apache_startup
+ */
+static int php_apache_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &apache_module_entry, 1) == FAILURE) {
+ return FAILURE;
+ } else {
+ return SUCCESS;
+ }
+}
+/* }}} */
+
+/* {{{ php_apache_log_message
+ */
+static void php_apache_log_message(char *message TSRMLS_DC)
+{
+ if (SG(server_context)) {
+#if MODULE_MAGIC_NUMBER >= 19970831
+ aplog_error(NULL, 0, APLOG_ERR | APLOG_NOERRNO, ((request_rec *) SG(server_context))->server, "%s", message);
+#else
+ log_error(message, ((request_rec *) SG(server_context))->server);
+#endif
+ } else {
+ fprintf(stderr, "%s", message);
+ fprintf(stderr, "\n");
+ }
+}
+/* }}} */
+
+/* {{{ php_apache_request_shutdown
+ */
+static void php_apache_request_shutdown(void *dummy)
+{
+ TSRMLS_FETCH();
+ AP(current_hook) = AP_CLEANUP;
+ php_output_set_status(PHP_OUTPUT_DISABLED TSRMLS_CC);
+ SG(server_context) = NULL; /* The server context (request) is invalid by the time run_cleanups() is called */
+ if(SG(sapi_started)) {
+ php_request_shutdown(dummy);
+ SG(sapi_started) = 0;
+ }
+ AP(in_request) = 0;
+ if(AP(setup_env)) {
+ AP(setup_env) = 0;
+ }
+ AP(current_hook) = AP_WAITING_FOR_REQUEST;
+ AP(headers_sent) = 0;
+}
+/* }}} */
+
+/* {{{ php_apache_sapi_activate
+ */
+static int php_apache_sapi_activate(TSRMLS_D)
+{
+ request_rec *r = (request_rec *) SG(server_context);
+
+ /*
+ * For the Apache module version, this bit of code registers a cleanup
+ * function that gets triggered when our request pool is destroyed.
+ * We need this because at any point in our code we can be interrupted
+ * and that may happen before we have had time to free our memory.
+ * The php_request_shutdown function needs to free all outstanding allocated
+ * memory.
+ */
+ block_alarms();
+ register_cleanup(r->pool, NULL, php_apache_request_shutdown, php_request_shutdown_for_exec);
+ AP(in_request)=1;
+ unblock_alarms();
+
+ /* Override the default headers_only value - sometimes "GET" requests should actually only
+ * send headers.
+ */
+ SG(request_info).headers_only = r->header_only;
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ php_apache_get_stat
+ */
+static struct stat *php_apache_get_stat(TSRMLS_D)
+{
+ return &((request_rec *) SG(server_context))->finfo;
+}
+/* }}} */
+
+/* {{{ php_apache_getenv
+ */
+static char *php_apache_getenv(char *name, size_t name_len TSRMLS_DC)
+{
+ return (char *) table_get(((request_rec *) SG(server_context))->subprocess_env, name);
+}
+/* }}} */
+
+/* {{{ sapi_module_struct apache_sapi_module
+ */
+static sapi_module_struct apache_sapi_module = {
+ "apache", /* name */
+ "Apache", /* pretty name */
+
+ php_apache_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ php_apache_sapi_activate, /* activate */
+ NULL, /* deactivate */
+
+ sapi_apache_ub_write, /* unbuffered write */
+ sapi_apache_flush, /* flush */
+ php_apache_get_stat, /* get uid */
+ php_apache_getenv, /* getenv */
+
+ php_error, /* error handler */
+
+ sapi_apache_header_handler, /* header handler */
+ sapi_apache_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ sapi_apache_read_post, /* read POST data */
+ sapi_apache_read_cookies, /* read Cookies */
+
+ sapi_apache_register_server_variables, /* register server variables */
+ php_apache_log_message, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* child terminate */
+
+ NULL, /* php.ini path override */
+
+#ifdef PHP_WIN32
+ NULL,
+ NULL,
+#else
+ block_alarms, /* Block interruptions */
+ unblock_alarms, /* Unblock interruptions */
+#endif
+
+ NULL, /* default post reader */
+ NULL, /* treat data */
+ NULL, /* exe location */
+ 0, /* ini ignore */
+ NULL
+
+};
+/* }}} */
+
+/* {{{ php_restore_umask
+ */
+static void php_restore_umask(void)
+{
+ umask(saved_umask);
+}
+/* }}} */
+
+/* {{{ init_request_info
+ */
+static void init_request_info(TSRMLS_D)
+{
+ request_rec *r = ((request_rec *) SG(server_context));
+ char *content_length = (char *) table_get(r->subprocess_env, "CONTENT_LENGTH");
+ const char *authorization=NULL;
+ char *tmp, *tmp_user;
+
+ SG(request_info).query_string = r->args;
+ SG(request_info).path_translated = r->filename;
+ SG(request_info).request_uri = r->uri;
+ SG(request_info).request_method = (char *)r->method;
+ SG(request_info).proto_num = r->proto_num;
+ SG(request_info).content_type = (char *) table_get(r->subprocess_env, "CONTENT_TYPE");
+ SG(request_info).content_length = (content_length ? atol(content_length) : 0);
+ SG(sapi_headers).http_response_code = r->status;
+
+ if (r->headers_in) {
+ authorization = table_get(r->headers_in, "Authorization");
+ }
+
+ SG(request_info).auth_user = NULL;
+ SG(request_info).auth_password = NULL;
+
+ if (authorization && !auth_type(r)) {
+ if (!strcasecmp(getword(r->pool, &authorization, ' '), "Basic")) {
+ tmp = uudecode(r->pool, authorization);
+ tmp_user = getword_nulls_nc(r->pool, &tmp, ':');
+ if (tmp_user) {
+ r->connection->user = pstrdup(r->connection->pool, tmp_user);
+ r->connection->ap_auth_type = "Basic";
+ SG(request_info).auth_user = estrdup(tmp_user);
+ }
+ if (tmp) {
+ SG(request_info).auth_password = estrdup(tmp);
+ }
+ } else if (!strcasecmp(getword(r->pool, &authorization, ' '), "Digest")) {
+ r->connection->ap_auth_type = "Digest";
+ SG(request_info).auth_digest = estrdup(authorization);
+ }
+ }
+}
+/* }}} */
+
+/* {{{ php_apache_alter_ini_entries
+ */
+static int php_apache_alter_ini_entries(php_per_dir_entry *per_dir_entry TSRMLS_DC)
+{
+ zend_alter_ini_entry(per_dir_entry->key, per_dir_entry->key_length+1, per_dir_entry->value, per_dir_entry->value_length, per_dir_entry->type, PHP_INI_STAGE_ACTIVATE);
+ return 0;
+}
+/* }}} */
+
+/* {{{ php_apache_get_default_mimetype
+ */
+static char *php_apache_get_default_mimetype(request_rec *r TSRMLS_DC)
+{
+
+ char *mimetype;
+ if (SG(default_mimetype) || SG(default_charset)) {
+ /* Assume output will be of the default MIME type. Individual
+ scripts may change this later. */
+ char *tmpmimetype;
+ tmpmimetype = sapi_get_default_content_type(TSRMLS_C);
+ mimetype = pstrdup(r->pool, tmpmimetype);
+ efree(tmpmimetype);
+ } else {
+ mimetype = SAPI_DEFAULT_MIMETYPE "; charset=" SAPI_DEFAULT_CHARSET;
+ }
+ return mimetype;
+}
+/* }}} */
+
+/* {{{ send_php
+ */
+static int send_php(request_rec *r, int display_source_mode, char *filename)
+{
+ int retval;
+ php_per_dir_config *per_dir_conf;
+ TSRMLS_FETCH();
+ if (AP(in_request)) {
+ zend_file_handle fh;
+
+ fh.filename = r->filename;
+ fh.opened_path = NULL;
+ fh.free_filename = 0;
+ fh.type = ZEND_HANDLE_FILENAME;
+
+ zend_execute_scripts(ZEND_INCLUDE TSRMLS_CC, NULL, 1, &fh);
+ return OK;
+ }
+
+ zend_first_try {
+
+ /* Make sure file exists */
+ if (filename == NULL && r->finfo.st_mode == 0) {
+ return DECLINED;
+ }
+
+ per_dir_conf = (php_per_dir_config *) get_module_config(r->per_dir_config, &php5_module);
+ if (per_dir_conf) {
+ zend_hash_apply((HashTable *) per_dir_conf->ini_settings, (apply_func_t) php_apache_alter_ini_entries TSRMLS_CC);
+ }
+
+ /* If PHP parser engine has been turned off with an "engine off"
+ * directive, then decline to handle this request
+ */
+ if (!AP(engine)) {
+ r->content_type = php_apache_get_default_mimetype(r TSRMLS_CC);
+ r->allowed |= (1 << METHODS) - 1;
+ zend_try {
+ zend_ini_deactivate(TSRMLS_C);
+ } zend_end_try();
+ return DECLINED;
+ }
+ if (filename == NULL) {
+ filename = r->filename;
+ }
+
+ /* Apache 1.2 has a more complex mechanism for reading POST data */
+#if MODULE_MAGIC_NUMBER > 19961007
+ if ((retval = setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
+ zend_try {
+ zend_ini_deactivate(TSRMLS_C);
+ } zend_end_try();
+ return retval;
+ }
+#endif
+
+ if (AP(last_modified)) {
+#if MODULE_MAGIC_NUMBER < 19970912
+ if ((retval = set_last_modified(r, r->finfo.st_mtime))) {
+ zend_try {
+ zend_ini_deactivate(TSRMLS_C);
+ } zend_end_try();
+ return retval;
+ }
+#else
+ update_mtime (r, r->finfo.st_mtime);
+ set_last_modified(r);
+ set_etag(r);
+#endif
+ }
+ /* Assume output will be of the default MIME type. Individual
+ scripts may change this later in the request. */
+ r->content_type = php_apache_get_default_mimetype(r TSRMLS_CC);
+
+ /* Init timeout */
+ hard_timeout("send", r);
+
+ SG(server_context) = r;
+
+ php_save_umask();
+ if(!AP(setup_env)) {
+ AP(setup_env) = 1;
+ add_common_vars(r);
+ add_cgi_vars(r);
+ }
+ init_request_info(TSRMLS_C);
+ apache_php_module_main(r, display_source_mode TSRMLS_CC);
+
+ /* Done, restore umask, turn off timeout, close file and return */
+ php_restore_umask();
+ kill_timeout(r);
+ } zend_end_try();
+
+ return OK;
+}
+/* }}} */
+
+/* {{{ send_parsed_php
+ */
+static int send_parsed_php(request_rec * r)
+{
+ int result = send_php(r, 0, NULL);
+ TSRMLS_FETCH();
+
+ ap_table_setn(r->notes, "mod_php_memory_usage",
+ ap_psprintf(r->pool, "%u", zend_memory_peak_usage(1 TSRMLS_CC)));
+
+ return result;
+}
+/* }}} */
+
+/* {{{ send_parsed_php_source
+ */
+static int send_parsed_php_source(request_rec * r)
+{
+ return send_php(r, 1, NULL);
+}
+/* }}} */
+
+
+/* {{{ destroy_per_dir_entry
+ */
+static void destroy_per_dir_entry(php_per_dir_entry *per_dir_entry)
+{
+ free(per_dir_entry->key);
+ free(per_dir_entry->value);
+}
+/* }}} */
+
+/* {{{ copy_per_dir_entry
+ */
+static void copy_per_dir_entry(php_per_dir_entry *per_dir_entry)
+{
+ php_per_dir_entry tmp = *per_dir_entry;
+
+ per_dir_entry->key = (char *) malloc(tmp.key_length+1);
+ memcpy(per_dir_entry->key, tmp.key, tmp.key_length);
+ per_dir_entry->key[per_dir_entry->key_length] = 0;
+
+ per_dir_entry->value = (char *) malloc(tmp.value_length+1);
+ memcpy(per_dir_entry->value, tmp.value, tmp.value_length);
+ per_dir_entry->value[per_dir_entry->value_length] = 0;
+}
+/* }}} */
+
+/* {{{ should_overwrite_per_dir_entry;
+
+ */
+static zend_bool should_overwrite_per_dir_entry(HashTable *target_ht, php_per_dir_entry *orig_per_dir_entry, zend_hash_key *hash_key, void *pData)
+{
+ php_per_dir_entry *new_per_dir_entry;
+
+ if (zend_hash_find(target_ht, hash_key->arKey, hash_key->nKeyLength, (void **) &new_per_dir_entry)==FAILURE) {
+ return 1; /* does not exist in dest, copy from source */
+ }
+
+ if (new_per_dir_entry->type==PHP_INI_SYSTEM
+ && orig_per_dir_entry->type!=PHP_INI_SYSTEM) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+/* }}} */
+/* {{{ php_destroy_per_server_info
+ */
+static void php_destroy_per_server_info(php_per_server_config *conf)
+{
+ php_handler_stack_destroy(&conf->requires);
+ php_handler_stack_destroy(&conf->uri_handlers);
+}
+/* }}} */
+
+/* {{{ php_destroy_per_dir_info
+ */
+static void php_destroy_per_dir_info(php_per_dir_config *conf)
+{
+ zend_hash_destroy(conf->ini_settings);
+ php_handler_stack_destroy(&conf->response_handlers);
+ php_handler_stack_destroy(&conf->auth_handlers);
+ php_handler_stack_destroy(&conf->access_handlers);
+ php_handler_stack_destroy(&conf->type_handlers);
+ php_handler_stack_destroy(&conf->fixup_handlers);
+ php_handler_stack_destroy(&conf->logger_handlers);
+ php_handler_stack_destroy(&conf->post_read_handlers);
+ php_handler_stack_destroy(&conf->headers_handlers);
+ free(conf->ini_settings);
+}
+/* }}} */
+
+/* {{{ php_create_server
+ */
+static void *php_create_server(pool *p, char *dummy)
+{
+ php_per_server_config *conf;
+ conf = (php_per_server_config *) malloc(sizeof(php_per_server_config));
+ register_cleanup(p, (void *) conf, (void (*)(void *)) php_destroy_per_server_info, (void (*)(void *)) php_destroy_per_server_info);
+
+ sapi_stack_init_ex(&conf->requires, 1);
+ sapi_stack_init_ex(&conf->uri_handlers, 1);
+ return conf;
+}
+
+/* }}} */
+
+
+/* {{{ php_create_dir
+ */
+static void *php_create_dir(pool *p, char *dummy)
+{
+ php_per_dir_config *conf;
+ conf = (php_per_dir_config *) malloc(sizeof(php_per_dir_config));
+ conf->ini_settings = (HashTable *) malloc(sizeof(HashTable));
+ zend_hash_init_ex(conf->ini_settings, 5, NULL, (void (*)(void *)) destroy_per_dir_entry, 1, 0);
+ sapi_stack_init_ex(&conf->response_handlers, 1);
+ sapi_stack_init_ex(&conf->headers_handlers, 1);
+ sapi_stack_init_ex(&conf->auth_handlers, 1);
+ sapi_stack_init_ex(&conf->access_handlers, 1);
+ sapi_stack_init_ex(&conf->type_handlers, 1);
+ sapi_stack_init_ex(&conf->fixup_handlers, 1);
+ sapi_stack_init_ex(&conf->logger_handlers, 1);
+ sapi_stack_init_ex(&conf->post_read_handlers, 1);
+ register_cleanup(p, (void *) conf, (void (*)(void *)) php_destroy_per_dir_info, (void (*)(void *)) php_destroy_per_dir_info);
+
+ return conf;
+}
+
+/* }}} */
+
+/* {{{ php_merge_dir
+ */
+static void *php_merge_dir(pool *p, void *basev, void *addv)
+{
+ php_per_dir_config *a = (php_per_dir_config *) addv;
+ php_per_dir_config *b = (php_per_dir_config *) basev;
+ /* This function *must* return addv, and not modify basev */
+ zend_hash_merge_ex((HashTable *) a->ini_settings, (HashTable *) b->ini_settings, (copy_ctor_func_t) copy_per_dir_entry, sizeof(php_per_dir_entry), (merge_checker_func_t) should_overwrite_per_dir_entry, NULL);
+ a->headers_handlers = (a->headers_handlers.top)?a->headers_handlers:b->headers_handlers;
+ a->auth_handlers = (a->auth_handlers.top)?a->auth_handlers:b->auth_handlers;
+ a->access_handlers = (a->access_handlers.top)?a->access_handlers:b->access_handlers;
+ a->type_handlers = (a->type_handlers.top)?a->type_handlers:b->type_handlers;
+ a->fixup_handlers = (a->fixup_handlers.top)?a->fixup_handlers:b->fixup_handlers;
+ a->logger_handlers = (a->logger_handlers.top)?a->logger_handlers:b->logger_handlers;
+ a->post_read_handlers = (a->post_read_handlers.top)?a->post_read_handlers:b->post_read_handlers;
+ a->response_handlers = (a->response_handlers.top)?a->response_handlers:b->response_handlers;
+ return a;
+}
+/* }}} */
+
+/* {{{ php_apache_value_handler_ex
+ */
+static CONST_PREFIX char *php_apache_value_handler_ex(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2, int mode)
+{
+ php_per_dir_entry per_dir_entry;
+
+ if (!apache_php_initialized) {
+ apache_php_initialized = 1;
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+#endif
+ sapi_startup(&apache_sapi_module);
+ php_apache_startup(&apache_sapi_module);
+ }
+ per_dir_entry.type = mode;
+
+ if (strcasecmp(arg2, "none") == 0) {
+ arg2 = "";
+ }
+
+ per_dir_entry.key_length = strlen(arg1);
+ per_dir_entry.value_length = strlen(arg2);
+
+ per_dir_entry.key = (char *) malloc(per_dir_entry.key_length+1);
+ memcpy(per_dir_entry.key, arg1, per_dir_entry.key_length);
+ per_dir_entry.key[per_dir_entry.key_length] = 0;
+
+ per_dir_entry.value = (char *) malloc(per_dir_entry.value_length+1);
+ memcpy(per_dir_entry.value, arg2, per_dir_entry.value_length);
+ per_dir_entry.value[per_dir_entry.value_length] = 0;
+
+ zend_hash_update(conf, per_dir_entry.key, per_dir_entry.key_length, &per_dir_entry, sizeof(php_per_dir_entry), NULL);
+ return NULL;
+}
+/* }}} */
+
+static CONST_PREFIX char *php_set_server_handler(server_rec *s, char *arg1, long handler_stage, long handler_type)
+{
+ php_per_server_config *conf;
+ php_handler *handler;
+ handler = (php_handler *) malloc(sizeof(php_handler));
+ handler->type = handler_type;
+ handler->stage = handler_stage;
+ handler->name = strdup(arg1);
+ conf = get_module_config(s->module_config, &php5_module);
+ switch(handler_stage) {
+ case AP_URI_TRANS:
+ sapi_stack_push(&conf->uri_handlers, handler);
+ break;
+ default:
+ sapi_stack_push(&conf->requires, handler);
+ break;
+ }
+ return NULL;
+}
+
+static CONST_PREFIX char *php_set_dir_handler(php_per_dir_config *conf, char *arg1, long handler_stage, long handler_type)
+{
+ php_handler *handler;
+ handler = (php_handler *) malloc(sizeof(php_handler));
+ handler->type = handler_type;
+ handler->stage = handler_stage;
+ handler->name = strdup(arg1);
+ switch(handler_stage) {
+ case AP_POST_READ:
+ sapi_stack_push(&conf->post_read_handlers, handler);
+ break;
+ case AP_HEADER_PARSE:
+ sapi_stack_push(&conf->headers_handlers, handler);
+ break;
+ case AP_ACCESS_CONTROL:
+ sapi_stack_push(&conf->access_handlers, handler);
+ break;
+ case AP_AUTHENTICATION:
+ sapi_stack_push(&conf->auth_handlers, handler);
+ break;
+ case AP_AUTHORIZATION:
+ break;
+ case AP_TYPE_CHECKING:
+ sapi_stack_push(&conf->type_handlers, handler);
+ break;
+ case AP_FIXUP:
+ sapi_stack_push(&conf->fixup_handlers, handler);
+ break;
+ case AP_RESPONSE:
+ sapi_stack_push(&conf->response_handlers, handler);
+ break;
+ case AP_LOGGING:
+ sapi_stack_push(&conf->logger_handlers, handler);
+ break;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+/* {{{ php_set_uri_handler
+ */
+static CONST_PREFIX char *php_set_uri_handler(cmd_parms *cmd, void *dummy, char *arg1)
+{
+ return php_set_server_handler(cmd->server, arg1, AP_URI_TRANS, AP_HANDLER_TYPE_FILE);
+}
+/* }}} */
+
+/* {{{ php_set_uri_handler_code */
+static CONST_PREFIX char *php_set_uri_handler_code(cmd_parms *cmd, void *dummy, char *arg1)
+{
+ return php_set_server_handler(cmd->server, arg1, AP_URI_TRANS, AP_HANDLER_TYPE_METHOD);
+}
+/* }}} */
+
+/* {{{ php_set_header_handler
+ */
+static CONST_PREFIX char *php_set_header_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_HEADER_PARSE, AP_HANDLER_TYPE_FILE);
+}
+static CONST_PREFIX char *php_set_header_handler_code(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_HEADER_PARSE, AP_HANDLER_TYPE_METHOD);
+}
+/* }}} */
+
+/* {{{ php_set_auth_handler
+ */
+static CONST_PREFIX char *php_set_auth_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_AUTHENTICATION, AP_HANDLER_TYPE_FILE);
+}
+static CONST_PREFIX char *php_set_auth_handler_code(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_AUTHENTICATION, AP_HANDLER_TYPE_METHOD);
+}
+
+/* }}} */
+
+/* {{{ php_set_access_handler
+ */
+static CONST_PREFIX char *php_set_access_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_ACCESS_CONTROL, AP_HANDLER_TYPE_FILE);
+}
+static CONST_PREFIX char *php_set_access_handler_code(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_ACCESS_CONTROL, AP_HANDLER_TYPE_METHOD);
+}
+
+/* }}} */
+
+/* {{{ php_set_type_handler
+ */
+static CONST_PREFIX char *php_set_type_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_TYPE_CHECKING, AP_HANDLER_TYPE_FILE);
+}
+static CONST_PREFIX char *php_set_type_handler_code(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_TYPE_CHECKING, AP_HANDLER_TYPE_METHOD);
+}
+
+/* }}} */
+
+/* {{{ php_set_fixup_handler
+ */
+static CONST_PREFIX char *php_set_fixup_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_FIXUP, AP_HANDLER_TYPE_FILE);
+}
+static CONST_PREFIX char *php_set_fixup_handler_code(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_FIXUP, AP_HANDLER_TYPE_METHOD);
+}
+/* }}} */
+
+/* {{{ php_set_logger_handler
+ */
+static CONST_PREFIX char *php_set_logger_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_LOGGING, AP_HANDLER_TYPE_FILE);
+}
+static CONST_PREFIX char *php_set_logger_handler_code(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_LOGGING, AP_HANDLER_TYPE_METHOD);
+}
+
+/* }}} */
+
+/* {{{ php_set_post_read_handler
+ */
+static CONST_PREFIX char *php_set_post_read_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_POST_READ, AP_HANDLER_TYPE_FILE);
+}
+static CONST_PREFIX char *php_set_post_read_handler_code(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_POST_READ, AP_HANDLER_TYPE_METHOD);
+}
+
+
+/* }}} */
+
+/* {{{ php_set_require
+ */
+
+static CONST_PREFIX char *php_set_require(cmd_parms *cmd, void *dummy, char *arg1)
+{
+ return php_set_server_handler(cmd->server, arg1, 0, AP_HANDLER_TYPE_FILE);
+}
+/* }}} */
+
+/* {{{ php_set_response_handler
+ */
+static CONST_PREFIX char *php_set_response_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_RESPONSE, AP_HANDLER_TYPE_FILE);
+}
+static CONST_PREFIX char *php_set_response_handler_code(cmd_parms *cmd, php_per_dir_config *conf, char *arg1)
+{
+ return php_set_dir_handler(conf, arg1, AP_RESPONSE, AP_HANDLER_TYPE_METHOD);
+}
+/* }}} */
+
+/* {{{ php_apache_value_handler
+ */
+static CONST_PREFIX char *php_apache_value_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1, char *arg2)
+{
+ return php_apache_value_handler_ex(cmd, conf->ini_settings, arg1, arg2, PHP_INI_PERDIR);
+}
+/* }}} */
+
+/* {{{ php_apache_admin_value_handler
+ */
+static CONST_PREFIX char *php_apache_admin_value_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1, char *arg2)
+{
+ return php_apache_value_handler_ex(cmd, conf->ini_settings, arg1, arg2, PHP_INI_SYSTEM);
+}
+/* }}} */
+
+/* {{{ php_apache_flag_handler_ex
+ */
+static CONST_PREFIX char *php_apache_flag_handler_ex(cmd_parms *cmd, HashTable *conf, char *arg1, char *arg2, int mode)
+{
+ char bool_val[2];
+
+ if (!strcasecmp(arg2, "On")) {
+ bool_val[0] = '1';
+ } else {
+ bool_val[0] = '0';
+ }
+ bool_val[1] = 0;
+
+ return php_apache_value_handler_ex(cmd, conf, arg1, bool_val, mode);
+}
+/* }}} */
+
+/* {{{ php_apache_flag_handler
+ */
+static CONST_PREFIX char *php_apache_flag_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1, char *arg2)
+{
+ return php_apache_flag_handler_ex(cmd, conf->ini_settings, arg1, arg2, PHP_INI_PERDIR);
+}
+/* }}} */
+
+/* {{{ php_apache_admin_flag_handler
+ */
+static CONST_PREFIX char *php_apache_admin_flag_handler(cmd_parms *cmd, php_per_dir_config *conf, char *arg1, char *arg2)
+{
+ return php_apache_flag_handler_ex(cmd, conf->ini_settings, arg1, arg2, PHP_INI_SYSTEM);
+}
+/* }}} */
+
+/* {{{ php_apache_phpini_set
+ */
+static CONST_PREFIX char *php_apache_phpini_set(cmd_parms *cmd, HashTable *conf, char *arg)
+{
+ if (apache_sapi_module.php_ini_path_override) {
+ return "Only first PHPINIDir directive honored per configuration tree - subsequent ones ignored";
+ }
+ apache_sapi_module.php_ini_path_override = ap_server_root_relative(cmd->pool, arg);
+ return NULL;
+}
+/* }}} */
+
+/* {{{ int php_xbithack_handler(request_rec * r)
+ */
+static int php_xbithack_handler(request_rec * r)
+{
+ php_per_dir_config *conf;
+ TSRMLS_FETCH();
+
+ if (!(r->finfo.st_mode & S_IXUSR)) {
+ r->allowed |= (1 << METHODS) - 1;
+ return DECLINED;
+ }
+ conf = (php_per_dir_config *) get_module_config(r->per_dir_config, &php5_module);
+ if (conf) {
+ zend_hash_apply((HashTable *) conf->ini_settings, (apply_func_t) php_apache_alter_ini_entries TSRMLS_CC);
+ }
+ if(!AP(xbithack)) {
+ r->allowed |= (1 << METHODS) - 1;
+ zend_try {
+ zend_ini_deactivate(TSRMLS_C);
+ } zend_end_try();
+ return DECLINED;
+ }
+ return send_parsed_php(r);
+}
+/* }}} */
+
+/* {{{ apache_php_module_shutdown_wrapper
+ */
+static void apache_php_module_shutdown_wrapper(void)
+{
+ apache_php_initialized = 0;
+ apache_sapi_module.shutdown(&apache_sapi_module);
+
+#if MODULE_MAGIC_NUMBER >= 19970728
+ /* This function is only called on server exit if the apache API
+ * child_exit handler exists, so shutdown globally
+ */
+ sapi_shutdown();
+#endif
+
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+}
+/* }}} */
+
+#if MODULE_MAGIC_NUMBER >= 19970728
+/* {{{ php_child_exit_handler
+ */
+static void php_child_exit_handler(server_rec *s, pool *p)
+{
+/* apache_php_initialized = 0; */
+ apache_sapi_module.shutdown(&apache_sapi_module);
+
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+}
+/* }}} */
+#endif
+
+/* {{{ void php_init_handler(server_rec *s, pool *p)
+ */
+static void php_init_handler(server_rec *s, pool *p)
+{
+ register_cleanup(p, NULL, (void (*)(void *))apache_php_module_shutdown_wrapper, (void (*)(void *))php_module_shutdown_for_exec);
+ if (!apache_php_initialized) {
+ apache_php_initialized = 1;
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+#endif
+ sapi_startup(&apache_sapi_module);
+ php_apache_startup(&apache_sapi_module);
+ }
+#if MODULE_MAGIC_NUMBER >= 19980527
+ {
+ TSRMLS_FETCH();
+ if (PG(expose_php)) {
+ ap_add_version_component("PHP/" PHP_VERSION);
+ }
+ }
+#endif
+}
+/* }}} */
+
+static int php_run_hook(php_handler *handler, request_rec *r)
+{
+ zval *ret = NULL;
+ php_per_dir_config *conf;
+
+ TSRMLS_FETCH();
+
+ if(!AP(apache_config_loaded)) {
+ conf = (php_per_dir_config *) get_module_config(r->per_dir_config, &php5_module);
+ if (conf)
+ zend_hash_apply((HashTable *)conf->ini_settings, (apply_func_t) php_apache_alter_ini_entries TSRMLS_CC);
+ AP(apache_config_loaded) = 1;
+ }
+ if (!handler->name) {
+ return DECLINED;
+ }
+ php_save_umask();
+ if (!AP(setup_env)) {
+ AP(setup_env) = 1;
+ add_common_vars(r);
+ add_cgi_vars(r);
+ }
+ SG(server_context) = r;
+ init_request_info(TSRMLS_C);
+ apache_php_module_hook(r, handler, &ret TSRMLS_CC);
+ php_restore_umask();
+ kill_timeout(r);
+ if (ret) {
+ convert_to_long(ret);
+ return Z_LVAL_P(ret);
+ }
+ return HTTP_INTERNAL_SERVER_ERROR;
+}
+
+
+static int php_uri_translation(request_rec *r)
+{
+ php_per_server_config *conf;
+ TSRMLS_FETCH();
+ AP(current_hook) = AP_URI_TRANS;
+ conf = (php_per_server_config *) get_module_config(r->server->module_config, &php5_module);
+ return sapi_stack_apply_with_argument_stop_if_equals(&conf->uri_handlers,
+ ZEND_STACK_APPLY_BOTTOMUP,
+ (int (*)(void *element, void *)) php_run_hook, r, OK);
+}
+
+static int php_header_hook(request_rec *r)
+{
+ php_per_dir_config *conf;
+ TSRMLS_FETCH();
+ AP(current_hook) = AP_HEADER_PARSE;
+ conf = (php_per_dir_config *) get_module_config(r->per_dir_config, &php5_module);
+ return sapi_stack_apply_with_argument_stop_if_http_error(&conf->headers_handlers,
+ ZEND_STACK_APPLY_BOTTOMUP,
+ (int (*)(void *element, void *)) php_run_hook, r);
+}
+
+static int php_auth_hook(request_rec *r)
+{
+ php_per_dir_config *conf;
+ TSRMLS_FETCH();
+ AP(current_hook) = AP_AUTHENTICATION;
+ conf = (php_per_dir_config *) get_module_config(r->per_dir_config, &php5_module);
+ return sapi_stack_apply_with_argument_stop_if_equals(&conf->auth_handlers,
+ ZEND_STACK_APPLY_BOTTOMUP,
+ (int (*)(void *element, void *)) php_run_hook, r, OK);
+}
+
+static int php_access_hook(request_rec *r)
+{
+ php_per_dir_config *conf;
+ int status = DECLINED;
+ TSRMLS_FETCH();
+ AP(current_hook) = AP_ACCESS_CONTROL;
+ conf = (php_per_dir_config *) get_module_config(r->per_dir_config, &php5_module);
+ status = sapi_stack_apply_with_argument_stop_if_http_error(&conf->access_handlers,
+ ZEND_STACK_APPLY_BOTTOMUP,
+ (int (*)(void *element, void *)) php_run_hook, r);
+ return status;
+
+}
+
+static int php_type_hook(request_rec *r)
+{
+ php_per_dir_config *conf;
+ TSRMLS_FETCH();
+ AP(current_hook) = AP_TYPE_CHECKING;
+ conf = (php_per_dir_config *) get_module_config(r->per_dir_config, &php5_module);
+ return sapi_stack_apply_with_argument_stop_if_equals(&conf->type_handlers,
+ ZEND_STACK_APPLY_BOTTOMUP,
+ (int (*)(void *element, void *)) php_run_hook,
+ r, OK);
+}
+
+static int php_fixup_hook(request_rec *r)
+{
+ php_per_dir_config *conf;
+ TSRMLS_FETCH();
+ AP(current_hook) = AP_FIXUP;
+ conf = (php_per_dir_config *) get_module_config(r->per_dir_config, &php5_module);
+ return sapi_stack_apply_with_argument_stop_if_http_error(&conf->fixup_handlers,
+ ZEND_STACK_APPLY_BOTTOMUP,
+ (int (*)(void *element, void *)) php_run_hook,
+ r);
+}
+
+static int php_logger_hook(request_rec *r)
+{
+ php_per_dir_config *conf;
+ TSRMLS_FETCH();
+ AP(current_hook) = AP_LOGGING;
+ conf = (php_per_dir_config *) get_module_config(r->per_dir_config, &php5_module);
+ return sapi_stack_apply_with_argument_stop_if_http_error(&conf->logger_handlers,
+ ZEND_STACK_APPLY_BOTTOMUP,
+ (int (*)(void *element, void *)) php_run_hook,
+ r);
+}
+
+static int php_post_read_hook(request_rec *r)
+{
+ php_per_dir_config *conf;
+ php_per_server_config *svr;
+ TSRMLS_FETCH();
+ AP(current_hook) = AP_POST_READ;
+ svr = get_module_config(r->server->module_config, &php5_module);
+ if(ap_is_initial_req(r)) {
+ sapi_stack_apply_with_argument_all(&svr->requires, ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_run_hook, r);
+ }
+ conf = (php_per_dir_config *) get_module_config(r->per_dir_config, &php5_module);
+ return sapi_stack_apply_with_argument_stop_if_http_error(&conf->post_read_handlers,
+ ZEND_STACK_APPLY_BOTTOMUP,
+ (int (*)(void *element, void *)) php_run_hook, r);
+}
+
+static int php_response_handler(request_rec *r)
+{
+ php_per_dir_config *conf;
+ TSRMLS_FETCH();
+ AP(current_hook) = AP_RESPONSE;
+ conf = (php_per_dir_config *) get_module_config(r->per_dir_config, &php5_module);
+ return sapi_stack_apply_with_argument_all(&conf->response_handlers, ZEND_STACK_APPLY_BOTTOMUP, (int (*)(void *element, void *)) php_run_hook, r);
+}
+
+/* {{{ handler_rec php_handlers[]
+ */
+handler_rec php_handlers[] =
+{
+ {"application/x-httpd-php", send_parsed_php},
+ {"application/x-httpd-php-source", send_parsed_php_source},
+ {"text/html", php_xbithack_handler},
+ {"php-script", php_response_handler},
+ {NULL}
+};
+/* }}} */
+
+/* {{{ command_rec php_commands[]
+ */
+command_rec php_commands[] =
+{
+ {"php_value", php_apache_value_handler, NULL, OR_OPTIONS, TAKE2, "PHP Value Modifier"},
+ {"phpUriHandler", php_set_uri_handler, NULL, RSRC_CONF, TAKE1, "PHP Value Modifier"},
+ {"phpUriHandlerMethod", php_set_uri_handler_code, NULL, RSRC_CONF, TAKE1, "PHP Value Modifier"},
+#if MODULE_MAGIC_NUMBER >= 19970103
+ {"phpHeaderHandler", php_set_header_handler, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpHeaderHandlerMethod", php_set_header_handler_code, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+#endif
+ {"phpAuthHandler", php_set_auth_handler, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpAuthHandlerMethod", php_set_auth_handler_code, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpAccessHandler", php_set_access_handler, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpAccessHandlerMethod", php_set_access_handler_code, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpTypeHandler", php_set_type_handler, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpTypeHandlerMethod", php_set_type_handler_code, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpFixupHandler", php_set_fixup_handler, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpFixupHandlerMethod", php_set_fixup_handler_code, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpLoggerHandler", php_set_logger_handler, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpLoggerHandlerMethod", php_set_logger_handler_code, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+#if MODULE_MAGIC_NUMBER >= 19970902
+ {"phpPostReadHandler", php_set_post_read_handler, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpPostReadHandlerMethod", php_set_post_read_handler_code, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpRequire", php_set_require, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpResponseHandler", php_set_response_handler, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+ {"phpResponseHandlerMethod", php_set_response_handler_code, NULL, OR_OPTIONS, TAKE1, "PHP Value Modifier"},
+#endif
+ {"php_flag", php_apache_flag_handler, NULL, OR_OPTIONS, TAKE2, "PHP Flag Modifier"},
+ {"php_admin_value", php_apache_admin_value_handler, NULL, ACCESS_CONF|RSRC_CONF, TAKE2, "PHP Value Modifier (Admin)"},
+ {"php_admin_flag", php_apache_admin_flag_handler, NULL, ACCESS_CONF|RSRC_CONF, TAKE2, "PHP Flag Modifier (Admin)"},
+ {"PHPINIDir", php_apache_phpini_set, NULL, RSRC_CONF, TAKE1, "Directory containing the php.ini file"},
+ {NULL}
+};
+/* }}} */
+
+/* {{{ module MODULE_VAR_EXPORT php5_module
+ */
+module MODULE_VAR_EXPORT php5_module =
+{
+ STANDARD_MODULE_STUFF,
+ php_init_handler, /* initializer */
+ php_create_dir, /* per-directory config creator */
+ php_merge_dir, /* dir merger */
+ php_create_server, /* per-server config creator */
+ NULL, /* merge server config */
+ php_commands, /* command table */
+ php_handlers, /* handlers */
+ php_uri_translation, /* filename translation */
+ NULL, /* check_user_id */
+ php_auth_hook, /* check auth */
+ php_access_hook, /* check access */
+ php_type_hook, /* type_checker */
+ php_fixup_hook, /* fixups */
+ php_logger_hook /* logger */
+#if MODULE_MAGIC_NUMBER >= 19970103
+ , php_header_hook /* header parser */
+#endif
+#if MODULE_MAGIC_NUMBER >= 19970719
+ , NULL /* child_init */
+#endif
+#if MODULE_MAGIC_NUMBER >= 19970728
+ , php_child_exit_handler /* child_exit */
+#endif
+#if MODULE_MAGIC_NUMBER >= 19970902
+ , php_post_read_hook /* post read-request */
+#endif
+};
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/apache_hooks/mod_php5.exp b/sapi/apache_hooks/mod_php5.exp
new file mode 100644
index 0000000..9ad0f0a
--- /dev/null
+++ b/sapi/apache_hooks/mod_php5.exp
@@ -0,0 +1 @@
+php5_module
diff --git a/sapi/apache_hooks/mod_php5.h b/sapi/apache_hooks/mod_php5.h
new file mode 100644
index 0000000..86a5863
--- /dev/null
+++ b/sapi/apache_hooks/mod_php5.h
@@ -0,0 +1,88 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Rasmus Lerdorf <rasmus@php.net> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#ifndef MOD_PHP5_H
+#define MOD_PHP5_H
+
+#if !defined(WIN32) && !defined(WINNT)
+#ifndef MODULE_VAR_EXPORT
+#define MODULE_VAR_EXPORT
+#endif
+#endif
+
+typedef struct {
+ long engine;
+ long last_modified;
+ long xbithack;
+ long terminate_child;
+ long setup_env;
+ long current_hook;
+ zend_bool in_request;
+ zend_bool apache_config_loaded;
+ zend_bool headers_sent;
+} php_apache_info_struct;
+
+typedef struct _php_handler {
+ long type;
+ long stage;
+ char *name;
+} php_handler;
+
+#define AP_HANDLER_TYPE_FILE 0
+#define AP_HANDLER_TYPE_METHOD 1
+
+extern zend_module_entry apache_module_entry;
+
+#ifdef ZTS
+extern int php_apache_info_id;
+#define AP(v) TSRMG(php_apache_info_id, php_apache_info_struct *, v)
+#else
+extern php_apache_info_struct php_apache_info;
+#define AP(v) (php_apache_info.v)
+#endif
+
+/* defines for the various stages of the apache request */
+#define AP_WAITING_FOR_REQUEST 0
+#define AP_POST_READ 1
+#define AP_URI_TRANS 2
+#define AP_HEADER_PARSE 3
+#define AP_ACCESS_CONTROL 4
+#define AP_AUTHENTICATION 5
+#define AP_AUTHORIZATION 6
+#define AP_TYPE_CHECKING 7
+#define AP_FIXUP 8
+#define AP_RESPONSE 9
+#define AP_LOGGING 10
+#define AP_CLEANUP 11
+
+
+/* fix for gcc4 visibility patch */
+#ifndef PHP_WIN32
+# undef MODULE_VAR_EXPORT
+# define MODULE_VAR_EXPORT PHPAPI
+#endif
+
+#endif /* MOD_PHP5_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/sapi/apache_hooks/php.sym b/sapi/apache_hooks/php.sym
new file mode 100644
index 0000000..9ad0f0a
--- /dev/null
+++ b/sapi/apache_hooks/php.sym
@@ -0,0 +1 @@
+php5_module
diff --git a/sapi/apache_hooks/php5apache_hooks.dsp b/sapi/apache_hooks/php5apache_hooks.dsp
new file mode 100755
index 0000000..cc60f4b
--- /dev/null
+++ b/sapi/apache_hooks/php5apache_hooks.dsp
@@ -0,0 +1,151 @@
+# Microsoft Developer Studio Project File - Name="php5apache_hooks" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=php5apache_hooks - Win32 Release_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "php5apache_hooks.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "php5apache_hooks.mak" CFG="php5apache_hooks - Win32 Release_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "php5apache_hooks - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5apache_hooks - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5apache_hooks - Win32 Release_TS_inline" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "php5apache_hooks - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APACHEPHP5_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\..\regex" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\..\php_build\apache\src\include" /I "..\..\main" /I "..\..\TSRM" /D ZEND_DEBUG=0 /D "NDEBUG" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "APACHEPHP5_EXPORTS" /D "WIN32" /D "_MBCS" /D "APACHE_READDIR_H" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 php5ts.lib ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x60000000" /version:4.0 /dll /machine:I386 /libpath:"..\..\..\php_build\apache\src\corer" /libpath:"..\..\Release_TS" /libpath:"..\..\TSRM\Release_TS" /libpath:"..\..\Zend\Release_TS"
+
+!ELSEIF "$(CFG)" == "php5apache_hooks - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APACHEPHP5_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\..\regex" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\..\php_build\apache\src\include" /I "..\..\main" /I "..\..\TSRM" /D "_DEBUG" /D ZEND_DEBUG=1 /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "APACHEPHP5_EXPORTS" /D "WIN32" /D "_MBCS" /D "APACHE_READDIR_H" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 php5ts_debug.lib ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x60000000" /version:4.0 /dll /incremental:yes /debug /machine:I386 /out:"..\..\Debug_TS/php5apache_hooks.dll" /pdbtype:sept /libpath:"..\..\..\php_build\apache\src\cored" /libpath:"..\..\Debug_TS" /libpath:"..\..\TSRM\Debug_TS" /libpath:"..\..\Zend\Debug_TS"
+
+!ELSEIF "$(CFG)" == "php5apache_hooks - Win32 Release_TS_inline"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS_inline"
+# PROP BASE Intermediate_Dir "Release_TS_inline"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS_inline"
+# PROP Intermediate_Dir "Release_TS_inline"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "APACHEPHP5_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\..\regex" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\..\php_build\apache\src\include" /I "..\..\main" /I "..\..\TSRM" /D ZEND_DEBUG=0 /D "ZEND_WIN32_FORCE_INLINE" /D "NDEBUG" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "APACHEPHP5_EXPORTS" /D "WIN32" /D "_MBCS" /D "APACHE_READDIR_H" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 php5ts.lib ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"\apache\src\corer" /libpath:"..\..\Release_TS_inline" /libpath:"..\..\TSRM\Release_TS_inline" /libpath:"..\..\Zend\Release_TS_inline"
+
+!ENDIF
+
+# Begin Target
+
+# Name "php5apache_hooks - Win32 Release_TS"
+# Name "php5apache_hooks - Win32 Debug_TS"
+# Name "php5apache_hooks - Win32 Release_TS_inline"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\mod_php5.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\php_apache.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sapi_apache.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\mod_php5.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\php_apache_http.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/sapi/apache_hooks/php_apache.c b/sapi/apache_hooks/php_apache.c
new file mode 100644
index 0000000..dde6d88
--- /dev/null
+++ b/sapi/apache_hooks/php_apache.c
@@ -0,0 +1,1972 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
+ | Stig Sæther Bakken <ssb@php.net> |
+ | David Sklar <sklar@student.net> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#include "php_apache_http.h"
+
+#if defined(PHP_WIN32) || defined(NETWARE)
+#include "zend.h"
+#include "ap_compat.h"
+#else
+#include <build-defs.h>
+#endif
+
+#ifdef ZTS
+int php_apache_info_id;
+#else
+php_apache_info_struct php_apache_info;
+#endif
+
+#define SECTION(name) PUTS("<H2 align=\"center\">" name "</H2>\n")
+
+#undef offsetof
+#define offsetof(s_type,field) ((size_t)&(((s_type*)0)->field))
+
+extern module *top_module;
+extern module **ap_loaded_modules;
+static int le_apachereq;
+static zend_class_entry *apacherequest_class_entry;
+
+static void apache_table_to_zval(table *, zval *return_value);
+
+PHP_FUNCTION(virtual);
+PHP_FUNCTION(apache_request_headers);
+PHP_FUNCTION(apache_response_headers);
+PHP_FUNCTION(apachelog);
+PHP_FUNCTION(apache_note);
+PHP_FUNCTION(apache_lookup_uri);
+PHP_FUNCTION(apache_child_terminate);
+PHP_FUNCTION(apache_setenv);
+PHP_FUNCTION(apache_get_version);
+PHP_FUNCTION(apache_get_modules);
+
+PHP_MINFO_FUNCTION(apache);
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apachehooks_virtual, 0, 0, 1)
+ ZEND_ARG_INFO(0, filename)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apachehooks_setenv, 0, 0, 2)
+ ZEND_ARG_INFO(0, variable)
+ ZEND_ARG_INFO(0, value)
+ ZEND_ARG_INFO(0, walk_to_top)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apachehooks_lookup_uri, 0, 0, 1)
+ ZEND_ARG_INFO(0, uri)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_apachehooks__void, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_apachehooks_note, 0, 0, 1)
+ ZEND_ARG_INFO(0, note_name)
+ ZEND_ARG_INFO(0, note_value)
+ZEND_END_ARG_INFO()
+
+const zend_function_entry apache_functions[] = {
+ PHP_FE(virtual, arginfo_apachehooks_virtual)
+ PHP_FE(apache_request_headers, arginfo_apachehooks__void)
+ PHP_FE(apache_note, arginfo_apachehooks_note)
+ PHP_FE(apache_lookup_uri, arginfo_apachehooks_lookup_uri)
+ PHP_FE(apache_child_terminate, arginfo_apachehooks__void)
+ PHP_FE(apache_setenv, arginfo_apachehooks_setenv)
+ PHP_FE(apache_response_headers, arginfo_apachehooks__void)
+ PHP_FE(apache_get_version, arginfo_apachehooks__void)
+ PHP_FE(apache_get_modules, arginfo_apachehooks__void)
+ PHP_FALIAS(getallheaders, apache_request_headers, arginfo_apachehooks__void)
+ {NULL, NULL, NULL}
+};
+
+/* {{{ php_apache ini entries
+ */
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY("xbithack", "0", PHP_INI_ALL, OnUpdateLong, xbithack, php_apache_info_struct, php_apache_info)
+ STD_PHP_INI_ENTRY("engine", "1", PHP_INI_ALL, OnUpdateLong, engine, php_apache_info_struct, php_apache_info)
+ STD_PHP_INI_ENTRY("last_modified", "0", PHP_INI_ALL, OnUpdateLong, last_modified, php_apache_info_struct, php_apache_info)
+ STD_PHP_INI_ENTRY("child_terminate", "0", PHP_INI_ALL, OnUpdateLong, terminate_child, php_apache_info_struct, php_apache_info)
+PHP_INI_END()
+/* }}} */
+
+static void php_apache_globals_ctor(php_apache_info_struct *apache_globals TSRMLS_DC)
+{
+ apache_globals->in_request = 0;
+}
+
+
+#define APREQ_GET_THIS(ZVAL) if (NULL == (ZVAL = getThis())) { \
+ php_error(E_WARNING, "%s(): underlying ApacheRequest object missing", \
+ get_active_function_name(TSRMLS_C)); \
+ RETURN_FALSE; \
+ }
+#define APREQ_GET_REQUEST(ZVAL, R) APREQ_GET_THIS(ZVAL); \
+ R = get_apache_request(ZVAL TSRMLS_CC)
+
+static void php_apache_request_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ zval *z = (zval *)rsrc->ptr;
+/* fprintf(stderr, "%s() %p\n", __FUNCTION__, z); */
+ zval_ptr_dtor(&z);
+}
+
+static request_rec *get_apache_request(zval *z TSRMLS_DC)
+{
+ request_rec *r;
+ zval **addr;
+
+ if (NULL == z) {
+ php_error(E_WARNING, "get_apache_request() invalid wrapper passed");
+ return NULL;
+ }
+
+ if (Z_TYPE_P(z) != IS_OBJECT) {
+ php_error(E_WARNING, "%s(): wrapper is not an object", get_active_function_name(TSRMLS_C));
+ return NULL;
+ }
+
+ if (zend_hash_index_find(Z_OBJPROP_P(z), 0, (void **)&addr) == FAILURE) {
+ php_error(E_WARNING, "%s(): underlying object missing", get_active_function_name(TSRMLS_C));
+ return NULL;
+ }
+
+ r = (request_rec *)Z_LVAL_PP(addr);
+ if (!r) {
+ php_error(E_WARNING, "%s(): request_rec invalid", get_active_function_name(TSRMLS_C));
+ return NULL;
+ }
+
+ return r;
+}
+
+/* {{{ php_apache_request_new(request_rec *r)
+ * create a new zval-instance for ApacheRequest that wraps request_rec
+ */
+zval *php_apache_request_new(request_rec *r)
+{
+ zval *req;
+ zval *addr;
+
+ TSRMLS_FETCH();
+
+ MAKE_STD_ZVAL(addr);
+ Z_TYPE_P(addr) = IS_LONG;
+ Z_LVAL_P(addr) = (int) r;
+
+ MAKE_STD_ZVAL(req);
+ object_init_ex(req, apacherequest_class_entry);
+ zend_hash_index_update(Z_OBJPROP_P(req), 0, &addr, sizeof(zval *), NULL);
+
+ return req;
+}
+/* }}} */
+
+/* {{{ apache_request_read_string_slot()
+ */
+static void apache_request_read_string_slot(int offset, INTERNAL_FUNCTION_PARAMETERS)
+{
+ zval *id;
+ request_rec *r;
+ char *s;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ s = *(char **)((char*)r + offset);
+
+ if (s) {
+ RETURN_STRING(s, 1);
+ }
+
+ RETURN_EMPTY_STRING();
+}
+/* }}} */
+
+
+/* {{{ apache_request_string_slot()
+ */
+static void apache_request_string_slot(int offset, INTERNAL_FUNCTION_PARAMETERS)
+{
+ zval *id;
+ request_rec *r;
+ char *old_value, *new_value = NULL;
+ int new_value_len;
+ char **target;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &new_value, &new_value_len) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ target = (char **)((char*)r + offset);
+ old_value = *target;
+
+ if (new_value) {
+ *target = ap_pstrdup(r->pool, new_value);
+ }
+
+ if (old_value) {
+ RETURN_STRING(old_value, 1);
+ }
+
+ RETURN_EMPTY_STRING();
+}
+/* }}} */
+
+/* {{{ apache_request_read_int_slot()
+ */
+static void apache_request_read_int_slot(int offset, INTERNAL_FUNCTION_PARAMETERS)
+{
+ zval *id;
+ request_rec *r;
+ long l;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ l = *(long *)((char*)r + offset);
+
+ RETURN_LONG(l);
+}
+/* }}} */
+
+/* {{{ apache_request_int_slot()
+ */
+static void apache_request_int_slot(int offset, INTERNAL_FUNCTION_PARAMETERS)
+{
+ zval *id;
+ request_rec *r;
+ long old_value, new_value;
+ long *target;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &new_value) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ target = (long *)((char*)r + offset);
+ old_value = *target;
+
+ switch (ZEND_NUM_ARGS()) {
+ case 0:
+ break;
+ case 1:
+ *target = new_value;
+ break;
+ default:
+ WRONG_PARAM_COUNT;
+ break;
+ }
+
+ RETURN_LONG(old_value);
+}
+/* }}} */
+
+
+/* {{{ access string slots of request rec
+ */
+
+/* {{{ proto string ApacheRequest::filename([string new_filename])
+ */
+PHP_FUNCTION(apache_request_filename)
+{
+ apache_request_string_slot(offsetof(request_rec, filename), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto string ApacheRequest::uri([string new_uri])
+ */
+PHP_FUNCTION(apache_request_uri)
+{
+ apache_request_string_slot(offsetof(request_rec, uri), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto string ApacheRequest::unparsed_uri([string new_unparsed_uri])
+ */
+PHP_FUNCTION(apache_request_unparsed_uri)
+{
+ apache_request_string_slot(offsetof(request_rec, unparsed_uri), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto string ApacheRequest::path_info([string new_path_info])
+ */
+PHP_FUNCTION(apache_request_path_info)
+{
+ apache_request_string_slot(offsetof(request_rec, path_info), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto string ApacheRequest::args([string new_args])
+ */
+PHP_FUNCTION(apache_request_args)
+{
+ apache_request_string_slot(offsetof(request_rec, args), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto string ApacheRequest::boundary()
+ */
+PHP_FUNCTION(apache_request_boundary)
+{
+ apache_request_read_string_slot(offsetof(request_rec, boundary), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+
+/* {{{ proto string ApacheRequest::content_type([string new_type])
+ */
+PHP_FUNCTION(apache_request_content_type)
+{
+ apache_request_string_slot(offsetof(request_rec, content_type), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto string ApacheRequest::content_encoding([string new_encoding])
+ */
+PHP_FUNCTION(apache_request_content_encoding)
+{
+ apache_request_string_slot(offsetof(request_rec, content_encoding), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto string ApacheRequest::handler([string new_handler])
+ */
+PHP_FUNCTION(apache_request_handler)
+{
+ apache_request_string_slot(offsetof(request_rec, handler), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto string ApacheRequest::the_request()
+ */
+PHP_FUNCTION(apache_request_the_request)
+{
+ apache_request_read_string_slot(offsetof(request_rec, the_request), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto string ApacheRequest::protocol()
+ */
+PHP_FUNCTION(apache_request_protocol)
+{
+ apache_request_read_string_slot(offsetof(request_rec, protocol), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto string ApacheRequest::hostname()
+ */
+PHP_FUNCTION(apache_request_hostname)
+{
+ apache_request_read_string_slot(offsetof(request_rec, hostname), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto string ApacheRequest::status_line([string new_status_line])
+ */
+PHP_FUNCTION(apache_request_status_line)
+{
+ apache_request_string_slot(offsetof(request_rec, status_line), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto string ApacheRequest::method()
+ */
+PHP_FUNCTION(apache_request_method)
+{
+ apache_request_read_string_slot(offsetof(request_rec, method), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* }}} access string slots of request rec */
+
+/* {{{ access int slots of request_rec
+ */
+
+/* {{{ proto int ApacheRequest::proto_num()
+ */
+PHP_FUNCTION(apache_request_proto_num)
+{
+ apache_request_read_int_slot(offsetof(request_rec, proto_num), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::assbackwards()
+ */
+PHP_FUNCTION(apache_request_assbackwards)
+{
+ apache_request_read_int_slot(offsetof(request_rec, assbackwards), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+
+/* {{{ proto int ApacheRequest::proxyreq([int new_proxyreq])
+ */
+PHP_FUNCTION(apache_request_proxyreq)
+{
+ apache_request_int_slot(offsetof(request_rec, proxyreq), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::chunked()
+ */
+PHP_FUNCTION(apache_request_chunked)
+{
+ apache_request_read_int_slot(offsetof(request_rec, chunked), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+
+/* {{{ proto int ApacheRequest::header_only()
+ */
+PHP_FUNCTION(apache_request_header_only)
+{
+ apache_request_read_int_slot(offsetof(request_rec, header_only), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::request_time()
+ */
+PHP_FUNCTION(apache_request_request_time)
+{
+ apache_request_read_int_slot(offsetof(request_rec, request_time), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::status([int new_status])
+ */
+PHP_FUNCTION(apache_request_status)
+{
+ apache_request_int_slot(offsetof(request_rec, status), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::method_number([int method_number])
+ */
+PHP_FUNCTION(apache_request_method_number)
+{
+ apache_request_read_int_slot(offsetof(request_rec, method_number), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::allowed([int allowed])
+ */
+PHP_FUNCTION(apache_request_allowed)
+{
+ apache_request_int_slot(offsetof(request_rec, allowed), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::bytes_sent()
+ */
+PHP_FUNCTION(apache_request_bytes_sent)
+{
+ apache_request_read_int_slot(offsetof(request_rec, bytes_sent), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::mtime()
+ */
+PHP_FUNCTION(apache_request_mtime)
+{
+ apache_request_read_int_slot(offsetof(request_rec, mtime), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::content_length([int new_content_length])
+ */
+PHP_FUNCTION(apache_request_content_length)
+{
+ zval *id;
+ long zlen;
+ request_rec *r;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &zlen) == FAILURE) {
+ return;
+ }
+
+ if (ZEND_NUM_ARGS() == 0) {
+ apache_request_read_int_slot(offsetof(request_rec, clength), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+ } else {
+ APREQ_GET_REQUEST(id, r);
+
+ (void)ap_set_content_length(r, zlen);
+ RETURN_TRUE;
+ }
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::remaining()
+ */
+PHP_FUNCTION(apache_request_remaining)
+{
+ apache_request_read_int_slot(offsetof(request_rec, remaining), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::no_cache()
+ */
+PHP_FUNCTION(apache_request_no_cache)
+{
+ apache_request_int_slot(offsetof(request_rec, no_cache), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::no_local_copy()
+ */
+PHP_FUNCTION(apache_request_no_local_copy)
+{
+ apache_request_int_slot(offsetof(request_rec, no_local_copy), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+/* {{{ proto int ApacheRequest::read_body()
+ */
+PHP_FUNCTION(apache_request_read_body)
+{
+ apache_request_int_slot(offsetof(request_rec, read_body), INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}
+/* }}} */
+
+
+/* }}} access int slots of request_rec */
+
+
+/* {{{ proto array apache_request_headers_in()
+ * fetch all incoming request headers
+ */
+PHP_FUNCTION(apache_request_headers_in)
+{
+ zval *id;
+ request_rec *r;
+
+ APREQ_GET_REQUEST(id, r);
+
+ apache_table_to_zval(r->headers_in, return_value);
+}
+/* }}} */
+
+
+/* {{{ add_header_to_table
+*/
+static void add_header_to_table(table *t, INTERNAL_FUNCTION_PARAMETERS)
+{
+ zval *first = NULL;
+ zval *second = NULL;
+ zval **entry, **value;
+ char *string_key;
+ uint string_key_len;
+ ulong num_key;
+
+ zend_bool replace = 0;
+ HashPosition pos;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|zb", &first, &second, &replace) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ if (Z_TYPE_P(first) == IS_ARRAY) {
+ switch(ZEND_NUM_ARGS()) {
+ case 1:
+ case 3:
+ zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(first), &pos);
+ while (zend_hash_get_current_data_ex(Z_ARRVAL_P(first), (void **)&entry, &pos) == SUCCESS) {
+ switch(zend_hash_get_current_key_ex(Z_ARRVAL_P(first), &string_key, &string_key_len, &num_key, 0, &pos)) {
+ case HASH_KEY_IS_STRING:
+ if (zend_hash_find(Z_ARRVAL_P(first), string_key, string_key_len, (void **)&value) == FAILURE) {
+ zend_hash_move_forward_ex(Z_ARRVAL_P(first), &pos);
+ continue;
+ }
+ if (!value) {
+ zend_hash_move_forward_ex(Z_ARRVAL_P(first), &pos);
+ continue;
+ }
+
+ convert_to_string_ex(value);
+ if (replace) {
+ ap_table_set(t, string_key, Z_STRVAL_PP(value));
+ } else {
+ ap_table_merge(t, string_key, Z_STRVAL_PP(value));
+ }
+ break;
+ case HASH_KEY_IS_LONG:
+ default:
+ php_error(E_WARNING, "%s(): Can only add STRING keys to headers!", get_active_function_name(TSRMLS_C));
+ break;
+ }
+
+ zend_hash_move_forward_ex(Z_ARRVAL_P(first), &pos);
+ }
+ break;
+ default:
+ WRONG_PARAM_COUNT;
+ break;
+ }
+ } else if (Z_TYPE_P(first) == IS_STRING) {
+ switch(ZEND_NUM_ARGS()) {
+ case 2:
+ case 3:
+ convert_to_string_ex(&second);
+ if (replace) {
+ ap_table_set(t, Z_STRVAL_P(first), Z_STRVAL_P(second));
+ } else {
+ ap_table_merge(t, Z_STRVAL_P(first), Z_STRVAL_P(second));
+ }
+ break;
+ default:
+ WRONG_PARAM_COUNT;
+ break;
+ }
+ } else {
+ RETURN_FALSE;
+ }
+}
+
+/* }}} */
+
+
+/* {{{ proto array apache_request_headers_out([{string name|array list} [, string value [, bool replace = false]]])
+ * fetch all outgoing request headers
+ */
+PHP_FUNCTION(apache_request_headers_out)
+{
+ zval *id;
+ request_rec *r;
+
+ APREQ_GET_REQUEST(id, r);
+
+ if (ZEND_NUM_ARGS() > 0) {
+ add_header_to_table(r->headers_out, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+ }
+
+ apache_table_to_zval(r->headers_out, return_value);
+}
+/* }}} */
+
+
+/* {{{ proto array apache_request_err_headers_out([{string name|array list} [, string value [, bool replace = false]]])
+ * fetch all headers that go out in case of an error or a subrequest
+ */
+PHP_FUNCTION(apache_request_err_headers_out)
+{
+ zval *id;
+ request_rec *r;
+
+ APREQ_GET_REQUEST(id, r);
+
+ if (ZEND_NUM_ARGS() > 0) {
+ add_header_to_table(r->err_headers_out, INTERNAL_FUNCTION_PARAM_PASSTHRU);
+ }
+
+ apache_table_to_zval(r->err_headers_out, return_value);
+}
+/* }}} */
+
+
+/* {{{ proxy functions for the ap_* functions family
+ */
+
+/* {{{ proto int apache_request_server_port()
+ */
+PHP_FUNCTION(apache_request_server_port)
+{
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ RETURN_LONG(ap_get_server_port(r));
+}
+/* }}} */
+
+/* {{{ proto int apache_request_remote_host([int type])
+ */
+PHP_FUNCTION(apache_request_remote_host)
+{
+ zval *id;
+ long type = 0;
+ request_rec *r;
+ char *res;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &type) == FAILURE) {
+ return;
+ }
+
+ if (!type) {
+ type = REMOTE_NAME;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ res = (char *)ap_get_remote_host(r->connection, r->per_dir_config, (int)type);
+
+ if (res) {
+ RETURN_STRING(res, 1);
+ }
+
+ RETURN_EMPTY_STRING();
+}
+/* }}} */
+
+/* {{{ proto long apache_request_update_mtime([int dependency_mtime])
+ */
+PHP_FUNCTION(apache_request_update_mtime)
+{
+ zval *id;
+ request_rec *r;
+ long mtime = 0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &mtime) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ RETURN_LONG(ap_update_mtime(r, (int) mtime));
+}
+/* }}} */
+
+
+/* {{{ proto void apache_request_set_etag()
+ */
+PHP_FUNCTION(apache_request_set_etag)
+{
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ ap_set_etag(r);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto void apache_request_set_last_modified()
+ */
+PHP_FUNCTION(apache_request_set_last_modified)
+{
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ ap_set_last_modified(r);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto long apache_request_meets_conditions()
+ */
+PHP_FUNCTION(apache_request_meets_conditions)
+{
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ RETURN_LONG(ap_meets_conditions(r));
+}
+/* }}} */
+
+/* {{{ proto long apache_request_discard_request_body()
+ */
+PHP_FUNCTION(apache_request_discard_request_body)
+{
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ RETURN_LONG(ap_discard_request_body(r));
+}
+/* }}} */
+
+/* {{{ proto long apache_request_satisfies()
+ */
+PHP_FUNCTION(apache_request_satisfies)
+{
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ RETURN_LONG(ap_satisfies(r));
+}
+/* }}} */
+
+
+/* {{{ proto bool apache_request_is_initial_req()
+ */
+PHP_FUNCTION(apache_request_is_initial_req)
+{
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ RETURN_BOOL(ap_is_initial_req(r));
+}
+/* }}} */
+
+/* {{{ proto bool apache_request_some_auth_required()
+ */
+PHP_FUNCTION(apache_request_some_auth_required)
+{
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ RETURN_BOOL(ap_some_auth_required(r));
+}
+/* }}} */
+
+/* {{{ proto string apache_request_auth_type()
+ */
+PHP_FUNCTION(apache_request_auth_type)
+{
+ zval *id;
+ request_rec *r;
+ char *t;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ t = (char *)ap_auth_type(r);
+ if (!t) {
+ RETURN_NULL();
+ }
+
+ RETURN_STRING(t, 1);
+}
+/* }}} */
+
+/* {{{ proto string apache_request_auth_name()
+ */
+PHP_FUNCTION(apache_request_auth_name)
+{
+ zval *id;
+ request_rec *r;
+ char *t;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ t = (char *)ap_auth_name(r);
+ if (!t) {
+ RETURN_NULL();
+ }
+
+ RETURN_STRING(t, 1);
+}
+/* }}} */
+
+/* {{{ proto apache_request_basic_auth_pw()
+ */
+PHP_FUNCTION(apache_request_basic_auth_pw)
+{
+ zval *id, *zpw;
+ request_rec *r;
+ const char *pw;
+ long status;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zpw) == FAILURE) {
+ return;
+ }
+
+ if (!PZVAL_IS_REF(zpw)) {
+ zend_error(E_WARNING, "Parameter wasn't passed by reference");
+ RETURN_NULL();
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ pw = NULL;
+ status = ap_get_basic_auth_pw(r, &pw);
+ if (status == OK && pw) {
+ ZVAL_STRING(zpw, (char *)pw, 1);
+ } else {
+ ZVAL_NULL(zpw);
+ }
+ RETURN_LONG(status);
+}
+/* }}} */
+
+
+/* http_protocol.h */
+
+PHP_FUNCTION(apache_request_send_http_header)
+{
+ zval *id;
+ request_rec *r;
+ char *type = NULL;
+ int typelen;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &type, &typelen) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+ if(type) {
+ r->content_type = pstrdup(r->pool, type);
+ }
+ ap_send_http_header(r);
+ SG(headers_sent) = 1;
+ AP(headers_sent) = 1;
+ RETURN_TRUE;
+}
+
+PHP_FUNCTION(apache_request_basic_http_header)
+{
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ ap_basic_http_header((request_rec *)SG(server_context));
+ SG(headers_sent) = 1;
+ AP(headers_sent) = 1;
+ RETURN_TRUE;
+}
+
+PHP_FUNCTION(apache_request_send_http_trace)
+{
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ ap_send_http_trace((request_rec *)SG(server_context));
+ SG(headers_sent) = 1;
+ AP(headers_sent) = 1;
+ RETURN_TRUE;
+}
+
+PHP_FUNCTION(apache_request_send_http_options)
+{
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ ap_send_http_options((request_rec *)SG(server_context));
+ SG(headers_sent) = 1;
+ AP(headers_sent) = 1;
+ RETURN_TRUE;
+}
+
+PHP_FUNCTION(apache_request_send_error_response)
+{
+ zval *id;
+ request_rec *r;
+ long rec = 0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &rec) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+ ap_send_error_response(r, (int) rec);
+ RETURN_TRUE;
+}
+
+PHP_FUNCTION(apache_request_set_content_length)
+{
+ long length;
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &length) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ ap_set_content_length(r, length);
+ RETURN_TRUE;
+}
+
+PHP_FUNCTION(apache_request_set_keepalive)
+{
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+ ap_set_keepalive(r);
+ RETURN_TRUE;
+}
+
+/* This stuff should use streams or however this is implemented now
+
+PHP_FUNCTION(apache_request_send_fd)
+{
+}
+
+PHP_FUNCTION(apache_request_send_fd_length)
+{
+}
+*/
+
+/* These are for overriding default output behaviour */
+PHP_FUNCTION(apache_request_rputs)
+{
+ char *buffer;
+ int buffer_len;
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buffer, &buffer_len) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+ ap_rwrite(buffer, buffer_len, (request_rec*)SG(server_context));
+}
+
+/* This stuff would be useful for custom POST handlers,
+ which should be supported. Probably by not using
+ sapi_activate at all inside a phpResponseHandler
+ and instead using a builtin composed of the below
+ calls as a apache_read_request_body() and allow
+ people to custom craft their own.
+
+PHP_FUNCTION(apache_request_setup_client_block)
+{
+}
+
+PHP_FUNCTION(apache_request_should_client_block)
+{
+}
+
+PHP_FUNCTION(apache_request_get_client_block)
+{
+}
+
+PHP_FUNCTION(apache_request_discard_request_body)
+{
+}
+*/
+
+/* http_log.h */
+
+/* {{{ proto boolean apache_request_log_error(string message, [long facility])
+ */
+PHP_FUNCTION(apache_request_log_error)
+{
+ zval *id;
+ char *z_errstr;
+ int z_errstr_len;
+ long facility = APLOG_ERR;
+ request_rec *r;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &z_errstr, &z_errstr_len, &facility) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+ ap_log_error(APLOG_MARK, (int) facility, r->server, "%s", z_errstr);
+ RETURN_TRUE;
+}
+/* }}} */
+/* http_main.h */
+
+/* {{{ proto object apache_request_sub_req_lookup_uri(string uri)
+ Returns sub-request for the specified uri. You would
+ need to run it yourself with run()
+*/
+PHP_FUNCTION(apache_request_sub_req_lookup_uri)
+{
+ zval *id;
+ char *file;
+ int file_len;
+ request_rec *r, *sub_r;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &file_len) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+ sub_r = ap_sub_req_lookup_uri(file, r);
+
+ if (!sub_r) {
+ RETURN_FALSE;
+ }
+ return_value = php_apache_request_new(sub_r);
+}
+/* }}} */
+
+/* {{{ proto object apache_request_sub_req_lookup_file(string file)
+ Returns sub-request for the specified file. You would
+ need to run it yourself with run().
+*/
+PHP_FUNCTION(apache_request_sub_req_lookup_file)
+{
+ zval *id;
+ char *file;
+ int file_len;
+ request_rec *r, *sub_r;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &file, &file_len) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ sub_r = ap_sub_req_lookup_file(file, r);
+
+ if (!sub_r) {
+ RETURN_FALSE;
+ }
+ return_value = php_apache_request_new(sub_r);
+}
+/* }}} */
+
+/* {{{ proto object apache_request_sub_req_method_uri(string method, string uri)
+ Returns sub-request for the specified file. You would
+ need to run it yourself with run().
+*/
+PHP_FUNCTION(apache_request_sub_req_method_uri)
+{
+ zval *id;
+ char *file, *method;
+ int file_len, method_len;
+ request_rec *r, *sub_r;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &method, &method_len, &file, &file_len) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ sub_r = ap_sub_req_method_uri(method, file, r);
+
+ if (!sub_r) {
+ RETURN_FALSE;
+ }
+ return_value = php_apache_request_new(sub_r);
+}
+/* }}} */
+
+/* {{{ proto long apache_request_run()
+ This is a wrapper for ap_sub_run_req and ap_destory_sub_req. It takes
+ sub_request, runs it, destroys it, and returns it's status.
+*/
+PHP_FUNCTION(apache_request_run)
+{
+ zval *id;
+ request_rec *r;
+ int status;
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+ if (!r || ap_is_initial_req(r)) {
+ RETURN_FALSE;
+ }
+ status = ap_run_sub_req(r);
+ ap_destroy_sub_req(r);
+ RETURN_LONG(status);
+}
+/* }}} */
+
+PHP_FUNCTION(apache_request_internal_redirect)
+{
+ zval *id;
+ char *new_uri;
+ int new_uri_len;
+ request_rec *r;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &new_uri, &new_uri_len) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ ap_internal_redirect(new_uri, r);
+}
+
+PHP_FUNCTION(apache_request_send_header_field)
+{
+ char *fieldname, *fieldval;
+ int fieldname_len, fieldval_len;
+ zval *id;
+ request_rec *r;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &fieldname, &fieldname_len, &fieldval, &fieldval_len) == FAILURE) {
+ return;
+ }
+
+ APREQ_GET_REQUEST(id, r);
+
+ ap_send_header_field(r, fieldname, fieldval);
+ SG(headers_sent) = 1;
+ AP(headers_sent) = 1;
+}
+
+
+
+/* }}} */
+
+/* {{{ php_apache_request_class_functions
+ */
+const static zend_function_entry php_apache_request_class_functions[] = {
+ /* string slots */
+ PHP_FALIAS(args, apache_request_args, NULL)
+ PHP_FALIAS(boundary, apache_request_boundary, NULL)
+ PHP_FALIAS(content_encoding, apache_request_content_encoding, NULL)
+ PHP_FALIAS(content_type, apache_request_content_type, NULL)
+ PHP_FALIAS(filename, apache_request_filename, NULL)
+ PHP_FALIAS(handler, apache_request_handler, NULL)
+ PHP_FALIAS(hostname, apache_request_hostname, NULL)
+ PHP_FALIAS(method, apache_request_method, NULL)
+ PHP_FALIAS(path_info, apache_request_path_info, NULL)
+ PHP_FALIAS(protocol, apache_request_protocol, NULL)
+ PHP_FALIAS(status_line, apache_request_status_line, NULL)
+ PHP_FALIAS(the_request, apache_request_the_request, NULL)
+ PHP_FALIAS(unparsed_uri, apache_request_unparsed_uri, NULL)
+ PHP_FALIAS(uri, apache_request_uri, NULL)
+
+ /* int slots */
+ PHP_FALIAS(allowed, apache_request_allowed, NULL)
+ PHP_FALIAS(bytes_sent, apache_request_bytes_sent, NULL)
+ PHP_FALIAS(chunked, apache_request_chunked, NULL)
+ PHP_FALIAS(content_length, apache_request_content_length, NULL)
+ PHP_FALIAS(header_only, apache_request_header_only, NULL)
+ PHP_FALIAS(method_number, apache_request_method_number, NULL)
+ PHP_FALIAS(mtime, apache_request_mtime, NULL)
+ PHP_FALIAS(no_cache, apache_request_no_cache, NULL)
+ PHP_FALIAS(no_local_copy, apache_request_no_local_copy, NULL)
+ PHP_FALIAS(proto_num, apache_request_proto_num, NULL)
+ PHP_FALIAS(proxyreq, apache_request_proxyreq, NULL)
+ PHP_FALIAS(read_body, apache_request_read_body, NULL)
+ PHP_FALIAS(remaining, apache_request_remaining, NULL)
+ PHP_FALIAS(request_time, apache_request_request_time, NULL)
+ PHP_FALIAS(status, apache_request_status, NULL)
+
+ /* tables & arrays */
+ PHP_FALIAS(headers_in, apache_request_headers_in, NULL)
+ PHP_FALIAS(headers_out, apache_request_headers_out, NULL)
+ PHP_FALIAS(err_headers_out, apache_request_err_headers_out, NULL)
+
+
+ /* proxy functions for the ap_* functions family */
+#undef auth_name
+#undef auth_type
+#undef discard_request_body
+#undef is_initial_req
+#undef meets_conditions
+#undef satisfies
+#undef set_etag
+#undef set_last_modified
+#undef some_auth_required
+#undef update_mtime
+#undef send_http_header
+#undef send_header_field
+#undef basic_http_header
+#undef send_http_trace
+#undef send_http_options
+#undef send_error_response
+#undef set_content_length
+#undef set_keepalive
+#undef rputs
+#undef log_error
+#undef lookup_uri
+#undef lookup_file
+#undef method_uri
+#undef run
+#undef internal_redirect
+ PHP_FALIAS(auth_name, apache_request_auth_name, NULL)
+ PHP_FALIAS(auth_type, apache_request_auth_type, NULL)
+ PHP_FALIAS(basic_auth_pw, apache_request_basic_auth_pw, NULL)
+ PHP_FALIAS(discard_request_body, apache_request_discard_request_body, NULL)
+ PHP_FALIAS(is_initial_req, apache_request_is_initial_req, NULL)
+ PHP_FALIAS(meets_conditions, apache_request_meets_conditions, NULL)
+ PHP_FALIAS(remote_host, apache_request_remote_host, NULL)
+ PHP_FALIAS(satisfies, apache_request_satisfies, NULL)
+ PHP_FALIAS(server_port, apache_request_server_port, NULL)
+ PHP_FALIAS(set_etag, apache_request_set_etag, NULL)
+ PHP_FALIAS(set_last_modified, apache_request_set_last_modified, NULL)
+ PHP_FALIAS(some_auth_required, apache_request_some_auth_required, NULL)
+ PHP_FALIAS(update_mtime, apache_request_update_mtime, NULL)
+ PHP_FALIAS(send_http_header, apache_request_send_http_header, NULL)
+ PHP_FALIAS(basic_http_header, apache_request_basic_http_header, NULL)
+ PHP_FALIAS(send_header_field, apache_request_send_header_field, NULL)
+ PHP_FALIAS(send_http_trace, apache_request_send_http_trace, NULL)
+ PHP_FALIAS(send_http_options, apache_request_send_http_trace, NULL)
+ PHP_FALIAS(send_error_response, apache_request_send_error_response, NULL)
+ PHP_FALIAS(set_content_length, apache_request_set_content_length, NULL)
+ PHP_FALIAS(set_keepalive, apache_request_set_keepalive, NULL)
+ PHP_FALIAS(rputs, apache_request_rputs, NULL)
+ PHP_FALIAS(log_error, apache_request_log_error, NULL)
+ PHP_FALIAS(lookup_uri, apache_request_sub_req_lookup_uri, NULL)
+ PHP_FALIAS(lookup_file, apache_request_sub_req_lookup_file, NULL)
+ PHP_FALIAS(method_uri, apache_request_sub_req_method_uri, NULL)
+ PHP_FALIAS(run, apache_request_run, NULL)
+ PHP_FALIAS(internal_redirect, apache_request_internal_redirect, NULL)
+ PHP_FE_END
+};
+/* }}} */
+
+
+static PHP_MINIT_FUNCTION(apache)
+{
+ zend_class_entry ce;
+
+#ifdef ZTS
+ ts_allocate_id(&php_apache_info_id, sizeof(php_apache_info_struct), (ts_allocate_ctor) php_apache_globals_ctor, NULL);
+#else
+ php_apache_globals_ctor(&php_apache_info TSRMLS_CC);
+#endif
+ REGISTER_INI_ENTRIES();
+
+
+ le_apachereq = zend_register_list_destructors_ex(php_apache_request_free, NULL, "ApacheRequest", module_number);
+ INIT_OVERLOADED_CLASS_ENTRY(ce, "ApacheRequest", php_apache_request_class_functions, NULL, NULL, NULL);
+ apacherequest_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
+
+ REGISTER_LONG_CONSTANT("OK", OK, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DECLINED", DECLINED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("FORBIDDEN", FORBIDDEN, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("AUTH_REQUIRED", AUTH_REQUIRED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("DONE", DONE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("SERVER_ERROR", SERVER_ERROR, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("REDIRECT", REDIRECT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("BAD_REQUEST", BAD_REQUEST, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("NOT_FOUND", NOT_FOUND, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_CONTINUE", HTTP_CONTINUE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_SWITCHING_PROTOCOLS", HTTP_SWITCHING_PROTOCOLS, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_PROCESSING", HTTP_PROCESSING, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_OK", HTTP_OK, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_CREATED", HTTP_CREATED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_ACCEPTED", HTTP_ACCEPTED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_NON_AUTHORITATIVE", HTTP_NON_AUTHORITATIVE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_NO_CONTENT", HTTP_NO_CONTENT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_RESET_CONTENT", HTTP_RESET_CONTENT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_PARTIAL_CONTENT", HTTP_PARTIAL_CONTENT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_MULTI_STATUS", HTTP_MULTI_STATUS, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_MULTIPLE_CHOICES", HTTP_MULTIPLE_CHOICES, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_MOVED_PERMANENTLY", HTTP_MOVED_PERMANENTLY, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_MOVED_TEMPORARILY", HTTP_MOVED_TEMPORARILY, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_SEE_OTHER", HTTP_SEE_OTHER, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_NOT_MODIFIED", HTTP_NOT_MODIFIED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_USE_PROXY", HTTP_USE_PROXY, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_TEMPORARY_REDIRECT", HTTP_TEMPORARY_REDIRECT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_BAD_REQUEST", HTTP_BAD_REQUEST, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_UNAUTHORIZED", HTTP_UNAUTHORIZED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_PAYMENT_REQUIRED", HTTP_PAYMENT_REQUIRED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_FORBIDDEN", HTTP_FORBIDDEN, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_NOT_FOUND", HTTP_NOT_FOUND, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_METHOD_NOT_ALLOWED", HTTP_METHOD_NOT_ALLOWED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_NOT_ACCEPTABLE", HTTP_NOT_ACCEPTABLE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_PROXY_AUTHENTICATION_REQUIRED", HTTP_PROXY_AUTHENTICATION_REQUIRED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_REQUEST_TIME_OUT", HTTP_REQUEST_TIME_OUT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_CONFLICT", HTTP_CONFLICT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_GONE", HTTP_GONE, CONST_CS | CONST_PERSISTENT);REGISTER_LONG_CONSTANT("HTTP_LENGTH_REQUIRED", HTTP_LENGTH_REQUIRED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_PRECONDITION_FAILED", HTTP_PRECONDITION_FAILED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_REQUEST_ENTITY_TOO_LARGE", HTTP_REQUEST_ENTITY_TOO_LARGE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_REQUEST_URI_TOO_LARGE", HTTP_REQUEST_URI_TOO_LARGE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_UNSUPPORTED_MEDIA_TYPE", HTTP_UNSUPPORTED_MEDIA_TYPE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_RANGE_NOT_SATISFIABLE", HTTP_RANGE_NOT_SATISFIABLE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_EXPECTATION_FAILED", HTTP_EXPECTATION_FAILED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_UNPROCESSABLE_ENTITY", HTTP_UNPROCESSABLE_ENTITY, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_LOCKED", HTTP_LOCKED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_FAILED_DEPENDENCY", HTTP_FAILED_DEPENDENCY, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_INTERNAL_SERVER_ERROR", HTTP_INTERNAL_SERVER_ERROR, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_NOT_IMPLEMENTED", HTTP_NOT_IMPLEMENTED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_BAD_GATEWAY", HTTP_BAD_GATEWAY, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_SERVICE_UNAVAILABLE", HTTP_SERVICE_UNAVAILABLE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_GATEWAY_TIME_OUT", HTTP_GATEWAY_TIME_OUT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_VERSION_NOT_SUPPORTED", HTTP_VERSION_NOT_SUPPORTED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_VARIANT_ALSO_VARIES", HTTP_VARIANT_ALSO_VARIES, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_INSUFFICIENT_STORAGE", HTTP_INSUFFICIENT_STORAGE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("HTTP_NOT_EXTENDED", HTTP_NOT_EXTENDED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("APLOG_EMERG", APLOG_EMERG, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("APLOG_ALERT", APLOG_ALERT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("APLOG_CRIT", APLOG_CRIT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("APLOG_ERR", APLOG_ERR, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("APLOG_WARNING", APLOG_WARNING, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("APLOG_NOTICE", APLOG_NOTICE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("APLOG_INFO", APLOG_INFO, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("APLOG_DEBUG", APLOG_DEBUG, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_GET", M_GET, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_PUT", M_PUT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_POST", M_POST, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_DELETE", M_DELETE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_CONNECT", M_CONNECT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_OPTIONS", M_OPTIONS, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_TRACE", M_TRACE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_PATCH", M_PATCH, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_PROPFIND", M_PROPFIND, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_PROPPATCH", M_PROPPATCH, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_MKCOL", M_MKCOL, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_COPY", M_COPY, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_MOVE", M_MOVE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_LOCK", M_LOCK, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_UNLOCK", M_UNLOCK, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("M_INVALID", M_INVALID, CONST_CS | CONST_PERSISTENT);
+
+ /* Possible values for request_rec.read_body (set by handling module):
+ * REQUEST_NO_BODY Send 413 error if message has any body
+ * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length
+ * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me.
+ * REQUEST_CHUNKED_PASS Pass the chunks to me without removal.
+ */
+ REGISTER_LONG_CONSTANT("REQUEST_NO_BODY", REQUEST_NO_BODY, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("REQUEST_CHUNKED_ERROR", REQUEST_CHUNKED_ERROR, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("REQUEST_CHUNKED_DECHUNK", REQUEST_CHUNKED_DECHUNK, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("REQUEST_CHUNKED_PASS", REQUEST_CHUNKED_PASS, CONST_CS | CONST_PERSISTENT);
+
+ /* resolve types for remote_host() */
+ REGISTER_LONG_CONSTANT("REMOTE_HOST", REMOTE_HOST, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("REMOTE_NAME", REMOTE_NAME, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("REMOTE_NOLOOKUP", REMOTE_NOLOOKUP, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("REMOTE_DOUBLE_REV", REMOTE_DOUBLE_REV, CONST_CS | CONST_PERSISTENT);
+
+ return SUCCESS;
+}
+
+
+static PHP_MSHUTDOWN_FUNCTION(apache)
+{
+ UNREGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+
+zend_module_entry apache_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "apache",
+ apache_functions,
+ PHP_MINIT(apache),
+ PHP_MSHUTDOWN(apache),
+ NULL,
+ NULL,
+ PHP_MINFO(apache),
+ NO_VERSION_YET,
+ STANDARD_MODULE_PROPERTIES
+};
+
+/* {{{ proto bool apache_child_terminate(void)
+ Terminate apache process after this request */
+PHP_FUNCTION(apache_child_terminate)
+{
+#ifndef MULTITHREAD
+ if (AP(terminate_child)) {
+ ap_child_terminate( ((request_rec *)SG(server_context)) );
+ RETURN_TRUE;
+ } else { /* tell them to get lost! */
+ php_error(E_WARNING, "apache.child_terminate is disabled");
+ RETURN_FALSE;
+ }
+#else
+ php_error(E_WARNING, "apache_child_terminate() is not supported in this build");
+ RETURN_FALSE;
+#endif
+}
+/* }}} */
+
+/* {{{ proto string apache_note(string note_name [, string note_value])
+ Get and set Apache request notes */
+PHP_FUNCTION(apache_note)
+{
+ char *arg_name, *arg_val = NULL;
+ int arg_name_len, arg_val_len;
+ char *note_val;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &arg_name, &arg_name_len, &arg_val, &arg_val_len) == FAILURE) {
+ return;
+ }
+
+ note_val = (char *) table_get(((request_rec *)SG(server_context))->notes, arg_name);
+
+ if (arg_val) {
+ table_set(((request_rec *)SG(server_context))->notes, arg_name, arg_val);
+ }
+
+ if (!note_val) {
+ RETURN_FALSE;
+ }
+
+ RETURN_STRING(note_val, 1);
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(apache)
+{
+ module *modp = NULL;
+ char output_buf[128];
+#if !defined(WIN32) && !defined(WINNT)
+ char name[64];
+ char modulenames[1024];
+ char *p;
+#endif
+ server_rec *serv;
+ extern char server_root[MAX_STRING_LEN];
+ extern uid_t user_id;
+ extern char *user_name;
+ extern gid_t group_id;
+ extern int max_requests_per_child;
+
+ serv = ((request_rec *) SG(server_context))->server;
+
+
+ php_info_print_table_start();
+
+#ifdef PHP_WIN32
+ php_info_print_table_row(1, "Apache for Windows 95/NT");
+ php_info_print_table_end();
+ php_info_print_table_start();
+#elif defined(NETWARE)
+ php_info_print_table_row(1, "Apache for NetWare");
+ php_info_print_table_end();
+ php_info_print_table_start();
+#else
+ php_info_print_table_row(2, "APACHE_INCLUDE", PHP_APACHE_INCLUDE);
+ php_info_print_table_row(2, "APACHE_TARGET", PHP_APACHE_TARGET);
+#endif
+
+ php_info_print_table_row(2, "Apache Version", SERVER_VERSION);
+
+#ifdef APACHE_RELEASE
+ snprintf(output_buf, sizeof(output_buf), "%d", APACHE_RELEASE);
+ php_info_print_table_row(2, "Apache Release", output_buf);
+#endif
+ snprintf(output_buf, sizeof(output_buf), "%d", MODULE_MAGIC_NUMBER);
+ php_info_print_table_row(2, "Apache API Version", output_buf);
+ snprintf(output_buf, sizeof(output_buf), "%s:%u", serv->server_hostname, serv->port);
+ php_info_print_table_row(2, "Hostname:Port", output_buf);
+#if !defined(WIN32) && !defined(WINNT)
+ snprintf(output_buf, sizeof(output_buf), "%s(%d)/%d", user_name, (int)user_id, (int)group_id);
+ php_info_print_table_row(2, "User/Group", output_buf);
+ snprintf(output_buf, sizeof(output_buf), "Per Child: %d - Keep Alive: %s - Max Per Connection: %d", max_requests_per_child, serv->keep_alive ? "on":"off", serv->keep_alive_max);
+ php_info_print_table_row(2, "Max Requests", output_buf);
+#endif
+ snprintf(output_buf, sizeof(output_buf), "Connection: %d - Keep-Alive: %d", serv->timeout, serv->keep_alive_timeout);
+ php_info_print_table_row(2, "Timeouts", output_buf);
+#if !defined(WIN32) && !defined(WINNT)
+/*
+ This block seems to be working on NetWare; But it seems to be showing
+ all modules instead of just the loaded ones
+*/
+ php_info_print_table_row(2, "Server Root", server_root);
+
+ strcpy(modulenames, "");
+ for(modp = top_module; modp; modp = modp->next) {
+ strlcpy(name, modp->name, sizeof(name));
+ if ((p = strrchr(name, '.'))) {
+ *p='\0'; /* Cut off ugly .c extensions on module names */
+ }
+ strlcat(modulenames, name, sizeof(modulenames));
+ if (modp->next) {
+ strlcat(modulenames, ", ", sizeof(modulenames));
+ }
+ }
+ php_info_print_table_row(2, "Loaded Modules", modulenames);
+#endif
+
+ php_info_print_table_end();
+
+ DISPLAY_INI_ENTRIES();
+
+ {
+ register int i;
+ array_header *arr;
+ table_entry *elts;
+ request_rec *r;
+
+ r = ((request_rec *) SG(server_context));
+ arr = table_elts(r->subprocess_env);
+ elts = (table_entry *)arr->elts;
+
+ SECTION("Apache Environment");
+ php_info_print_table_start();
+ php_info_print_table_header(2, "Variable", "Value");
+ for (i=0; i < arr->nelts; i++) {
+ php_info_print_table_row(2, elts[i].key, elts[i].val);
+ }
+ php_info_print_table_end();
+ }
+
+ {
+ array_header *env_arr;
+ table_entry *env;
+ int i;
+ request_rec *r;
+
+ r = ((request_rec *) SG(server_context));
+ SECTION("HTTP Headers Information");
+ php_info_print_table_start();
+ php_info_print_table_colspan_header(2, "HTTP Request Headers");
+ php_info_print_table_row(2, "HTTP Request", r->the_request);
+ env_arr = table_elts(r->headers_in);
+ env = (table_entry *)env_arr->elts;
+ for (i = 0; i < env_arr->nelts; ++i) {
+ if (env[i].key) {
+ php_info_print_table_row(2, env[i].key, env[i].val);
+ }
+ }
+ php_info_print_table_colspan_header(2, "HTTP Response Headers");
+ env_arr = table_elts(r->headers_out);
+ env = (table_entry *)env_arr->elts;
+ for(i = 0; i < env_arr->nelts; ++i) {
+ if (env[i].key) {
+ php_info_print_table_row(2, env[i].key, env[i].val);
+ }
+ }
+ php_info_print_table_end();
+ }
+}
+/* }}} */
+
+/* {{{ proto bool virtual(string filename)
+ Perform an Apache sub-request */
+/* This function is equivalent to <!--#include virtual...-->
+ * in mod_include. It does an Apache sub-request. It is useful
+ * for including CGI scripts or .shtml files, or anything else
+ * that you'd parse through Apache (for .phtml files, you'd probably
+ * want to use <?Include>. This only works when PHP is compiled
+ * as an Apache module, since it uses the Apache API for doing
+ * sub requests.
+ */
+PHP_FUNCTION(virtual)
+{
+ char *filename;
+ int filename_len;
+ request_rec *rr = NULL;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
+ return;
+ }
+
+ if (!(rr = sub_req_lookup_uri (filename, ((request_rec *) SG(server_context))))) {
+ php_error(E_WARNING, "Unable to include '%s' - URI lookup failed", filename);
+ if (rr)
+ destroy_sub_req (rr);
+ RETURN_FALSE;
+ }
+
+ if (rr->status != 200) {
+ php_error(E_WARNING, "Unable to include '%s' - error finding URI", filename);
+ if (rr)
+ destroy_sub_req (rr);
+ RETURN_FALSE;
+ }
+
+ php_output_end_all(TSRMLS_C);
+ php_header(TSRMLS_C);
+
+ if (run_sub_req(rr)) {
+ php_error(E_WARNING, "Unable to include '%s' - request execution failed", filename);
+ if (rr)
+ destroy_sub_req (rr);
+ RETURN_FALSE;
+ }
+
+ if (rr)
+ destroy_sub_req (rr);
+ RETURN_TRUE;
+}
+/* }}} */
+
+
+/* {{{ apache_table_to_zval(table *, zval *return_value)
+ Fetch all HTTP request headers */
+static void apache_table_to_zval(table *t, zval *return_value)
+{
+ array_header *env_arr;
+ table_entry *tenv;
+ int i;
+
+ array_init(return_value);
+ env_arr = table_elts(t);
+ tenv = (table_entry *)env_arr->elts;
+ for (i = 0; i < env_arr->nelts; ++i) {
+ if (!tenv[i].key) {
+ continue;
+ }
+ if (add_assoc_string(return_value, tenv[i].key, (tenv[i].val==NULL) ? "" : tenv[i].val, 1)==FAILURE) {
+ RETURN_FALSE;
+ }
+ }
+
+}
+/* }}} */
+
+
+/* {{{ proto array getallheaders(void)
+*/
+/* Alias for apache_request_headers() */
+/* }}} */
+
+/* {{{ proto array apache_request_headers(void)
+ Fetch all HTTP request headers */
+PHP_FUNCTION(apache_request_headers)
+{
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ apache_table_to_zval(((request_rec *)SG(server_context))->headers_in, return_value);
+}
+/* }}} */
+
+/* {{{ proto array apache_response_headers(void)
+ Fetch all HTTP response headers */
+PHP_FUNCTION(apache_response_headers)
+{
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ apache_table_to_zval(((request_rec *) SG(server_context))->headers_out, return_value);
+}
+/* }}} */
+
+/* {{{ proto bool apache_setenv(string variable, string value [, bool walk_to_top])
+ Set an Apache subprocess_env variable */
+PHP_FUNCTION(apache_setenv)
+{
+ int var_len, val_len;
+ zend_bool top=0;
+ char *var = NULL, *val = NULL;
+ request_rec *r = (request_rec *) SG(server_context);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &var, &var_len, &val, &val_len, &top) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ while(top) {
+ if (r->prev) {
+ r = r->prev;
+ }
+ else break;
+ }
+
+ ap_table_setn(r->subprocess_env, ap_pstrndup(r->pool, var, var_len), ap_pstrndup(r->pool, val, val_len));
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto object apache_lookup_uri(string URI)
+ Perform a partial request of the given URI to obtain information about it */
+PHP_FUNCTION(apache_lookup_uri)
+{
+ char *filename;
+ int filename_len;
+ request_rec *rr=NULL;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
+ return;
+ }
+
+ if(!(rr = sub_req_lookup_uri(filename, ((request_rec *) SG(server_context))))) {
+ php_error(E_WARNING, "URI lookup failed", filename);
+ RETURN_FALSE;
+ }
+
+ object_init(return_value);
+ add_property_long(return_value,"status", rr->status);
+
+ if (rr->the_request) {
+ add_property_string(return_value,"the_request", rr->the_request, 1);
+ }
+ if (rr->status_line) {
+ add_property_string(return_value,"status_line", (char *)rr->status_line, 1);
+ }
+ if (rr->method) {
+ add_property_string(return_value,"method", (char *)rr->method, 1);
+ }
+ if (rr->content_type) {
+ add_property_string(return_value,"content_type", (char *)rr->content_type, 1);
+ }
+ if (rr->handler) {
+ add_property_string(return_value,"handler", (char *)rr->handler, 1);
+ }
+ if (rr->uri) {
+ add_property_string(return_value,"uri", rr->uri, 1);
+ }
+ if (rr->filename) {
+ add_property_string(return_value,"filename", rr->filename, 1);
+ }
+ if (rr->path_info) {
+ add_property_string(return_value,"path_info", rr->path_info, 1);
+ }
+ if (rr->args) {
+ add_property_string(return_value,"args", rr->args, 1);
+ }
+ if (rr->boundary) {
+ add_property_string(return_value,"boundary", rr->boundary, 1);
+ }
+ add_property_long(return_value,"no_cache", rr->no_cache);
+ add_property_long(return_value,"no_local_copy", rr->no_local_copy);
+ add_property_long(return_value,"allowed", rr->allowed);
+ add_property_long(return_value,"sent_bodyct", rr->sent_bodyct);
+ add_property_long(return_value,"bytes_sent", rr->bytes_sent);
+ add_property_long(return_value,"byterange", rr->byterange);
+ add_property_long(return_value,"clength", rr->clength);
+
+#if MODULE_MAGIC_NUMBER >= 19980324
+ if (rr->unparsed_uri) {
+ add_property_string(return_value,"unparsed_uri", rr->unparsed_uri, 1);
+ }
+ if(rr->mtime) {
+ add_property_long(return_value,"mtime", rr->mtime);
+ }
+#endif
+ if(rr->request_time) {
+ add_property_long(return_value,"request_time", rr->request_time);
+ }
+
+ destroy_sub_req(rr);
+}
+/* }}} */
+
+
+#if 0
+/*
+This function is most likely a bad idea. Just playing with it for now.
+*/
+
+PHP_FUNCTION(apache_exec_uri)
+{
+ zval **filename;
+ request_rec *rr=NULL;
+
+ if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &filename) == FAILURE) {
+ WRONG_PARAM_COUNT;
+ }
+ convert_to_string_ex(filename);
+
+ if(!(rr = ap_sub_req_lookup_uri((*filename)->value.str.val, ((request_rec *) SG(server_context))))) {
+ php_error(E_WARNING, "URI lookup failed", (*filename)->value.str.val);
+ RETURN_FALSE;
+ }
+ RETVAL_LONG(ap_run_sub_req(rr));
+ ap_destroy_sub_req(rr);
+}
+#endif
+
+/* {{{ proto string apache_get_version(void)
+ Fetch Apache version */
+PHP_FUNCTION(apache_get_version)
+{
+ char *apv = (char *) ap_get_server_version();
+
+ if (apv && *apv) {
+ RETURN_STRING(apv, 1);
+ } else {
+ RETURN_FALSE;
+ }
+}
+/* }}} */
+
+/* {{{ proto array apache_get_modules(void)
+ Get a list of loaded Apache modules */
+PHP_FUNCTION(apache_get_modules)
+{
+ int n;
+ char *p;
+
+ array_init(return_value);
+
+ for (n = 0; ap_loaded_modules[n]; ++n) {
+ char *s = (char *) ap_loaded_modules[n]->name;
+ if ((p = strchr(s, '.'))) {
+ add_next_index_stringl(return_value, s, (p - s), 1);
+ } else {
+ add_next_index_string(return_value, s, 1);
+ }
+ }
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/apache_hooks/php_apache_http.h b/sapi/apache_hooks/php_apache_http.h
new file mode 100644
index 0000000..23cf7fe
--- /dev/null
+++ b/sapi/apache_hooks/php_apache_http.h
@@ -0,0 +1,44 @@
+#define NO_REGEX_EXTRA_H
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <stddef.h>
+#endif
+
+#ifdef NETWARE
+#include <netinet/in.h>
+#endif
+
+#include "zend.h"
+#include "zend_stack.h"
+#include "ext/ereg/php_regex.h"
+
+#include "httpd.h"
+#include "http_config.h"
+
+#if MODULE_MAGIC_NUMBER > 19980712
+# include "ap_compat.h"
+#else
+# if MODULE_MAGIC_NUMBER > 19980324
+# include "compat.h"
+# endif
+#endif
+
+#include "http_core.h"
+#include "http_main.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "http_log.h"
+#include "util_script.h"
+
+#include "php_variables.h"
+#include "php_main.h"
+#include "php_ini.h"
+#include "ext/standard/php_standard.h"
+
+#include "mod_php5.h"
+
+
+zval *php_apache_request_new(request_rec *r);
+
+int apache_php_module_hook(request_rec *r, php_handler *handler, zval **ret TSRMLS_DC);
diff --git a/sapi/apache_hooks/sapi_apache.c b/sapi/apache_hooks/sapi_apache.c
new file mode 100644
index 0000000..a9c9d67
--- /dev/null
+++ b/sapi/apache_hooks/sapi_apache.c
@@ -0,0 +1,131 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@php.net> |
+ | (with helpful hints from Dean Gaudet <dgaudet@arctic.org> |
+ | PHP 4.0 patches by: |
+ | Zeev Suraski <zeev@zend.com> |
+ | Stig Bakken <ssb@php.net> |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#include "php_apache_http.h"
+
+/* {{{ apache_php_module_main
+ */
+int apache_php_module_main(request_rec *r, int display_source_mode TSRMLS_DC)
+{
+ zend_file_handle file_handle;
+
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ return FAILURE;
+ }
+ /* sending a file handle to another dll is not working
+ so let zend open it. */
+
+ if (display_source_mode) {
+ zend_syntax_highlighter_ini syntax_highlighter_ini;
+
+ php_get_highlight_struct(&syntax_highlighter_ini);
+ if (highlight_file(SG(request_info).path_translated, &syntax_highlighter_ini TSRMLS_CC)){
+ return OK;
+ } else {
+ return NOT_FOUND;
+ }
+ } else {
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.handle.fd = 0;
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.opened_path = NULL;
+ file_handle.free_filename = 0;
+ (void) php_execute_script(&file_handle TSRMLS_CC);
+ }
+ AP(in_request) = 0;
+
+ return (OK);
+}
+/* }}} */
+
+/* {{{ apache_php_module_hook
+ */
+int apache_php_module_hook(request_rec *r, php_handler *handler, zval **ret TSRMLS_DC)
+{
+ zend_file_handle file_handle;
+ zval *req;
+ char *tmp;
+
+#if PHP_SIGCHILD
+ signal(SIGCHLD, sigchld_handler);
+#endif
+ if(AP(current_hook) == AP_RESPONSE) {
+ if (php_request_startup_for_hook(TSRMLS_C) == FAILURE)
+ return FAILURE;
+ }
+ else {
+ if (php_request_startup_for_hook(TSRMLS_C) == FAILURE)
+ return FAILURE;
+ }
+
+ req = php_apache_request_new(r);
+ php_register_variable_ex("request", req, PG(http_globals)[TRACK_VARS_SERVER] TSRMLS_CC);
+
+ switch(handler->type) {
+ case AP_HANDLER_TYPE_FILE:
+ php_register_variable("PHP_SELF_HOOK", handler->name, PG(http_globals)[TRACK_VARS_SERVER] TSRMLS_CC);
+ memset(&file_handle, 0, sizeof(file_handle));
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = handler->name;
+ (void) php_execute_simple_script(&file_handle, ret TSRMLS_CC);
+ break;
+ case AP_HANDLER_TYPE_METHOD:
+ if( (tmp = strstr(handler->name, "::")) != NULL && *(tmp+2) != '\0' ) {
+ zval *class;
+ zval *method;
+ *tmp = '\0';
+ ALLOC_ZVAL(class);
+ ZVAL_STRING(class, handler->name, 1);
+ ALLOC_ZVAL(method);
+ ZVAL_STRING(method, tmp +2, 1);
+ *tmp = ':';
+ call_user_function_ex(EG(function_table), &class, method, ret, 0, NULL, 0, NULL TSRMLS_CC);
+ zval_dtor(class);
+ zval_dtor(method);
+ }
+ else {
+ php_error(E_ERROR, "Unable to call %s - not a Class::Method\n", handler->name);
+ /* not a class::method */
+ }
+ break;
+ default:
+ /* not a valid type */
+ assert(0);
+ break;
+ }
+ zval_dtor(req);
+ AP(in_request) = 0;
+
+ return OK;
+}
+
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/caudium/CREDITS b/sapi/caudium/CREDITS
new file mode 100644
index 0000000..45789db
--- /dev/null
+++ b/sapi/caudium/CREDITS
@@ -0,0 +1,2 @@
+Caudium / Roxen
+David Hedbor
diff --git a/sapi/caudium/README b/sapi/caudium/README
new file mode 100644
index 0000000..86ef656
--- /dev/null
+++ b/sapi/caudium/README
@@ -0,0 +1,16 @@
+Embedded Caudium PHP support. It seems to work but isn't tested
+much. Due to a design choice it requires _Pike_ threads and a
+thread-safe PHP. It doesn't however require _Caudium_ to run in
+threaded mode. Due to the design, utilization of multiple processors
+should be pretty good.
+
+It requires a new Pike 7.0 and Caudium. It will not work with
+Roxen. Also, with the help of the VIRTUAL_DIR code in PHP, PHP
+execution should be very similar to that of mod_php or the cgi
+script.
+
+If you have any questions, please contact me, David Hedbor
+(neotron@php.net or neotron@caudium.net). For more information on
+Caudium, see http://caudium.net/ and http://caudium.org/.
+
+
diff --git a/sapi/caudium/TODO b/sapi/caudium/TODO
new file mode 100644
index 0000000..b8217c5
--- /dev/null
+++ b/sapi/caudium/TODO
@@ -0,0 +1,30 @@
+TODO:
+
+- per-virtual-server configuration
+- configurable limit of number of concurrent PHP executions
+- fix setting of auth info.
+
+FIXED:
++ => fixed
+- => not fixed and no fix planned
+? => Maybe fixed, maybe not.
+
++ Allow multiple headers
+ This is now fixed.
++ fix backtraces
+ Uses th_farm, thus problem is fixed
++ exit in PHP exits Caudium
+ Name conflict with do_exit in Pike and PHP. Reported to both teams.
++ POST newline added?
+ Was a Caudium bug.
++ use th_farm
+ Yeppers. Works great.
+- change cwd in single threaded mode
+ There will be no single threaded mode support. The Caudium module
+ will requite PHP ZTS and Pike threads to run. Single threaded PHP
+ is rather uninteresting anyway.
+? Recursive mutex lock problem:
+ Dunno if this is fixed. Major rewrite so it would have to be
+ retested.
+
+
diff --git a/sapi/caudium/caudium.c b/sapi/caudium/caudium.c
new file mode 100644
index 0000000..d3b834f
--- /dev/null
+++ b/sapi/caudium/caudium.c
@@ -0,0 +1,784 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: David Hedbor <neotron@php.net> |
+ | Based on aolserver SAPI by Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include "php.h"
+#ifdef HAVE_CAUDIUM
+
+#include "php_ini.h"
+#include "php_globals.h"
+#include "SAPI.h"
+#include "php_main.h"
+#include "ext/standard/info.h"
+
+#include "php_version.h"
+
+/* Pike Include Files
+ *
+ * conflicts with pike avoided by only using long names. Requires a new
+ * Pike 0.7 since it was implemented for this interface only.
+ *
+ */
+#define NO_PIKE_SHORTHAND
+
+/* Ok, we are now using Pike level threads to handle PHP5 since
+ * the nice th_farm threads aren't working on Linux with glibc 2.2
+ * (why this is I don't know).
+ */
+#define USE_PIKE_LEVEL_THREADS
+
+#include <fdlib.h>
+#include <program.h>
+#include <pike_types.h>
+#include <interpret.h>
+#include <module_support.h>
+#include <array.h>
+#include <backend.h>
+#include <stralloc.h>
+#include <mapping.h>
+#include <object.h>
+#include <threads.h>
+#include <builtin_functions.h>
+#include <operators.h>
+#include <version.h>
+
+#if (PIKE_MAJOR_VERSION == 7 && PIKE_MINOR_VERSION == 1 && PIKE_BUILD_VERSION >= 12) || PIKE_MAJOR_VERSION > 7 || (PIKE_MAJOR_VERSION == 7 && PIKE_MINOR_VERSION > 1)
+# include "pike_error.h"
+#else
+# include "error.h"
+# ifndef Pike_error
+# define Pike_error error
+# endif
+#endif
+
+/* Pike 7.x and newer */
+#define MY_MAPPING_LOOP(md, COUNT, KEY) \
+ for(COUNT=0;COUNT < md->data->hashsize; COUNT++ ) \
+ for(KEY=md->data->hash[COUNT];KEY;KEY=KEY->next)
+
+#ifndef ZTS
+/* Need thread safety */
+#error You need to compile PHP with threads.
+#endif
+
+#ifndef PIKE_THREADS
+#error The PHP5 module requires that your Pike has thread support.
+#endif
+
+#undef HIDE_GLOBAL_VARIABLES
+#undef REVEAL_GLOBAL_VARIABLES
+#define HIDE_GLOBAL_VARIABLES()
+#define REVEAL_GLOBAL_VARIABLES()
+
+/* php_caudium_request is per-request object storage */
+
+typedef struct
+{
+ struct mapping *request_data;
+ struct object *my_fd_obj;
+ struct svalue done_cb;
+ struct pike_string *filename;
+ int my_fd;
+ int written;
+ TSRMLS_D;
+} php_caudium_request;
+
+
+void pike_module_init(void);
+void pike_module_exit(void);
+static void free_struct(TSRMLS_D);
+void f_php_caudium_request_handler(INT32 args);
+
+/* Defines to get to the data supplied when the script is started. */
+
+/* Per thread storage area id... */
+static int caudium_globals_id;
+
+#define GET_THIS() php_caudium_request *_request = ts_resource(caudium_globals_id)
+#define THIS _request
+#define PTHIS ((php_caudium_request *)(Pike_fp->current_storage))
+/* File descriptor integer. Used to write directly to the FD without
+ * passing Pike
+ */
+#define MY_FD (THIS->my_fd)
+
+/* FD object. Really a PHPScript object from Pike which implements a couple
+ * of functions to handle headers, writing and buffering.
+ */
+#define MY_FD_OBJ ((struct object *)(THIS->my_fd_obj))
+
+/* Mapping with data supplied from the calling Caudium module. Contains
+ * a mapping with headers, an FD object etc.
+ */
+#define REQUEST_DATA ((struct mapping *)(THIS->request_data))
+
+extern int fd_from_object(struct object *o);
+static unsigned char caudium_php_initialized;
+
+#ifndef mt_lock_interpreter
+#define mt_lock_interpreter() mt_lock(&interpreter_lock);
+#define mt_unlock_interpreter() mt_unlock(&interpreter_lock);
+#endif
+
+
+/* This allows calling of pike functions from the PHP callbacks,
+ * which requires the Pike interpreter to be locked.
+ */
+#define THREAD_SAFE_RUN(COMMAND, what) do {\
+ struct thread_state *state;\
+ if((state = thread_state_for_id(th_self()))!=NULL) {\
+ if(!state->swapped) {\
+ COMMAND;\
+ } else {\
+ mt_lock_interpreter();\
+ SWAP_IN_THREAD(state);\
+ COMMAND;\
+ SWAP_OUT_THREAD(state);\
+ mt_unlock_interpreter();\
+ }\
+ }\
+} while(0)
+
+
+
+/* Low level header lookup. Basically looks for the named header in the mapping
+ * headers in the supplied options mapping.
+ */
+
+INLINE static struct svalue *lookup_header(char *headername)
+{
+ struct svalue *headers, *value;
+ struct pike_string *sind;
+ GET_THIS();
+ sind = make_shared_string("env");
+ headers = low_mapping_string_lookup(REQUEST_DATA, sind);
+ free_string(sind);
+ if(!headers || headers->type != PIKE_T_MAPPING) return NULL;
+ sind = make_shared_string(headername);
+ value = low_mapping_string_lookup(headers->u.mapping, sind);
+ free_string(sind);
+ if(!value) return NULL;
+ return value;
+}
+
+/* Lookup a header in the mapping and return the value as a string, or
+ * return the default if it's missing
+ */
+INLINE static char *lookup_string_header(char *headername, char *default_value)
+{
+ struct svalue *head = NULL;
+ THREAD_SAFE_RUN(head = lookup_header(headername), "header lookup");
+ if(!head || head->type != PIKE_T_STRING)
+ return default_value;
+ return head->u.string->str;
+}
+
+/* Lookup a header in the mapping and return the value as if it's an integer
+ * and otherwise return the default.
+ */
+INLINE static int lookup_integer_header(char *headername, int default_value)
+{
+ struct svalue *head = NULL;
+ THREAD_SAFE_RUN(head = lookup_header(headername), "header lookup");
+ if(!head || head->type != PIKE_T_INT)
+ return default_value;
+ return head->u.integer;
+}
+
+/*
+ * php_caudium_low_ub_write() writes data to the client connection. Might be
+ * rewritten to do more direct IO to save CPU and the need to lock the
+ * interpreter for better threading.
+ */
+
+INLINE static int
+php_caudium_low_ub_write(const char *str, uint str_length TSRMLS_DC) {
+ int sent_bytes = 0;
+ struct pike_string *to_write = NULL;
+ GET_THIS();
+ if(!MY_FD_OBJ->prog) {
+ PG(connection_status) = PHP_CONNECTION_ABORTED;
+ zend_bailout();
+ return -1;
+ }
+ to_write = make_shared_binary_string(str, str_length);
+ push_string(to_write);
+ safe_apply(MY_FD_OBJ, "write", 1);
+ if(Pike_sp[-1].type == PIKE_T_INT)
+ sent_bytes = Pike_sp[-1].u.integer;
+ pop_stack();
+ if(sent_bytes != str_length) {
+ /* This means the connection is closed. Dead. Gone. *sniff* */
+ PG(connection_status) = PHP_CONNECTION_ABORTED;
+ zend_bailout();
+ }
+ return sent_bytes;
+}
+
+/*
+ * php_caudium_sapi_ub_write() calls php_caudium_low_ub_write in a Pike thread
+ * safe manner or writes directly to the output FD if RXML post-parsing is
+ * disabled.
+ */
+
+static int
+php_caudium_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ GET_THIS();
+ int sent_bytes = 0, fd = MY_FD;
+ if(fd)
+ {
+ for(sent_bytes=0;sent_bytes < str_length;)
+ {
+ int written;
+ written = fd_write(fd, str + sent_bytes, str_length - sent_bytes);
+ if(written < 0)
+ {
+ switch(errno)
+ {
+ default:
+ /* This means the connection is closed. Dead. Gone. *sniff* */
+ PG(connection_status) = PHP_CONNECTION_ABORTED;
+ zend_bailout();
+ THIS->written += sent_bytes;
+ return sent_bytes;
+ case EINTR:
+ case EWOULDBLOCK:
+ continue;
+ }
+ } else {
+ sent_bytes += written;
+ }
+ }
+ THIS->written += sent_bytes;
+ } else {
+ THREAD_SAFE_RUN(sent_bytes = php_caudium_low_ub_write(str, str_length TSRMLS_CC),
+ "write");
+ }
+ return sent_bytes;
+}
+
+/* php_caudium_set_header() sets a header in the header mapping. Called in a
+ * thread safe manner from php_caudium_sapi_header_handler.
+ */
+INLINE static void
+php_caudium_set_header(char *header_name, char *value, char *p)
+{
+ struct svalue hsval;
+ struct pike_string *hval, *ind, *hind;
+ struct mapping *headermap;
+ struct svalue *s_headermap, *soldval;
+ int vallen;
+ GET_THIS();
+ /* hval = make_shared_string(value); */
+ ind = make_shared_string(" _headers");
+ hind = make_shared_binary_string(header_name,
+ (int)(p - header_name));
+
+ s_headermap = low_mapping_string_lookup(REQUEST_DATA, ind);
+ if(!s_headermap || s_headermap->type != PIKE_T_MAPPING)
+ {
+ struct svalue mappie;
+ mappie.type = PIKE_T_MAPPING;
+ headermap = allocate_mapping(1);
+ mappie.u.mapping = headermap;
+ mapping_string_insert(REQUEST_DATA, ind, &mappie);
+ free_mapping(headermap);
+ hval = make_shared_string(value);
+ } else {
+ headermap = s_headermap->u.mapping;
+ soldval = low_mapping_string_lookup(headermap, hind);
+ vallen = strlen(value);
+ if(soldval != NULL &&
+ soldval->type == PIKE_T_STRING &&
+ soldval->u.string->size_shift == 0) {
+ /* Existing, valid header. Prepend.*/
+ hval = begin_shared_string(soldval->u.string->len + 1 + vallen);
+ MEMCPY(hval->str, soldval->u.string->str, soldval->u.string->len);
+ STR0(hval)[soldval->u.string->len] = '\0';
+ MEMCPY(hval->str+soldval->u.string->len+1, value, vallen);
+ hval = end_shared_string(hval);
+ } else {
+ hval = make_shared_string(value);
+ }
+ }
+ hsval.type = PIKE_T_STRING;
+ hsval.u.string = hval;
+
+ mapping_string_insert(headermap, hind, &hsval);
+
+ free_string(hval);
+ free_string(ind);
+ free_string(hind);
+}
+
+/*
+ * php_caudium_sapi_header_handler() sets a HTTP reply header to be
+ * sent to the client.
+ */
+static int
+php_caudium_sapi_header_handler(sapi_header_struct *sapi_header,
+ sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ char *header_name, *header_content, *p;
+ header_name = sapi_header->header;
+ header_content = p = strchr(header_name, ':');
+
+ if(p) {
+ do {
+ header_content++;
+ } while(*header_content == ' ');
+ THREAD_SAFE_RUN(php_caudium_set_header(header_name, header_content, p), "header handler");
+ }
+ sapi_free_header(sapi_header);
+ return 0;
+}
+
+/*
+ * php_caudium_sapi_send_headers() flushes the headers to the client.
+ * Called before real content is sent by PHP.
+ */
+
+INLINE static int
+php_caudium_low_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ struct pike_string *ind;
+ struct svalue *s_headermap;
+ GET_THIS();
+ if(!MY_FD_OBJ->prog) {
+ PG(connection_status) = PHP_CONNECTION_ABORTED;
+ zend_bailout();
+ return SAPI_HEADER_SEND_FAILED;
+ }
+ ind = make_shared_string(" _headers");
+ s_headermap = low_mapping_string_lookup(REQUEST_DATA, ind);
+ free_string(ind);
+
+ push_int(SG(sapi_headers).http_response_code);
+ if(s_headermap && s_headermap->type == PIKE_T_MAPPING)
+ ref_push_mapping(s_headermap->u.mapping);
+ else
+ push_int(0);
+ safe_apply(MY_FD_OBJ, "send_headers", 2);
+ pop_stack();
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+static int
+php_caudium_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ int res = 0;
+ THREAD_SAFE_RUN(res = php_caudium_low_send_headers(sapi_headers TSRMLS_CC), "send headers");
+ return res;
+}
+
+/*
+ * php_caudium_sapi_read_post() reads a specified number of bytes from
+ * the client. Used for POST/PUT requests.
+ */
+
+INLINE static int php_caudium_low_read_post(char *buf, uint count_bytes)
+{
+ uint total_read = 0;
+ GET_THIS();
+ TSRMLS_FETCH();
+
+ if(!MY_FD_OBJ->prog)
+ {
+ PG(connection_status) = PHP_CONNECTION_ABORTED;
+ zend_bailout();
+ return -1;
+ }
+ push_int(count_bytes);
+ safe_apply(MY_FD_OBJ, "read_post", 1);
+ if(Pike_sp[-1].type == PIKE_T_STRING) {
+ MEMCPY(buf, Pike_sp[-1].u.string->str,
+ (total_read = Pike_sp[-1].u.string->len));
+ buf[total_read] = '\0';
+ } else
+ total_read = 0;
+ pop_stack();
+ return total_read;
+}
+
+static int
+php_caudium_sapi_read_post(char *buf, uint count_bytes TSRMLS_DC)
+{
+ uint total_read = 0;
+ THREAD_SAFE_RUN(total_read = php_caudium_low_read_post(buf, count_bytes), "read post");
+ return total_read;
+}
+
+/*
+ * php_caudium_sapi_read_cookies() returns the Cookie header from
+ * the HTTP request header
+ */
+
+static char *
+php_caudium_sapi_read_cookies(TSRMLS_D)
+{
+ char *cookies;
+ cookies = lookup_string_header("HTTP_COOKIE", NULL);
+ return cookies;
+}
+
+static void php_info_caudium(ZEND_MODULE_INFO_FUNC_ARGS)
+{
+ /* char buf[512]; */
+ php_info_print_table_start();
+ php_info_print_table_row(2, "SAPI module version", "$Id$");
+ /* php_info_print_table_row(2, "Build date", Ns_InfoBuildDate());
+ php_info_print_table_row(2, "Config file path", Ns_InfoConfigFile());
+ php_info_print_table_row(2, "Error Log path", Ns_InfoErrorLog());
+ php_info_print_table_row(2, "Installation path", Ns_InfoHomePath());
+ php_info_print_table_row(2, "Hostname of server", Ns_InfoHostname());
+ php_info_print_table_row(2, "Source code label", Ns_InfoLabel());
+ php_info_print_table_row(2, "Server platform", Ns_InfoPlatform());
+ snprintf(buf, 511, "%s/%s", Ns_InfoServerName(), Ns_InfoServerVersion());
+ php_info_print_table_row(2, "Server version", buf);
+ snprintf(buf, 511, "%d day(s), %02d:%02d:%02d",
+ uptime / 86400,
+ (uptime / 3600) % 24,
+ (uptime / 60) % 60,
+ uptime % 60);
+ php_info_print_table_row(2, "Server uptime", buf);
+ */
+ php_info_print_table_end();
+}
+
+static zend_module_entry php_caudium_module = {
+ STANDARD_MODULE_HEADER,
+ "Caudium",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ php_info_caudium,
+ NULL,
+ STANDARD_MODULE_PROPERTIES
+};
+
+
+INLINE static void low_sapi_caudium_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ int i;
+ struct keypair *k;
+ struct svalue *headers;
+ struct pike_string *sind;
+ struct svalue *ind;
+ struct svalue *val;
+ GET_THIS();
+ php_register_variable("PHP_SELF", SG(request_info).request_uri,
+ track_vars_array TSRMLS_CC);
+ php_register_variable("GATEWAY_INTERFACE", "CGI/1.1",
+ track_vars_array TSRMLS_CC);
+ php_register_variable("REQUEST_METHOD",
+ (char *) SG(request_info).request_method,
+ track_vars_array TSRMLS_CC);
+ php_register_variable("REQUEST_URI", SG(request_info).request_uri,
+ track_vars_array TSRMLS_CC);
+ php_register_variable("PATH_TRANSLATED", SG(request_info).path_translated,
+ track_vars_array TSRMLS_CC);
+
+ sind = make_shared_string("env");
+ headers = low_mapping_string_lookup(REQUEST_DATA, sind);
+ free_string(sind);
+ if(headers && headers->type == PIKE_T_MAPPING) {
+ MY_MAPPING_LOOP(headers->u.mapping, i, k) {
+ ind = &k->ind;
+ val = &k->val;
+ if(ind && ind->type == PIKE_T_STRING &&
+ val && val->type == PIKE_T_STRING) {
+ php_register_variable(ind->u.string->str, val->u.string->str,
+ track_vars_array TSRMLS_CC );
+ }
+ }
+ }
+}
+
+static void sapi_caudium_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ THREAD_SAFE_RUN(low_sapi_caudium_register_variables(track_vars_array TSRMLS_CC), "register_variables");
+}
+
+
+static int php_caudium_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &php_caudium_module, 1)==FAILURE) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+
+/* this structure is static (as in "it does not change") */
+static sapi_module_struct caudium_sapi_module = {
+ "caudium",
+ "Caudium",
+ php_caudium_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+ NULL, /* activate */
+ NULL, /* deactivate */
+ php_caudium_sapi_ub_write, /* unbuffered write */
+ NULL, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+ php_error, /* error handler */
+ php_caudium_sapi_header_handler, /* header handler */
+ php_caudium_sapi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+ php_caudium_sapi_read_post, /* read POST data */
+ php_caudium_sapi_read_cookies, /* read cookies */
+ sapi_caudium_register_variables, /* register server variables */
+ NULL, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+/*
+ * php_caudium_module_main() is called by the per-request handler and
+ * "executes" the script
+ */
+
+static void php_caudium_module_main(php_caudium_request *ureq)
+{
+ int res;
+ zend_file_handle file_handle;
+#ifndef USE_PIKE_LEVEL_THREADS
+ struct thread_state *state;
+ extern struct program *thread_id_prog;
+#endif
+ TSRMLS_FETCH();
+ GET_THIS();
+ THIS->filename = ureq->filename;
+ THIS->done_cb = ureq->done_cb;
+ THIS->my_fd_obj = ureq->my_fd_obj;
+ THIS->my_fd = ureq->my_fd;
+ THIS->request_data = ureq->request_data;
+ free(ureq);
+
+#ifndef USE_PIKE_LEVEL_THREADS
+ mt_lock_interpreter();
+ init_interpreter();
+#if PIKE_MAJOR_VERSION == 7 && PIKE_MINOR_VERSION < 1
+ thread_id = low_clone(thread_id_prog);
+ state = OBJ2THREAD(thread_id);
+ Pike_stack_top=((char *)&state)+ (thread_stack_size-16384) * STACK_DIRECTION;
+ recoveries = NULL;
+ call_c_initializers(thread_id);
+ OBJ2THREAD(thread_id)->id=th_self();
+ num_threads++;
+ thread_table_insert(thread_id);
+ state->status=THREAD_RUNNING;
+#else
+ Pike_interpreter.thread_id = low_clone(thread_id_prog);
+ state = OBJ2THREAD(Pike_interpreter.thread_id);
+ Pike_interpreter.stack_top=((char *)&state)+ (thread_stack_size-16384) * STACK_DIRECTION;
+ Pike_interpreter.recoveries = NULL;
+ call_c_initializers(Pike_interpreter.thread_id);
+ state->id=th_self();
+ /* SWAP_OUT_THREAD(OBJ2THREAD(Pike_interpreter.thread_id)); */
+ num_threads++;
+ thread_table_insert(Pike_interpreter.thread_id);
+ state->status=THREAD_RUNNING;
+#endif
+ state->swapped = 0;
+#endif
+ SG(request_info).query_string = lookup_string_header("QUERY_STRING", 0);
+ SG(server_context) = (void *)1; /* avoid server_context == NULL */
+
+ /* path_translated is apparently the absolute path to the file, not
+ the translated PATH_INFO
+ */
+ SG(request_info).path_translated =
+ lookup_string_header("SCRIPT_FILENAME", NULL);
+ SG(request_info).request_uri = lookup_string_header("DOCUMENT_URI", NULL);
+ if(!SG(request_info).request_uri)
+ SG(request_info).request_uri = lookup_string_header("SCRIPT_NAME", NULL);
+ SG(request_info).request_method = lookup_string_header("REQUEST_METHOD", "GET");
+ SG(request_info).content_length = lookup_integer_header("HTTP_CONTENT_LENGTH", 0);
+ SG(request_info).content_type = lookup_string_header("HTTP_CONTENT_TYPE", NULL);
+ SG(sapi_headers).http_response_code = 200;
+ if (!strcmp(SG(request_info).request_method, "HEAD")) {
+ SG(request_info).headers_only = 1;
+ } else {
+ SG(request_info).headers_only = 0;
+ }
+
+ /* Let PHP5 handle the deconding of the AUTH */
+ php_handle_auth_data(lookup_string_header("HTTP_AUTHORIZATION", NULL), TSRMLS_C);
+ /* Swap out this thread and release the interpreter lock to allow
+ * Pike threads to run. We wait since the above would otherwise require
+ * a lot of unlock/lock.
+ */
+#ifndef USE_PIKE_LEVEL_THREADS
+ SWAP_OUT_THREAD(state);
+ mt_unlock_interpreter();
+#else
+ THREADS_ALLOW();
+#endif
+
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = THIS->filename->str;
+ file_handle.opened_path = NULL;
+ file_handle.free_filename = 0;
+
+ THIS->written = 0;
+ res = php_request_startup(TSRMLS_C);
+
+ if(res == FAILURE) {
+ THREAD_SAFE_RUN({
+ apply_svalue(&THIS->done_cb, 0);
+ pop_stack();
+ free_struct(TSRMLS_C);
+ }, "Negative run response");
+ } else {
+ php_execute_script(&file_handle TSRMLS_CC);
+ php_request_shutdown(NULL);
+ THREAD_SAFE_RUN({
+ push_int(THIS->written);
+ apply_svalue(&THIS->done_cb, 1);
+ pop_stack();
+ free_struct(TSRMLS_C);
+ }, "positive run response");
+ }
+
+#ifndef USE_PIKE_LEVEL_THREADS
+ mt_lock_interpreter();
+ SWAP_IN_THREAD(state);
+#if PIKE_MAJOR_VERSION == 7 && PIKE_MINOR_VERSION < 1
+ state->status=THREAD_EXITED;
+ co_signal(& state->status_change);
+ thread_table_delete(thread_id);
+ free_object(thread_id);
+ thread_id=NULL;
+#else
+ state->status=THREAD_EXITED;
+ co_signal(& state->status_change);
+ thread_table_delete(Pike_interpreter.thread_id);
+ free_object(Pike_interpreter.thread_id);
+ Pike_interpreter.thread_id=NULL;
+#endif
+ cleanup_interpret();
+ num_threads--;
+ mt_unlock_interpreter();
+#else
+ THREADS_DISALLOW();
+#endif
+}
+
+/*
+ * The php_caudium_request_handler() is called per request and handles
+ * everything for one request.
+ */
+
+void f_php_caudium_request_handler(INT32 args)
+{
+ struct object *my_fd_obj;
+ struct mapping *request_data;
+ struct svalue *done_callback;
+ struct pike_string *script;
+ struct svalue *raw_fd;
+ struct pike_string *ind;
+ php_caudium_request *_request;
+ THIS = malloc(sizeof(php_caudium_request));
+ if(THIS == NULL)
+ Pike_error("Out of memory.");
+
+ get_all_args("PHP5.Interpreter->run", args, "%S%m%O%*", &script,
+ &request_data, &my_fd_obj, &done_callback);
+ if(done_callback->type != PIKE_T_FUNCTION)
+ Pike_error("PHP5.Interpreter->run: Bad argument 4, expected function.\n");
+ add_ref(request_data);
+ add_ref(my_fd_obj);
+ add_ref(script);
+
+ THIS->request_data = request_data;
+ THIS->my_fd_obj = my_fd_obj;
+ THIS->filename = script;
+ assign_svalue_no_free(&THIS->done_cb, done_callback);
+
+ ind = make_shared_binary_string("my_fd", 5);
+ raw_fd = low_mapping_string_lookup(THIS->request_data, ind);
+ if(raw_fd && raw_fd->type == PIKE_T_OBJECT)
+ {
+ int fd = fd_from_object(raw_fd->u.object);
+ if(fd == -1)
+ THIS->my_fd = 0; /* Don't send directly to this FD... */
+ else
+ THIS->my_fd = fd;
+ } else
+ THIS->my_fd = 0;
+#ifdef USE_PIKE_LEVEL_THREADS
+ php_caudium_module_main(THIS);
+#else
+ th_farm((void (*)(void *))php_caudium_module_main, THIS);
+#endif
+ pop_n_elems(args);
+}
+
+static void free_struct(TSRMLS_D)
+{
+ GET_THIS();
+ if(THIS->request_data) free_mapping(THIS->request_data);
+ if(THIS->my_fd_obj) free_object(THIS->my_fd_obj);
+ free_svalue(&THIS->done_cb);
+ if(THIS->filename) free_string(THIS->filename);
+ MEMSET(THIS, 0, sizeof(php_caudium_request));
+}
+
+
+/*
+ * pike_module_init() is called by Pike once at startup
+ *
+ * This functions allocates basic structures
+ */
+
+void pike_module_init( void )
+{
+ if (!caudium_php_initialized) {
+ caudium_php_initialized = 1;
+ tsrm_startup(1, 1, 0, NULL);
+ ts_allocate_id(&caudium_globals_id, sizeof(php_caudium_request), NULL, NULL);
+ sapi_startup(&caudium_sapi_module);
+ sapi_module.startup(&caudium_sapi_module);
+ }
+ start_new_program(); /* Text */
+ pike_add_function("run", f_php_caudium_request_handler,
+ "function(string, mapping, object, function:void)", 0);
+ end_class("Interpreter", 0);
+}
+
+/*
+ * pike_module_exit() performs the last steps before the
+ * server exists. Shutdowns basic services and frees memory
+ */
+
+void pike_module_exit(void)
+{
+ caudium_php_initialized = 0;
+ sapi_module.shutdown(&caudium_sapi_module);
+ tsrm_shutdown();
+}
+#endif
diff --git a/sapi/caudium/config.m4 b/sapi/caudium/config.m4
new file mode 100644
index 0000000..8aba33e
--- /dev/null
+++ b/sapi/caudium/config.m4
@@ -0,0 +1,98 @@
+dnl
+dnl $Id$
+dnl
+
+RESULT=no
+PHP_ARG_WITH(caudium,,
+[ --with-caudium[=DIR] Build PHP as a Pike module for use with Caudium.
+ DIR is the Caudium server dir [/usr/local/caudium/server]], no, no)
+
+AC_MSG_CHECKING([for Caudium support])
+
+if test "$PHP_CAUDIUM" != "no"; then
+ if test "$prefix" = "NONE"; then CPREF=/usr/local/; fi
+ if test ! -d $PHP_CAUDIUM ; then
+ if test "$prefix" = "NONE"; then
+ PHP_CAUDIUM=/usr/local/caudium/server/
+ else
+ PHP_CAUDIUM=$prefix/caudium/server/
+ fi
+ fi
+ if test -f $PHP_CAUDIUM/bin/caudium; then
+ PIKE=$PHP_CAUDIUM/bin/caudium
+ elif test -f $PHP_CAUDIUM/bin/pike; then
+ PIKE=$PHP_CAUDIUM/bin/pike
+ else
+ AC_MSG_ERROR([Could not find a pike in $PHP_CAUDIUM/bin/])
+ fi
+ if $PIKE -e 'float v; int rel;sscanf(version(), "Pike v%f release %d", v, rel);v += rel/10000.0; if(v < 7.0268) exit(1); exit(0);'; then
+ PIKE_MODULE_DIR=`$PIKE --show-paths 2>&1| grep '^Module' | sed -e 's/.*: //'`
+ PIKE_INCLUDE_DIR=`echo $PIKE_MODULE_DIR | sed -e 's,lib/pike/modules,include/pike,' -e 's,lib/modules,include/pike,' `
+ if test -z "$PIKE_INCLUDE_DIR" || test -z "$PIKE_MODULE_DIR"; then
+ AC_MSG_ERROR(Failed to figure out Pike module and include directories)
+ fi
+ AC_MSG_RESULT(yes)
+ PIKE=`echo $PIKE | pike -e 'int tries=100;
+ string orig,pike=Stdio.File("stdin")->read()-"\n";
+ orig=pike;
+ if(search(orig, "/"))
+ orig = combine_path(getcwd(), orig);
+ while(!catch(pike=readlink(pike)) && tries--)
+ ;
+ write(combine_path(dirname(orig), pike)); '`
+ PHP_ADD_INCLUDE($PIKE_INCLUDE_DIR)
+ if test "$prefix" != "NONE"; then
+ PIKE_C_INCLUDE=$prefix/include/`basename $PIKE`
+ else
+ PIKE_C_INCLUDE=/usr/local/include/`basename $PIKE`
+ fi
+ AC_MSG_CHECKING([for C includes in $PIKE_C_INCLUDE])
+ if test -f $PIKE_C_INCLUDE/version.h; then
+ PIKE_TEST_VER=`$PIKE -e 'string v; int rel;sscanf(version(), "Pike v%s release %d", v, rel); write(v+"."+rel);'`
+ ###### VERSION MATCH CHECK #######
+ PMAJOR="^#define PIKE_MAJOR_VERSION"
+ PMINOR="^#define PIKE_MINOR_VERSION"
+ PBUILD="^#define PIKE_BUILD_VERSION"
+
+ PIKE_CMAJOR_VERSION=0
+ PIKE_CMINOR_VERSION=0
+ PIKE_CBUILD_VERSION=0
+
+ PIKE_CMAJOR_VERSION=`grep "$PMAJOR" $PIKE_C_INCLUDE/version.h | sed -e 's/\(#define.*N \)\(.*\)/\2/'`
+ if test -z "$PIKE_CMAJOR_VERSION"; then
+ if test -n "`grep f_version $PIKE_C_INCLUDE/version.h`"; then
+ PIKE_CMAJOR_VERSION=6
+ fi
+ else
+ PIKE_CMINOR_VERSION=`grep "$PMINOR" $PIKE_C_INCLUDE/version.h | sed -e 's/\(#define.*N \)\(.*\)/\2/'`
+ PIKE_CBUILD_VERSION=`grep "$PBUILD" $PIKE_C_INCLUDE/version.h | sed -e 's/\(#define.*N \)\(.*\)/\2/'`
+ fi
+
+ if test "$PIKE_TEST_VER" = "${PIKE_CMAJOR_VERSION}.${PIKE_CMINOR_VERSION}.${PIKE_CBUILD_VERSION}"; then
+ PHP_ADD_INCLUDE($PIKE_C_INCLUDE)
+ PIKE_INCLUDE_DIR="$PIKE_INCLUDE_DIR, $PIKE_C_INCLUDE"
+ AC_MSG_RESULT(found)
+ else
+ AC_MSG_RESULT(version mismatch)
+ fi
+ else
+ AC_MSG_RESULT(not found)
+ fi
+ else
+ AC_MSG_ERROR([Caudium PHP5 requires Pike 7.0 or newer])
+ fi
+ PIKE_VERSION=`$PIKE -e 'string v; int rel;sscanf(version(), "Pike v%s release %d", v, rel); write(v+"."+rel);'`
+ AC_DEFINE(HAVE_CAUDIUM,1,[Whether to compile with Caudium support])
+ PHP_SELECT_SAPI(caudium, shared, caudium.c)
+ INSTALL_IT="\$(INSTALL) -m 0755 $SAPI_SHARED $PHP_CAUDIUM/lib/$PIKE_VERSION/PHP5.so"
+ RESULT=" *** Pike binary used: $PIKE
+ *** Pike include dir(s) used: $PIKE_INCLUDE_DIR
+ *** Pike version: $PIKE_VERSION"
+ dnl Always use threads since thread-free support really blows.
+ PHP_BUILD_THREAD_SAFE
+fi
+AC_MSG_RESULT($RESULT)
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/cgi/CHANGES b/sapi/cgi/CHANGES
new file mode 100755
index 0000000..de02947
--- /dev/null
+++ b/sapi/cgi/CHANGES
@@ -0,0 +1,34 @@
+In PHP5.3 all additional configure options (except --enable-cgi) are removed:
+
+ --enable-fastcgi CGI: If this is enabled, the cgi module will
+ be built with support for fastcgi also
+
+ Now fastcgi is always enabled
+
+ --disable-path-info-check CGI: If this is disabled, paths such as
+ /info.php/test?a=b will fail to work
+
+ Now it is enabled by default, but can be disabled
+ with ini directive "cgi.fix_pathinfo=0"
+
+ --enable-force-cgi-redirect
+ CGI: Enable the security check for internal server
+ redirects. You should use this if you are
+ running the CGI version with Apache
+
+ Now it is enabled by default, but can be disabled
+ with ini directive "cgi.force_redirect=0"
+
+ --enable-discard-path CGI: If this is enabled, the PHP CGI binary
+ can safely be placed outside of the
+ web tree and people will not be able
+ to circumvent .htaccess security
+
+ This option had effect only with
+ --disable-path-info-check or "cgi.fix_pathinfo=0".
+ Seems it needs only for CGI configuration that
+ require each script start from "#! /usr/bin/php".
+
+ Now it is disabled by default, but can be enabled
+ with ini directive "cgi.discard_path=1".
+
diff --git a/sapi/cgi/CREDITS b/sapi/cgi/CREDITS
new file mode 100644
index 0000000..1a2ec49
--- /dev/null
+++ b/sapi/cgi/CREDITS
@@ -0,0 +1,2 @@
+CGI / FastCGI
+Rasmus Lerdorf, Stig Bakken, Shane Caraveo, Dmitry Stogov
diff --git a/sapi/cgi/Makefile.frag b/sapi/cgi/Makefile.frag
new file mode 100644
index 0000000..505119e
--- /dev/null
+++ b/sapi/cgi/Makefile.frag
@@ -0,0 +1,9 @@
+cgi: $(SAPI_CGI_PATH)
+
+$(SAPI_CGI_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_CGI_OBJS)
+ $(BUILD_CGI)
+
+install-cgi: $(SAPI_CGI_PATH)
+ @echo "Installing PHP CGI binary: $(INSTALL_ROOT)$(bindir)/"
+ @$(INSTALL) -m 0755 $(SAPI_CGI_PATH) $(INSTALL_ROOT)$(bindir)/$(program_prefix)php-cgi$(program_suffix)$(EXEEXT)
+
diff --git a/sapi/cgi/README.FastCGI b/sapi/cgi/README.FastCGI
new file mode 100644
index 0000000..3dda295
--- /dev/null
+++ b/sapi/cgi/README.FastCGI
@@ -0,0 +1,151 @@
+Credits:
+Ben Mansell, Stephen Landamore, Daniel Silverstone, Shane Caraveo
+
+Building PHP
+------------
+
+You must add '--enable-fastcgi' to the configure command on Linux or
+OSX based systems to get fastcgi support in the php-cgi binary. You
+also must not use '--enable-discard-path'.
+
+Running the FastCGI PHP module
+------------------------------
+
+There are two ways to run the resulting 'php' binary after the fastcgi
+version has been built:
+
+1) Configure your web server to run the PHP binary itself.
+
+This is the simplest method, obviously you will have to configure your
+web server appropriately. Some web servers may also not support this method,
+or may not be as efficient.
+
+2) Run PHP separately from the web server.
+
+In this setup, PHP is started as a separate process entirely from the web
+server. It will listen on a socket for new FastCGI requests, and deliver
+PHP pages as appropriate. This is the recommended way of running PHP-FastCGI.
+To run this way, you must start the PHP binary running by giving it an IP
+and a port number to listen to on the command line, e.g.:
+
+ ./php -b 127.0.0.1:8002
+
+The above line is the recommended way of running FastCGI. You usually
+want the FastCGI server to provide services to the localhost, not
+everyone on the Internet.
+
+If your web server sits on a remote host, you can make FastCGI listen
+on all interfaces:
+
+ ./php -b :8002
+ ./php -b "*:8002"
+
+Note that hostnames are not supported.
+
+You must also configure your web server to connect to the appropriate port
+in order to talk to the PHP FastCGI process.
+
+The advantage of running PHP in this way is that it entirely separates the
+web server and PHP process, so that one cannot disrupt the other. It also
+allows PHP to be on an entirely separate machine from the web server if need
+be, you could even have several web servers utilising the same running PHP
+process if required!
+
+
+Using FastCGI PHP with Apache
+=============================
+
+First of all, you may well ask 'Why?'. After all, Apache already has mod_php.
+However, there are advantages to running PHP with FastCGI. Separating the
+PHP code from the web server removes 'bloat' from the main server, and should
+improve the performance of non-PHP requests. Secondly, having one permanent
+PHP process as opposed to one per apache process means that shared resources
+like persistent database connections are used more efficiently.
+
+First of all, make sure that the FastCGI module is enabled. You should have
+a line in your config like:
+
+ LoadModule fastcgi_module /usr/lib/apache/2.0/mod_fastcgi.so
+
+Don't load mod_php, by the way. Make sure it is commented out!
+
+ #LoadModule php5_module /usr/lib/apache/2.0/libphp5.so
+
+Now, we'll create a fcgi-bin directory, just like you would do with normal
+CGI scripts. You'll need to create a directory somewhere to store your
+FastCGI binaries. We'll use /space/fcgi-bin/ for this example. Remember to
+copy the FastCGI-PHP binary in there. (named 'php-cgi') This sets up
+php to run under mod_fastcgi as a dynamic server.
+
+ ScriptAlias /fcgi-bin/ /space/fcgi-bin/
+ <Location /fcgi-bin/>
+ Options ExecCGI
+ SetHandler fastcgi-script
+ </Location>
+
+To setup a specific static configuration for php, you have to use
+the FastCgiServer configuration for mod_fastcgi. For this, do not
+use the above configuration, but rather the following.
+(see mod_fastcgi docs for more configuration information):
+
+ Alias /fcgi-bin/ /space/fcgi-bin/
+ FastCgiServer /path/to/php-cgi -processes 5
+
+For either of the above configurations, we need to tell Apache to
+use the FastCGI binary /fcgi-bin/php to deliver PHP pages.
+All that is needed is:
+
+ AddType application/x-httpd-fastphp .php
+ Action application/x-httpd-fastphp /fcgi-bin/php-cgi
+
+Now, if you restart Apache, php pages should now be delivered!
+
+Using FastCGI PHP with IIS or iPlanet
+=====================================
+
+FastCGI server plugins are available at www.caraveo.com/fastcgi/
+Documentation on these are sparse. iPlanet is not very tested,
+and no makefile exists yet for unix based iPlanet servers.
+
+
+Security
+--------
+
+Be sure to run the php binary as an appropriate userid. Also, firewall out
+the port that PHP is listening on. In addition, you can set the environment
+variable FCGI_WEB_SERVER_ADDRS to control who can connect to the FastCGI.
+Set it to a comma separated list of IP addresses, e.g.:
+
+export FCGI_WEB_SERVER_ADDRS=199.170.183.28,199.170.183.71
+
+
+Tuning
+------
+
+There are a few tuning parameters that can be tweaked to control the
+performance of FastCGI PHP. The following are environment variables that can
+be set before running the PHP binary:
+
+PHP_FCGI_CHILDREN (default value: 0)
+
+This controls how many child processes the PHP process spawns. When the
+fastcgi starts, it creates a number of child processes which handle one
+page request at a time. Value 0 means that PHP willnot start additional
+processes and main process will handle FastCGI requests by itself. Note that
+this process may die (because of PHP_FCGI_MAX_REQUESTS) and it willnot
+respawned automatic. Values 1 and above force PHP start additioanl processes
+those will handle requests. The main process will restart children in case of
+their death. So by default, you will be able to handle 1 concurrent PHP page
+requests. Further requests will be queued. Increasing this number will allow
+for better concurrency, especially if you have pages that take a significant
+time to create, or supply a lot of data (e.g. downloading huge files via PHP).
+On the other hand, having more processes running will use more RAM, and letting
+too many PHP pages be generated concurrently will mean that each request will
+be slow.
+
+PHP_FCGI_MAX_REQUESTS (default value: 500)
+
+This controls how many requests each child process will handle before
+exitting. When one process exits, another will be created. This tuning is
+necessary because several PHP functions are known to have memory leaks. If the
+PHP processes were left around forever, they would be become very inefficient.
diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c
new file mode 100644
index 0000000..c8dfec0
--- /dev/null
+++ b/sapi/cgi/cgi_main.c
@@ -0,0 +1,2606 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
+ | Stig Bakken <ssb@php.net> |
+ | Zeev Suraski <zeev@zend.com> |
+ | FastCGI: Ben Mansell <php@slimyhorror.com> |
+ | Shane Caraveo <shane@caraveo.com> |
+ | Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "php.h"
+#include "php_globals.h"
+#include "php_variables.h"
+#include "zend_modules.h"
+
+#include "SAPI.h"
+
+#include <stdio.h>
+#include "php.h"
+
+#ifdef PHP_WIN32
+# include "win32/time.h"
+# include "win32/signal.h"
+# include <process.h>
+#endif
+
+#if HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+
+#if HAVE_SETLOCALE
+# include <locale.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#include "zend.h"
+#include "zend_extensions.h"
+#include "php_ini.h"
+#include "php_globals.h"
+#include "php_main.h"
+#include "fopen_wrappers.h"
+#include "ext/standard/php_standard.h"
+#include "ext/standard/url.h"
+
+#ifdef PHP_WIN32
+# include <io.h>
+# include <fcntl.h>
+# include "win32/php_registry.h"
+#endif
+
+#ifdef __riscos__
+# include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS;
+#endif
+
+#include "zend_compile.h"
+#include "zend_execute.h"
+#include "zend_highlight.h"
+#include "zend_indent.h"
+
+#include "php_getopt.h"
+
+#include "fastcgi.h"
+
+#ifndef PHP_WIN32
+/* XXX this will need to change later when threaded fastcgi is implemented. shane */
+struct sigaction act, old_term, old_quit, old_int;
+#endif
+
+static void (*php_php_import_environment_variables)(zval *array_ptr TSRMLS_DC);
+
+#ifndef PHP_WIN32
+/* these globals used for forking children on unix systems */
+/**
+ * Number of child processes that will get created to service requests
+ */
+static int children = 0;
+
+
+/**
+ * Set to non-zero if we are the parent process
+ */
+static int parent = 1;
+
+/* Did parent received exit signals SIG_TERM/SIG_INT/SIG_QUIT */
+static int exit_signal = 0;
+
+/* Is Parent waiting for children to exit */
+static int parent_waiting = 0;
+
+/**
+ * Process group
+ */
+static pid_t pgroup;
+#endif
+
+#define PHP_MODE_STANDARD 1
+#define PHP_MODE_HIGHLIGHT 2
+#define PHP_MODE_INDENT 3
+#define PHP_MODE_LINT 4
+#define PHP_MODE_STRIP 5
+
+static char *php_optarg = NULL;
+static int php_optind = 1;
+static zend_module_entry cgi_module_entry;
+
+static const opt_struct OPTIONS[] = {
+ {'a', 0, "interactive"},
+ {'b', 1, "bindpath"},
+ {'C', 0, "no-chdir"},
+ {'c', 1, "php-ini"},
+ {'d', 1, "define"},
+ {'e', 0, "profile-info"},
+ {'f', 1, "file"},
+ {'h', 0, "help"},
+ {'i', 0, "info"},
+ {'l', 0, "syntax-check"},
+ {'m', 0, "modules"},
+ {'n', 0, "no-php-ini"},
+ {'q', 0, "no-header"},
+ {'s', 0, "syntax-highlight"},
+ {'s', 0, "syntax-highlighting"},
+ {'w', 0, "strip"},
+ {'?', 0, "usage"},/* help alias (both '?' and 'usage') */
+ {'v', 0, "version"},
+ {'z', 1, "zend-extension"},
+ {'T', 1, "timing"},
+ {'-', 0, NULL} /* end of args */
+};
+
+typedef struct _php_cgi_globals_struct {
+ zend_bool rfc2616_headers;
+ zend_bool nph;
+ zend_bool check_shebang_line;
+ zend_bool fix_pathinfo;
+ zend_bool force_redirect;
+ zend_bool discard_path;
+ zend_bool fcgi_logging;
+ char *redirect_status_env;
+#ifdef PHP_WIN32
+ zend_bool impersonate;
+#endif
+ HashTable user_config_cache;
+} php_cgi_globals_struct;
+
+/* {{{ user_config_cache
+ *
+ * Key for each cache entry is dirname(PATH_TRANSLATED).
+ *
+ * NOTE: Each cache entry config_hash contains the combination from all user ini files found in
+ * the path starting from doc_root throught to dirname(PATH_TRANSLATED). There is no point
+ * storing per-file entries as it would not be possible to detect added / deleted entries
+ * between separate files.
+ */
+typedef struct _user_config_cache_entry {
+ time_t expires;
+ HashTable *user_config;
+} user_config_cache_entry;
+
+static void user_config_cache_entry_dtor(user_config_cache_entry *entry)
+{
+ zend_hash_destroy(entry->user_config);
+ free(entry->user_config);
+}
+/* }}} */
+
+#ifdef ZTS
+static int php_cgi_globals_id;
+#define CGIG(v) TSRMG(php_cgi_globals_id, php_cgi_globals_struct *, v)
+#else
+static php_cgi_globals_struct php_cgi_globals;
+#define CGIG(v) (php_cgi_globals.v)
+#endif
+
+#ifdef PHP_WIN32
+#define TRANSLATE_SLASHES(path) \
+ { \
+ char *tmp = path; \
+ while (*tmp) { \
+ if (*tmp == '\\') *tmp = '/'; \
+ tmp++; \
+ } \
+ }
+#else
+#define TRANSLATE_SLASHES(path)
+#endif
+
+static int print_module_info(zend_module_entry *module, void *arg TSRMLS_DC)
+{
+ php_printf("%s\n", module->name);
+ return 0;
+}
+
+static int module_name_cmp(const void *a, const void *b TSRMLS_DC)
+{
+ Bucket *f = *((Bucket **) a);
+ Bucket *s = *((Bucket **) b);
+
+ return strcasecmp( ((zend_module_entry *)f->pData)->name,
+ ((zend_module_entry *)s->pData)->name);
+}
+
+static void print_modules(TSRMLS_D)
+{
+ HashTable sorted_registry;
+ zend_module_entry tmp;
+
+ zend_hash_init(&sorted_registry, 50, NULL, NULL, 1);
+ zend_hash_copy(&sorted_registry, &module_registry, NULL, &tmp, sizeof(zend_module_entry));
+ zend_hash_sort(&sorted_registry, zend_qsort, module_name_cmp, 0 TSRMLS_CC);
+ zend_hash_apply_with_argument(&sorted_registry, (apply_func_arg_t) print_module_info, NULL TSRMLS_CC);
+ zend_hash_destroy(&sorted_registry);
+}
+
+static int print_extension_info(zend_extension *ext, void *arg TSRMLS_DC)
+{
+ php_printf("%s\n", ext->name);
+ return 0;
+}
+
+static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s TSRMLS_DC)
+{
+ return strcmp( ((zend_extension *)(*f)->data)->name,
+ ((zend_extension *)(*s)->data)->name);
+}
+
+static void print_extensions(TSRMLS_D)
+{
+ zend_llist sorted_exts;
+
+ zend_llist_copy(&sorted_exts, &zend_extensions);
+ sorted_exts.dtor = NULL;
+ zend_llist_sort(&sorted_exts, extension_name_cmp TSRMLS_CC);
+ zend_llist_apply_with_argument(&sorted_exts, (llist_apply_with_arg_func_t) print_extension_info, NULL TSRMLS_CC);
+ zend_llist_destroy(&sorted_exts);
+}
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+
+static inline size_t sapi_cgi_single_write(const char *str, uint str_length TSRMLS_DC)
+{
+#ifdef PHP_WRITE_STDOUT
+ long ret;
+
+ ret = write(STDOUT_FILENO, str, str_length);
+ if (ret <= 0) return 0;
+ return ret;
+#else
+ size_t ret;
+
+ ret = fwrite(str, 1, MIN(str_length, 16384), stdout);
+ return ret;
+#endif
+}
+
+static int sapi_cgi_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ const char *ptr = str;
+ uint remaining = str_length;
+ size_t ret;
+
+ while (remaining > 0) {
+ ret = sapi_cgi_single_write(ptr, remaining TSRMLS_CC);
+ if (!ret) {
+ php_handle_aborted_connection();
+ return str_length - remaining;
+ }
+ ptr += ret;
+ remaining -= ret;
+ }
+
+ return str_length;
+}
+
+static int sapi_fcgi_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ const char *ptr = str;
+ uint remaining = str_length;
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+
+ while (remaining > 0) {
+ long ret = fcgi_write(request, FCGI_STDOUT, ptr, remaining);
+
+ if (ret <= 0) {
+ php_handle_aborted_connection();
+ return str_length - remaining;
+ }
+ ptr += ret;
+ remaining -= ret;
+ }
+
+ return str_length;
+}
+
+static void sapi_cgi_flush(void *server_context)
+{
+ if (fflush(stdout) == EOF) {
+ php_handle_aborted_connection();
+ }
+}
+
+static void sapi_fcgi_flush(void *server_context)
+{
+ fcgi_request *request = (fcgi_request*) server_context;
+
+ if (
+#ifndef PHP_WIN32
+ !parent &&
+#endif
+ request && !fcgi_flush(request, 0)) {
+
+ php_handle_aborted_connection();
+ }
+}
+
+#define SAPI_CGI_MAX_HEADER_LENGTH 1024
+
+typedef struct _http_error {
+ int code;
+ const char* msg;
+} http_error;
+
+static const http_error http_error_codes[] = {
+ {100, "Continue"},
+ {101, "Switching Protocols"},
+ {200, "OK"},
+ {201, "Created"},
+ {202, "Accepted"},
+ {203, "Non-Authoritative Information"},
+ {204, "No Content"},
+ {205, "Reset Content"},
+ {206, "Partial Content"},
+ {300, "Multiple Choices"},
+ {301, "Moved Permanently"},
+ {302, "Moved Temporarily"},
+ {303, "See Other"},
+ {304, "Not Modified"},
+ {305, "Use Proxy"},
+ {400, "Bad Request"},
+ {401, "Unauthorized"},
+ {402, "Payment Required"},
+ {403, "Forbidden"},
+ {404, "Not Found"},
+ {405, "Method Not Allowed"},
+ {406, "Not Acceptable"},
+ {407, "Proxy Authentication Required"},
+ {408, "Request Time-out"},
+ {409, "Conflict"},
+ {410, "Gone"},
+ {411, "Length Required"},
+ {412, "Precondition Failed"},
+ {413, "Request Entity Too Large"},
+ {414, "Request-URI Too Large"},
+ {415, "Unsupported Media Type"},
+ {428, "Precondition Required"},
+ {429, "Too Many Requests"},
+ {431, "Request Header Fields Too Large"},
+ {500, "Internal Server Error"},
+ {501, "Not Implemented"},
+ {502, "Bad Gateway"},
+ {503, "Service Unavailable"},
+ {504, "Gateway Time-out"},
+ {505, "HTTP Version not supported"},
+ {511, "Network Authentication Required"},
+ {0, NULL}
+};
+
+static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ char buf[SAPI_CGI_MAX_HEADER_LENGTH];
+ sapi_header_struct *h;
+ zend_llist_position pos;
+ zend_bool ignore_status = 0;
+ int response_status = SG(sapi_headers).http_response_code;
+
+ if (SG(request_info).no_headers == 1) {
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+ }
+
+ if (CGIG(nph) || SG(sapi_headers).http_response_code != 200)
+ {
+ int len;
+ zend_bool has_status = 0;
+
+ if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) {
+ char *s;
+ len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s\r\n", SG(sapi_headers).http_status_line);
+ if ((s = strchr(SG(sapi_headers).http_status_line, ' '))) {
+ response_status = atoi((s + 1));
+ }
+
+ if (len > SAPI_CGI_MAX_HEADER_LENGTH) {
+ len = SAPI_CGI_MAX_HEADER_LENGTH;
+ }
+
+ } else {
+ char *s;
+
+ if (SG(sapi_headers).http_status_line &&
+ (s = strchr(SG(sapi_headers).http_status_line, ' ')) != 0 &&
+ (s - SG(sapi_headers).http_status_line) >= 5 &&
+ strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0
+ ) {
+ len = slprintf(buf, sizeof(buf), "Status:%s\r\n", s);
+ response_status = atoi((s + 1));
+ } else {
+ h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+ if (h->header_len > sizeof("Status:")-1 &&
+ strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0
+ ) {
+ has_status = 1;
+ break;
+ }
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ }
+ if (!has_status) {
+ http_error *err = (http_error*)http_error_codes;
+
+ while (err->code != 0) {
+ if (err->code == SG(sapi_headers).http_response_code) {
+ break;
+ }
+ err++;
+ }
+ if (err->msg) {
+ len = slprintf(buf, sizeof(buf), "Status: %d %s\r\n", SG(sapi_headers).http_response_code, err->msg);
+ } else {
+ len = slprintf(buf, sizeof(buf), "Status: %d\r\n", SG(sapi_headers).http_response_code);
+ }
+ }
+ }
+ }
+
+ if (!has_status) {
+ PHPWRITE_H(buf, len);
+ ignore_status = 1;
+ }
+ }
+
+ h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+ /* prevent CRLFCRLF */
+ if (h->header_len) {
+ if (h->header_len > sizeof("Status:")-1 &&
+ strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0
+ ) {
+ if (!ignore_status) {
+ ignore_status = 1;
+ PHPWRITE_H(h->header, h->header_len);
+ PHPWRITE_H("\r\n", 2);
+ }
+ } else if (response_status == 304 && h->header_len > sizeof("Content-Type:")-1 &&
+ strncasecmp(h->header, "Content-Type:", sizeof("Content-Type:")-1) == 0
+ ) {
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ continue;
+ } else {
+ PHPWRITE_H(h->header, h->header_len);
+ PHPWRITE_H("\r\n", 2);
+ }
+ }
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ }
+ PHPWRITE_H("\r\n", 2);
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+#ifndef STDIN_FILENO
+# define STDIN_FILENO 0
+#endif
+
+static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ uint read_bytes = 0;
+ int tmp_read_bytes;
+
+ count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes));
+ while (read_bytes < count_bytes) {
+ tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes);
+ if (tmp_read_bytes <= 0) {
+ break;
+ }
+ read_bytes += tmp_read_bytes;
+ }
+ return read_bytes;
+}
+
+static int sapi_fcgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ uint read_bytes = 0;
+ int tmp_read_bytes;
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+
+ count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes));
+ while (read_bytes < count_bytes) {
+ tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes);
+ if (tmp_read_bytes <= 0) {
+ break;
+ }
+ read_bytes += tmp_read_bytes;
+ }
+ return read_bytes;
+}
+
+static char *sapi_cgi_getenv(char *name, size_t name_len TSRMLS_DC)
+{
+ return getenv(name);
+}
+
+static char *sapi_fcgi_getenv(char *name, size_t name_len TSRMLS_DC)
+{
+ /* when php is started by mod_fastcgi, no regular environment
+ * is provided to PHP. It is always sent to PHP at the start
+ * of a request. So we have to do our own lookup to get env
+ * vars. This could probably be faster somehow. */
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+ char *ret = fcgi_getenv(request, name, name_len);
+
+ if (ret) return ret;
+ /* if cgi, or fastcgi and not found in fcgi env
+ check the regular environment */
+ return getenv(name);
+}
+
+static char *_sapi_cgi_putenv(char *name, int name_len, char *value)
+{
+#if !HAVE_SETENV || !HAVE_UNSETENV
+ int len;
+ char *buf;
+#endif
+
+#if HAVE_SETENV
+ if (value) {
+ setenv(name, value, 1);
+ }
+#endif
+#if HAVE_UNSETENV
+ if (!value) {
+ unsetenv(name);
+ }
+#endif
+
+#if !HAVE_SETENV || !HAVE_UNSETENV
+ /* if cgi, or fastcgi and not found in fcgi env
+ check the regular environment
+ this leaks, but it's only cgi anyway, we'll fix
+ it for 5.0
+ */
+ len = name_len + (value ? strlen(value) : 0) + sizeof("=") + 2;
+ buf = (char *) malloc(len);
+ if (buf == NULL) {
+ return getenv(name);
+ }
+#endif
+#if !HAVE_SETENV
+ if (value) {
+ len = slprintf(buf, len - 1, "%s=%s", name, value);
+ putenv(buf);
+ }
+#endif
+#if !HAVE_UNSETENV
+ if (!value) {
+ len = slprintf(buf, len - 1, "%s=", name);
+ putenv(buf);
+ }
+#endif
+ return getenv(name);
+}
+
+static char *sapi_cgi_read_cookies(TSRMLS_D)
+{
+ return getenv("HTTP_COOKIE");
+}
+
+static char *sapi_fcgi_read_cookies(TSRMLS_D)
+{
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+
+ return FCGI_GETENV(request, "HTTP_COOKIE");
+}
+
+static void cgi_php_load_env_var(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg TSRMLS_DC)
+{
+ zval *array_ptr = (zval*)arg;
+ int filter_arg = (array_ptr == PG(http_globals)[TRACK_VARS_ENV])?PARSE_ENV:PARSE_SERVER;
+ unsigned int new_val_len;
+
+ if (sapi_module.input_filter(filter_arg, var, &val, strlen(val), &new_val_len TSRMLS_CC)) {
+ php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC);
+ }
+}
+
+static void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC)
+{
+ if (PG(http_globals)[TRACK_VARS_ENV] &&
+ array_ptr != PG(http_globals)[TRACK_VARS_ENV] &&
+ Z_TYPE_P(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY &&
+ zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_ENV])) > 0
+ ) {
+ zval_dtor(array_ptr);
+ *array_ptr = *PG(http_globals)[TRACK_VARS_ENV];
+ INIT_PZVAL(array_ptr);
+ zval_copy_ctor(array_ptr);
+ return;
+ } else if (PG(http_globals)[TRACK_VARS_SERVER] &&
+ array_ptr != PG(http_globals)[TRACK_VARS_SERVER] &&
+ Z_TYPE_P(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY &&
+ zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER])) > 0
+ ) {
+ zval_dtor(array_ptr);
+ *array_ptr = *PG(http_globals)[TRACK_VARS_SERVER];
+ INIT_PZVAL(array_ptr);
+ zval_copy_ctor(array_ptr);
+ return;
+ }
+
+ /* call php's original import as a catch-all */
+ php_php_import_environment_variables(array_ptr TSRMLS_CC);
+
+ if (fcgi_is_fastcgi()) {
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+ fcgi_loadenv(request, cgi_php_load_env_var, array_ptr TSRMLS_CC);
+ }
+}
+
+static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ unsigned int php_self_len;
+ char *php_self;
+
+ /* In CGI mode, we consider the environment to be a part of the server
+ * variables
+ */
+ php_import_environment_variables(track_vars_array TSRMLS_CC);
+
+ if (CGIG(fix_pathinfo)) {
+ char *script_name = SG(request_info).request_uri;
+ char *path_info;
+ int free_php_self;
+ ALLOCA_FLAG(use_heap)
+
+ if (fcgi_is_fastcgi()) {
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+
+ path_info = FCGI_GETENV(request, "PATH_INFO");
+ } else {
+ path_info = getenv("PATH_INFO");
+ }
+
+ if (path_info) {
+ unsigned int path_info_len = strlen(path_info);
+
+ if (script_name) {
+ unsigned int script_name_len = strlen(script_name);
+
+ php_self_len = script_name_len + path_info_len;
+ php_self = do_alloca(php_self_len + 1, use_heap);
+ memcpy(php_self, script_name, script_name_len + 1);
+ memcpy(php_self + script_name_len, path_info, path_info_len + 1);
+ free_php_self = 1;
+ } else {
+ php_self = path_info;
+ php_self_len = path_info_len;
+ free_php_self = 0;
+ }
+ } else if (script_name) {
+ php_self = script_name;
+ php_self_len = strlen(script_name);
+ free_php_self = 0;
+ } else {
+ php_self = "";
+ php_self_len = 0;
+ free_php_self = 0;
+ }
+
+ /* Build the special-case PHP_SELF variable for the CGI version */
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) {
+ php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC);
+ }
+ if (free_php_self) {
+ free_alloca(php_self, use_heap);
+ }
+ } else {
+ php_self = SG(request_info).request_uri ? SG(request_info).request_uri : "";
+ php_self_len = strlen(php_self);
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) {
+ php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC);
+ }
+ }
+}
+
+static void sapi_cgi_log_message(char *message TSRMLS_DC)
+{
+ if (fcgi_is_fastcgi() && CGIG(fcgi_logging)) {
+ fcgi_request *request;
+
+ request = (fcgi_request*) SG(server_context);
+ if (request) {
+ int len = strlen(message);
+ char *buf = malloc(len+2);
+
+ memcpy(buf, message, len);
+ memcpy(buf + len, "\n", sizeof("\n"));
+ fcgi_write(request, FCGI_STDERR, buf, len+1);
+ free(buf);
+ } else {
+ fprintf(stderr, "%s\n", message);
+ }
+ /* ignore return code */
+ } else {
+ fprintf(stderr, "%s\n", message);
+ }
+}
+
+/* {{{ php_cgi_ini_activate_user_config
+ */
+static void php_cgi_ini_activate_user_config(char *path, int path_len, const char *doc_root, int doc_root_len, int start TSRMLS_DC)
+{
+ char *ptr;
+ user_config_cache_entry *new_entry, *entry;
+ time_t request_time = sapi_get_request_time(TSRMLS_C);
+
+ /* Find cached config entry: If not found, create one */
+ if (zend_hash_find(&CGIG(user_config_cache), path, path_len + 1, (void **) &entry) == FAILURE) {
+ new_entry = pemalloc(sizeof(user_config_cache_entry), 1);
+ new_entry->expires = 0;
+ new_entry->user_config = (HashTable *) pemalloc(sizeof(HashTable), 1);
+ zend_hash_init(new_entry->user_config, 0, NULL, (dtor_func_t) config_zval_dtor, 1);
+ zend_hash_update(&CGIG(user_config_cache), path, path_len + 1, new_entry, sizeof(user_config_cache_entry), (void **) &entry);
+ free(new_entry);
+ }
+
+ /* Check whether cache entry has expired and rescan if it is */
+ if (request_time > entry->expires) {
+ char *real_path = NULL;
+ int real_path_len;
+ char *s1, *s2;
+ int s_len;
+
+ /* Clear the expired config */
+ zend_hash_clean(entry->user_config);
+
+ if (!IS_ABSOLUTE_PATH(path, path_len)) {
+ real_path = tsrm_realpath(path, NULL TSRMLS_CC);
+ if (real_path == NULL) {
+ return;
+ }
+ real_path_len = strlen(real_path);
+ path = real_path;
+ path_len = real_path_len;
+ }
+
+ if (path_len > doc_root_len) {
+ s1 = (char *) doc_root;
+ s2 = path;
+ s_len = doc_root_len;
+ } else {
+ s1 = path;
+ s2 = (char *) doc_root;
+ s_len = path_len;
+ }
+
+ /* we have to test if path is part of DOCUMENT_ROOT.
+ if it is inside the docroot, we scan the tree up to the docroot
+ to find more user.ini, if not we only scan the current path.
+ */
+#ifdef PHP_WIN32
+ if (strnicmp(s1, s2, s_len) == 0) {
+#else
+ if (strncmp(s1, s2, s_len) == 0) {
+#endif
+ ptr = s2 + start; /* start is the point where doc_root ends! */
+ while ((ptr = strchr(ptr, DEFAULT_SLASH)) != NULL) {
+ *ptr = 0;
+ php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC);
+ *ptr = '/';
+ ptr++;
+ }
+ } else {
+ php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC);
+ }
+
+ if (real_path) {
+ free(real_path);
+ }
+ entry->expires = request_time + PG(user_ini_cache_ttl);
+ }
+
+ /* Activate ini entries with values from the user config hash */
+ php_ini_activate_config(entry->user_config, PHP_INI_PERDIR, PHP_INI_STAGE_HTACCESS TSRMLS_CC);
+}
+/* }}} */
+
+static int sapi_cgi_activate(TSRMLS_D)
+{
+ char *path, *doc_root, *server_name;
+ uint path_len, doc_root_len, server_name_len;
+
+ /* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */
+ if (!SG(request_info).path_translated) {
+ return FAILURE;
+ }
+
+ if (php_ini_has_per_host_config()) {
+ /* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */
+ if (fcgi_is_fastcgi()) {
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+
+ server_name = FCGI_GETENV(request, "SERVER_NAME");
+ } else {
+ server_name = getenv("SERVER_NAME");
+ }
+ /* SERVER_NAME should also be defined at this stage..but better check it anyway */
+ if (server_name) {
+ server_name_len = strlen(server_name);
+ server_name = estrndup(server_name, server_name_len);
+ zend_str_tolower(server_name, server_name_len);
+ php_ini_activate_per_host_config(server_name, server_name_len + 1 TSRMLS_CC);
+ efree(server_name);
+ }
+ }
+
+ if (php_ini_has_per_dir_config() ||
+ (PG(user_ini_filename) && *PG(user_ini_filename))
+ ) {
+ /* Prepare search path */
+ path_len = strlen(SG(request_info).path_translated);
+
+ /* Make sure we have trailing slash! */
+ if (!IS_SLASH(SG(request_info).path_translated[path_len])) {
+ path = emalloc(path_len + 2);
+ memcpy(path, SG(request_info).path_translated, path_len + 1);
+ path_len = zend_dirname(path, path_len);
+ path[path_len++] = DEFAULT_SLASH;
+ } else {
+ path = estrndup(SG(request_info).path_translated, path_len);
+ path_len = zend_dirname(path, path_len);
+ }
+ path[path_len] = 0;
+
+ /* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */
+ php_ini_activate_per_dir_config(path, path_len TSRMLS_CC); /* Note: for global settings sake we check from root to path */
+
+ /* Load and activate user ini files in path starting from DOCUMENT_ROOT */
+ if (PG(user_ini_filename) && *PG(user_ini_filename)) {
+ if (fcgi_is_fastcgi()) {
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+
+ doc_root = FCGI_GETENV(request, "DOCUMENT_ROOT");
+ } else {
+ doc_root = getenv("DOCUMENT_ROOT");
+ }
+
+ /* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */
+ if (doc_root) {
+ doc_root_len = strlen(doc_root);
+ if (doc_root_len > 0 && IS_SLASH(doc_root[doc_root_len - 1])) {
+ --doc_root_len;
+ }
+#ifdef PHP_WIN32
+ /* paths on windows should be case-insensitive */
+ doc_root = estrndup(doc_root, doc_root_len);
+ zend_str_tolower(doc_root, doc_root_len);
+#endif
+ php_cgi_ini_activate_user_config(path, path_len, doc_root, doc_root_len, doc_root_len - 1 TSRMLS_CC);
+
+#ifdef PHP_WIN32
+ efree(doc_root);
+#endif
+ }
+ }
+
+ efree(path);
+ }
+
+ return SUCCESS;
+}
+
+static int sapi_cgi_deactivate(TSRMLS_D)
+{
+ /* flush only when SAPI was started. The reasons are:
+ 1. SAPI Deactivate is called from two places: module init and request shutdown
+ 2. When the first call occurs and the request is not set up, flush fails on FastCGI.
+ */
+ if (SG(sapi_started)) {
+ if (fcgi_is_fastcgi()) {
+ if (
+#ifndef PHP_WIN32
+ !parent &&
+#endif
+ !fcgi_finish_request((fcgi_request*)SG(server_context), 0)) {
+ php_handle_aborted_connection();
+ }
+ } else {
+ sapi_cgi_flush(SG(server_context));
+ }
+ }
+ return SUCCESS;
+}
+
+static int php_cgi_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+/* {{{ sapi_module_struct cgi_sapi_module
+ */
+static sapi_module_struct cgi_sapi_module = {
+ "cgi-fcgi", /* name */
+ "CGI/FastCGI", /* pretty name */
+
+ php_cgi_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ sapi_cgi_activate, /* activate */
+ sapi_cgi_deactivate, /* deactivate */
+
+ sapi_cgi_ub_write, /* unbuffered write */
+ sapi_cgi_flush, /* flush */
+ NULL, /* get uid */
+ sapi_cgi_getenv, /* getenv */
+
+ php_error, /* error handler */
+
+ NULL, /* header handler */
+ sapi_cgi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ sapi_cgi_read_post, /* read POST data */
+ sapi_cgi_read_cookies, /* read Cookies */
+
+ sapi_cgi_register_variables, /* register server variables */
+ sapi_cgi_log_message, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+/* }}} */
+
+/* {{{ arginfo ext/standard/dl.c */
+ZEND_BEGIN_ARG_INFO(arginfo_dl, 0)
+ ZEND_ARG_INFO(0, extension_filename)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+static const zend_function_entry additional_functions[] = {
+ ZEND_FE(dl, arginfo_dl)
+ {NULL, NULL, NULL}
+};
+
+/* {{{ php_cgi_usage
+ */
+static void php_cgi_usage(char *argv0)
+{
+ char *prog;
+
+ prog = strrchr(argv0, '/');
+ if (prog) {
+ prog++;
+ } else {
+ prog = "php";
+ }
+
+ php_printf( "Usage: %s [-q] [-h] [-s] [-v] [-i] [-f <file>]\n"
+ " %s <file> [args...]\n"
+ " -a Run interactively\n"
+ " -b <address:port>|<port> Bind Path for external FASTCGI Server mode\n"
+ " -C Do not chdir to the script's directory\n"
+ " -c <path>|<file> Look for php.ini file in this directory\n"
+ " -n No php.ini file will be used\n"
+ " -d foo[=bar] Define INI entry foo with value 'bar'\n"
+ " -e Generate extended information for debugger/profiler\n"
+ " -f <file> Parse <file>. Implies `-q'\n"
+ " -h This help\n"
+ " -i PHP information\n"
+ " -l Syntax check only (lint)\n"
+ " -m Show compiled in modules\n"
+ " -q Quiet-mode. Suppress HTTP Header output.\n"
+ " -s Display colour syntax highlighted source.\n"
+ " -v Version number\n"
+ " -w Display source with stripped comments and whitespace.\n"
+ " -z <file> Load Zend extension <file>.\n"
+ " -T <count> Measure execution time of script repeated <count> times.\n",
+ prog, prog);
+}
+/* }}} */
+
+/* {{{ is_valid_path
+ *
+ * some server configurations allow '..' to slip through in the
+ * translated path. We'll just refuse to handle such a path.
+ */
+static int is_valid_path(const char *path)
+{
+ const char *p = path;
+
+ if (UNEXPECTED(!p)) {
+ return 0;
+ }
+ if (UNEXPECTED(*p == '.') && *(p+1) == '.' && (!*(p+2) || IS_SLASH(*(p+2)))) {
+ return 0;
+ }
+ while (*p) {
+ if (IS_SLASH(*p)) {
+ p++;
+ if (UNEXPECTED(*p == '.')) {
+ p++;
+ if (UNEXPECTED(*p == '.')) {
+ p++;
+ if (UNEXPECTED(!*p) || UNEXPECTED(IS_SLASH(*p))) {
+ return 0;
+ }
+ }
+ }
+ }
+ p++;
+ }
+ return 1;
+}
+/* }}} */
+
+#define CGI_GETENV(name) \
+ ((request) ? \
+ FCGI_GETENV(request, name) : \
+ getenv(name))
+
+#define CGI_PUTENV(name, value) \
+ ((request) ? \
+ FCGI_PUTENV(request, name, value) : \
+ _sapi_cgi_putenv(name, sizeof(name)-1, value))
+
+/* {{{ init_request_info
+
+ initializes request_info structure
+
+ specificly in this section we handle proper translations
+ for:
+
+ PATH_INFO
+ derived from the portion of the URI path following
+ the script name but preceding any query data
+ may be empty
+
+ PATH_TRANSLATED
+ derived by taking any path-info component of the
+ request URI and performing any virtual-to-physical
+ translation appropriate to map it onto the server's
+ document repository structure
+
+ empty if PATH_INFO is empty
+
+ The env var PATH_TRANSLATED **IS DIFFERENT** than the
+ request_info.path_translated variable, the latter should
+ match SCRIPT_FILENAME instead.
+
+ SCRIPT_NAME
+ set to a URL path that could identify the CGI script
+ rather than the interpreter. PHP_SELF is set to this
+
+ REQUEST_URI
+ uri section following the domain:port part of a URI
+
+ SCRIPT_FILENAME
+ The virtual-to-physical translation of SCRIPT_NAME (as per
+ PATH_TRANSLATED)
+
+ These settings are documented at
+ http://cgi-spec.golux.com/
+
+
+ Based on the following URL request:
+
+ http://localhost/info.php/test?a=b
+
+ should produce, which btw is the same as if
+ we were running under mod_cgi on apache (ie. not
+ using ScriptAlias directives):
+
+ PATH_INFO=/test
+ PATH_TRANSLATED=/docroot/test
+ SCRIPT_NAME=/info.php
+ REQUEST_URI=/info.php/test?a=b
+ SCRIPT_FILENAME=/docroot/info.php
+ QUERY_STRING=a=b
+
+ but what we get is (cgi/mod_fastcgi under apache):
+
+ PATH_INFO=/info.php/test
+ PATH_TRANSLATED=/docroot/info.php/test
+ SCRIPT_NAME=/php/php-cgi (from the Action setting I suppose)
+ REQUEST_URI=/info.php/test?a=b
+ SCRIPT_FILENAME=/path/to/php/bin/php-cgi (Action setting translated)
+ QUERY_STRING=a=b
+
+ Comments in the code below refer to using the above URL in a request
+
+ */
+static void init_request_info(fcgi_request *request TSRMLS_DC)
+{
+ char *env_script_filename = CGI_GETENV("SCRIPT_FILENAME");
+ char *env_path_translated = CGI_GETENV("PATH_TRANSLATED");
+ char *script_path_translated = env_script_filename;
+
+ /* some broken servers do not have script_filename or argv0
+ * an example, IIS configured in some ways. then they do more
+ * broken stuff and set path_translated to the cgi script location */
+ if (!script_path_translated && env_path_translated) {
+ script_path_translated = env_path_translated;
+ }
+
+ /* initialize the defaults */
+ SG(request_info).path_translated = NULL;
+ SG(request_info).request_method = NULL;
+ SG(request_info).proto_num = 1000;
+ SG(request_info).query_string = NULL;
+ SG(request_info).request_uri = NULL;
+ SG(request_info).content_type = NULL;
+ SG(request_info).content_length = 0;
+ SG(sapi_headers).http_response_code = 200;
+
+ /* script_path_translated being set is a good indication that
+ * we are running in a cgi environment, since it is always
+ * null otherwise. otherwise, the filename
+ * of the script will be retreived later via argc/argv */
+ if (script_path_translated) {
+ const char *auth;
+ char *content_length = CGI_GETENV("CONTENT_LENGTH");
+ char *content_type = CGI_GETENV("CONTENT_TYPE");
+ char *env_path_info = CGI_GETENV("PATH_INFO");
+ char *env_script_name = CGI_GETENV("SCRIPT_NAME");
+
+#ifdef PHP_WIN32
+ /* Hack for buggy IIS that sets incorrect PATH_INFO */
+ char *env_server_software = CGI_GETENV("SERVER_SOFTWARE");
+
+ if (env_server_software &&
+ env_script_name &&
+ env_path_info &&
+ strncmp(env_server_software, "Microsoft-IIS", sizeof("Microsoft-IIS")-1) == 0 &&
+ strncmp(env_path_info, env_script_name, strlen(env_script_name)) == 0
+ ) {
+ env_path_info = CGI_PUTENV("ORIG_PATH_INFO", env_path_info);
+ env_path_info += strlen(env_script_name);
+ if (*env_path_info == 0) {
+ env_path_info = NULL;
+ }
+ env_path_info = CGI_PUTENV("PATH_INFO", env_path_info);
+ }
+#endif
+
+ if (CGIG(fix_pathinfo)) {
+ struct stat st;
+ char *real_path = NULL;
+ char *env_redirect_url = CGI_GETENV("REDIRECT_URL");
+ char *env_document_root = CGI_GETENV("DOCUMENT_ROOT");
+ char *orig_path_translated = env_path_translated;
+ char *orig_path_info = env_path_info;
+ char *orig_script_name = env_script_name;
+ char *orig_script_filename = env_script_filename;
+ int script_path_translated_len;
+
+ if (!env_document_root && PG(doc_root)) {
+ env_document_root = CGI_PUTENV("DOCUMENT_ROOT", PG(doc_root));
+ /* fix docroot */
+ TRANSLATE_SLASHES(env_document_root);
+ }
+
+ if (env_path_translated != NULL && env_redirect_url != NULL &&
+ env_path_translated != script_path_translated &&
+ strcmp(env_path_translated, script_path_translated) != 0) {
+ /*
+ * pretty much apache specific. If we have a redirect_url
+ * then our script_filename and script_name point to the
+ * php executable
+ */
+ script_path_translated = env_path_translated;
+ /* we correct SCRIPT_NAME now in case we don't have PATH_INFO */
+ env_script_name = env_redirect_url;
+ }
+
+#ifdef __riscos__
+ /* Convert path to unix format*/
+ __riscosify_control |= __RISCOSIFY_DONT_CHECK_DIR;
+ script_path_translated = __unixify(script_path_translated, 0, NULL, 1, 0);
+#endif
+
+ /*
+ * if the file doesn't exist, try to extract PATH_INFO out
+ * of it by stat'ing back through the '/'
+ * this fixes url's like /info.php/test
+ */
+ if (script_path_translated &&
+ (script_path_translated_len = strlen(script_path_translated)) > 0 &&
+ (script_path_translated[script_path_translated_len-1] == '/' ||
+#ifdef PHP_WIN32
+ script_path_translated[script_path_translated_len-1] == '\\' ||
+#endif
+ (real_path = tsrm_realpath(script_path_translated, NULL TSRMLS_CC)) == NULL)
+ ) {
+ char *pt = estrndup(script_path_translated, script_path_translated_len);
+ int len = script_path_translated_len;
+ char *ptr;
+
+ while ((ptr = strrchr(pt, '/')) || (ptr = strrchr(pt, '\\'))) {
+ *ptr = 0;
+ if (stat(pt, &st) == 0 && S_ISREG(st.st_mode)) {
+ /*
+ * okay, we found the base script!
+ * work out how many chars we had to strip off;
+ * then we can modify PATH_INFO
+ * accordingly
+ *
+ * we now have the makings of
+ * PATH_INFO=/test
+ * SCRIPT_FILENAME=/docroot/info.php
+ *
+ * we now need to figure out what docroot is.
+ * if DOCUMENT_ROOT is set, this is easy, otherwise,
+ * we have to play the game of hide and seek to figure
+ * out what SCRIPT_NAME should be
+ */
+ int slen = len - strlen(pt);
+ int pilen = env_path_info ? strlen(env_path_info) : 0;
+ char *path_info = env_path_info ? env_path_info + pilen - slen : NULL;
+
+ if (orig_path_info != path_info) {
+ if (orig_path_info) {
+ char old;
+
+ CGI_PUTENV("ORIG_PATH_INFO", orig_path_info);
+ old = path_info[0];
+ path_info[0] = 0;
+ if (!orig_script_name ||
+ strcmp(orig_script_name, env_path_info) != 0) {
+ if (orig_script_name) {
+ CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name);
+ }
+ SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_path_info);
+ } else {
+ SG(request_info).request_uri = orig_script_name;
+ }
+ path_info[0] = old;
+ }
+ env_path_info = CGI_PUTENV("PATH_INFO", path_info);
+ }
+ if (!orig_script_filename ||
+ strcmp(orig_script_filename, pt) != 0) {
+ if (orig_script_filename) {
+ CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename);
+ }
+ script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", pt);
+ }
+ TRANSLATE_SLASHES(pt);
+
+ /* figure out docroot
+ * SCRIPT_FILENAME minus SCRIPT_NAME
+ */
+ if (env_document_root) {
+ int l = strlen(env_document_root);
+ int path_translated_len = 0;
+ char *path_translated = NULL;
+
+ if (l && env_document_root[l - 1] == '/') {
+ --l;
+ }
+
+ /* we have docroot, so we should have:
+ * DOCUMENT_ROOT=/docroot
+ * SCRIPT_FILENAME=/docroot/info.php
+ */
+
+ /* PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO */
+ path_translated_len = l + (env_path_info ? strlen(env_path_info) : 0);
+ path_translated = (char *) emalloc(path_translated_len + 1);
+ memcpy(path_translated, env_document_root, l);
+ if (env_path_info) {
+ memcpy(path_translated + l, env_path_info, (path_translated_len - l));
+ }
+ path_translated[path_translated_len] = '\0';
+ if (orig_path_translated) {
+ CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated);
+ }
+ env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated);
+ efree(path_translated);
+ } else if ( env_script_name &&
+ strstr(pt, env_script_name)
+ ) {
+ /* PATH_TRANSLATED = PATH_TRANSLATED - SCRIPT_NAME + PATH_INFO */
+ int ptlen = strlen(pt) - strlen(env_script_name);
+ int path_translated_len = ptlen + (env_path_info ? strlen(env_path_info) : 0);
+ char *path_translated = NULL;
+
+ path_translated = (char *) emalloc(path_translated_len + 1);
+ memcpy(path_translated, pt, ptlen);
+ if (env_path_info) {
+ memcpy(path_translated + ptlen, env_path_info, path_translated_len - ptlen);
+ }
+ path_translated[path_translated_len] = '\0';
+ if (orig_path_translated) {
+ CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated);
+ }
+ env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated);
+ efree(path_translated);
+ }
+ break;
+ }
+ }
+ if (!ptr) {
+ /*
+ * if we stripped out all the '/' and still didn't find
+ * a valid path... we will fail, badly. of course we would
+ * have failed anyway... we output 'no input file' now.
+ */
+ if (orig_script_filename) {
+ CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename);
+ }
+ script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", NULL);
+ SG(sapi_headers).http_response_code = 404;
+ }
+ if (!SG(request_info).request_uri) {
+ if (!orig_script_name ||
+ strcmp(orig_script_name, env_script_name) != 0) {
+ if (orig_script_name) {
+ CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name);
+ }
+ SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name);
+ } else {
+ SG(request_info).request_uri = orig_script_name;
+ }
+ }
+ if (pt) {
+ efree(pt);
+ }
+ } else {
+ /* make sure path_info/translated are empty */
+ if (!orig_script_filename ||
+ (script_path_translated != orig_script_filename &&
+ strcmp(script_path_translated, orig_script_filename) != 0)) {
+ if (orig_script_filename) {
+ CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename);
+ }
+ script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", script_path_translated);
+ }
+ if (env_redirect_url) {
+ if (orig_path_info) {
+ CGI_PUTENV("ORIG_PATH_INFO", orig_path_info);
+ CGI_PUTENV("PATH_INFO", NULL);
+ }
+ if (orig_path_translated) {
+ CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated);
+ CGI_PUTENV("PATH_TRANSLATED", NULL);
+ }
+ }
+ if (env_script_name != orig_script_name) {
+ if (orig_script_name) {
+ CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name);
+ }
+ SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name);
+ } else {
+ SG(request_info).request_uri = env_script_name;
+ }
+ free(real_path);
+ }
+ } else {
+ /* pre 4.3 behaviour, shouldn't be used but provides BC */
+ if (env_path_info) {
+ SG(request_info).request_uri = env_path_info;
+ } else {
+ SG(request_info).request_uri = env_script_name;
+ }
+ if (!CGIG(discard_path) && env_path_translated) {
+ script_path_translated = env_path_translated;
+ }
+ }
+
+ if (is_valid_path(script_path_translated)) {
+ SG(request_info).path_translated = estrdup(script_path_translated);
+ }
+
+ SG(request_info).request_method = CGI_GETENV("REQUEST_METHOD");
+ /* FIXME - Work out proto_num here */
+ SG(request_info).query_string = CGI_GETENV("QUERY_STRING");
+ SG(request_info).content_type = (content_type ? content_type : "" );
+ SG(request_info).content_length = (content_length ? atol(content_length) : 0);
+
+ /* The CGI RFC allows servers to pass on unvalidated Authorization data */
+ auth = CGI_GETENV("HTTP_AUTHORIZATION");
+ php_handle_auth_data(auth TSRMLS_CC);
+ }
+}
+/* }}} */
+
+#ifndef PHP_WIN32
+/**
+ * Clean up child processes upon exit
+ */
+void fastcgi_cleanup(int signal)
+{
+#ifdef DEBUG_FASTCGI
+ fprintf(stderr, "FastCGI shutdown, pid %d\n", getpid());
+#endif
+
+ sigaction(SIGTERM, &old_term, 0);
+
+ /* Kill all the processes in our process group */
+ kill(-pgroup, SIGTERM);
+
+ if (parent && parent_waiting) {
+ exit_signal = 1;
+ } else {
+ exit(0);
+ }
+}
+#endif
+
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY("cgi.rfc2616_headers", "0", PHP_INI_ALL, OnUpdateBool, rfc2616_headers, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.nph", "0", PHP_INI_ALL, OnUpdateBool, nph, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.check_shebang_line", "1", PHP_INI_SYSTEM, OnUpdateBool, check_shebang_line, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.force_redirect", "1", PHP_INI_SYSTEM, OnUpdateBool, force_redirect, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.redirect_status_env", NULL, PHP_INI_SYSTEM, OnUpdateString, redirect_status_env, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.fix_pathinfo", "1", PHP_INI_SYSTEM, OnUpdateBool, fix_pathinfo, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.discard_path", "0", PHP_INI_SYSTEM, OnUpdateBool, discard_path, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("fastcgi.logging", "1", PHP_INI_SYSTEM, OnUpdateBool, fcgi_logging, php_cgi_globals_struct, php_cgi_globals)
+#ifdef PHP_WIN32
+ STD_PHP_INI_ENTRY("fastcgi.impersonate", "0", PHP_INI_SYSTEM, OnUpdateBool, impersonate, php_cgi_globals_struct, php_cgi_globals)
+#endif
+PHP_INI_END()
+
+/* {{{ php_cgi_globals_ctor
+ */
+static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals TSRMLS_DC)
+{
+ php_cgi_globals->rfc2616_headers = 0;
+ php_cgi_globals->nph = 0;
+ php_cgi_globals->check_shebang_line = 1;
+ php_cgi_globals->force_redirect = 1;
+ php_cgi_globals->redirect_status_env = NULL;
+ php_cgi_globals->fix_pathinfo = 1;
+ php_cgi_globals->discard_path = 0;
+ php_cgi_globals->fcgi_logging = 1;
+#ifdef PHP_WIN32
+ php_cgi_globals->impersonate = 0;
+#endif
+ zend_hash_init(&php_cgi_globals->user_config_cache, 0, NULL, (dtor_func_t) user_config_cache_entry_dtor, 1);
+}
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+static PHP_MINIT_FUNCTION(cgi)
+{
+#ifdef ZTS
+ ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL);
+#else
+ php_cgi_globals_ctor(&php_cgi_globals TSRMLS_CC);
+#endif
+ REGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+static PHP_MSHUTDOWN_FUNCTION(cgi)
+{
+ zend_hash_destroy(&CGIG(user_config_cache));
+
+ UNREGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+static PHP_MINFO_FUNCTION(cgi)
+{
+ DISPLAY_INI_ENTRIES();
+}
+/* }}} */
+
+PHP_FUNCTION(apache_child_terminate) /* {{{ */
+{
+ if (ZEND_NUM_ARGS() > 0) {
+ WRONG_PARAM_COUNT;
+ }
+ if (fcgi_is_fastcgi()) {
+ fcgi_terminate();
+ }
+}
+/* }}} */
+
+static void add_request_header(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg TSRMLS_DC) /* {{{ */
+{
+ zval *return_value = (zval*)arg;
+ char *str = NULL;
+ char *p;
+ ALLOCA_FLAG(use_heap)
+
+ if (var_len > 5 &&
+ var[0] == 'H' &&
+ var[1] == 'T' &&
+ var[2] == 'T' &&
+ var[3] == 'P' &&
+ var[4] == '_') {
+
+ var_len -= 5;
+ p = var + 5;
+ var = str = do_alloca(var_len + 1, use_heap);
+ *str++ = *p++;
+ while (*p) {
+ if (*p == '_') {
+ *str++ = '-';
+ p++;
+ if (*p) {
+ *str++ = *p++;
+ }
+ } else if (*p >= 'A' && *p <= 'Z') {
+ *str++ = (*p++ - 'A' + 'a');
+ } else {
+ *str++ = *p++;
+ }
+ }
+ *str = 0;
+ } else if (var_len == sizeof("CONTENT_TYPE")-1 &&
+ memcmp(var, "CONTENT_TYPE", sizeof("CONTENT_TYPE")-1) == 0) {
+ var = "Content-Type";
+ } else if (var_len == sizeof("CONTENT_LENGTH")-1 &&
+ memcmp(var, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1) == 0) {
+ var = "Content-Length";
+ } else {
+ return;
+ }
+ add_assoc_stringl_ex(return_value, var, var_len+1, val, val_len, 1);
+ if (str) {
+ free_alloca(var, use_heap);
+ }
+}
+/* }}} */
+
+PHP_FUNCTION(apache_request_headers) /* {{{ */
+{
+ if (ZEND_NUM_ARGS() > 0) {
+ WRONG_PARAM_COUNT;
+ }
+ array_init(return_value);
+ if (fcgi_is_fastcgi()) {
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+
+ fcgi_loadenv(request, add_request_header, return_value TSRMLS_CC);
+ } else {
+ char buf[128];
+ char **env, *p, *q, *var, *val, *t = buf;
+ size_t alloc_size = sizeof(buf);
+ unsigned long var_len;
+
+ for (env = environ; env != NULL && *env != NULL; env++) {
+ val = strchr(*env, '=');
+ if (!val) { /* malformed entry? */
+ continue;
+ }
+ var_len = val - *env;
+ if (var_len >= alloc_size) {
+ alloc_size = var_len + 64;
+ t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size));
+ }
+ var = *env;
+ if (var_len > 5 &&
+ var[0] == 'H' &&
+ var[1] == 'T' &&
+ var[2] == 'T' &&
+ var[3] == 'P' &&
+ var[4] == '_') {
+
+ var_len -= 5;
+
+ if (var_len >= alloc_size) {
+ alloc_size = var_len + 64;
+ t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size));
+ }
+ p = var + 5;
+
+ var = q = t;
+ /* First char keep uppercase */
+ *q++ = *p++;
+ while (*p) {
+ if (*p == '=') {
+ /* End of name */
+ break;
+ } else if (*p == '_') {
+ *q++ = '-';
+ p++;
+ /* First char after - keep uppercase */
+ if (*p && *p!='=') {
+ *q++ = *p++;
+ }
+ } else if (*p >= 'A' && *p <= 'Z') {
+ /* lowercase */
+ *q++ = (*p++ - 'A' + 'a');
+ } else {
+ *q++ = *p++;
+ }
+ }
+ *q = 0;
+ } else if (var_len == sizeof("CONTENT_TYPE")-1 &&
+ memcmp(var, "CONTENT_TYPE", sizeof("CONTENT_TYPE")-1) == 0) {
+ var = "Content-Type";
+ } else if (var_len == sizeof("CONTENT_LENGTH")-1 &&
+ memcmp(var, "CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1) == 0) {
+ var = "Content-Length";
+ } else {
+ continue;
+ }
+ val++;
+ add_assoc_string_ex(return_value, var, var_len+1, val, 1);
+ }
+ if (t != buf && t != NULL) {
+ efree(t);
+ }
+ }
+}
+/* }}} */
+
+static void add_response_header(sapi_header_struct *h, zval *return_value TSRMLS_DC) /* {{{ */
+{
+ char *s, *p;
+ int len;
+ ALLOCA_FLAG(use_heap)
+
+ if (h->header_len > 0) {
+ p = strchr(h->header, ':');
+ len = p - h->header;
+ if (p && (len > 0)) {
+ while (len > 0 && (h->header[len-1] == ' ' || h->header[len-1] == '\t')) {
+ len--;
+ }
+ if (len) {
+ s = do_alloca(len + 1, use_heap);
+ memcpy(s, h->header, len);
+ s[len] = 0;
+ do {
+ p++;
+ } while (*p == ' ' || *p == '\t');
+ add_assoc_stringl_ex(return_value, s, len+1, p, h->header_len - (p - h->header), 1);
+ free_alloca(s, use_heap);
+ }
+ }
+ }
+}
+/* }}} */
+
+PHP_FUNCTION(apache_response_headers) /* {{{ */
+{
+ if (ZEND_NUM_ARGS() > 0) {
+ WRONG_PARAM_COUNT;
+ }
+
+ if (!&SG(sapi_headers).headers) {
+ RETURN_FALSE;
+ }
+ array_init(return_value);
+ zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t)add_response_header, return_value TSRMLS_CC);
+}
+/* }}} */
+
+ZEND_BEGIN_ARG_INFO(arginfo_no_args, 0)
+ZEND_END_ARG_INFO()
+
+const zend_function_entry cgi_functions[] = {
+ PHP_FE(apache_child_terminate, arginfo_no_args)
+ PHP_FE(apache_request_headers, arginfo_no_args)
+ PHP_FE(apache_response_headers, arginfo_no_args)
+ PHP_FALIAS(getallheaders, apache_request_headers, arginfo_no_args)
+ {NULL, NULL, NULL}
+};
+
+static zend_module_entry cgi_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "cgi-fcgi",
+ cgi_functions,
+ PHP_MINIT(cgi),
+ PHP_MSHUTDOWN(cgi),
+ NULL,
+ NULL,
+ PHP_MINFO(cgi),
+ NO_VERSION_YET,
+ STANDARD_MODULE_PROPERTIES
+};
+
+/* {{{ main
+ */
+int main(int argc, char *argv[])
+{
+ int free_query_string = 0;
+ int exit_status = SUCCESS;
+ int cgi = 0, c, i, len;
+ zend_file_handle file_handle;
+ char *s;
+
+ /* temporary locals */
+ int behavior = PHP_MODE_STANDARD;
+ int no_headers = 0;
+ int orig_optind = php_optind;
+ char *orig_optarg = php_optarg;
+ char *script_file = NULL;
+ int ini_entries_len = 0;
+ /* end of temporary locals */
+
+#ifdef ZTS
+ void ***tsrm_ls;
+#endif
+
+ int max_requests = 500;
+ int requests = 0;
+ int fastcgi;
+ char *bindpath = NULL;
+ int fcgi_fd = 0;
+ fcgi_request *request = NULL;
+ int repeats = 1;
+ int benchmark = 0;
+#if HAVE_GETTIMEOFDAY
+ struct timeval start, end;
+#else
+ time_t start, end;
+#endif
+#ifndef PHP_WIN32
+ int status = 0;
+#endif
+ char *query_string;
+ char *decoded_query_string;
+ int skip_getopt = 0;
+
+#if 0 && defined(PHP_DEBUG)
+ /* IIS is always making things more difficult. This allows
+ * us to stop PHP and attach a debugger before much gets started */
+ {
+ char szMessage [256];
+ wsprintf (szMessage, "Please attach a debugger to the process 0x%X [%d] (%s) and click OK", GetCurrentProcessId(), GetCurrentProcessId(), argv[0]);
+ MessageBox(NULL, szMessage, "CGI Debug Time!", MB_OK|MB_SERVICE_NOTIFICATION);
+ }
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#if defined(SIGPIPE) && defined(SIG_IGN)
+ signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
+ that sockets created via fsockopen()
+ don't kill PHP if the remote site
+ closes it. in apache|apxs mode apache
+ does that for us! thies@thieso.net
+ 20000419 */
+#endif
+#endif
+
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+ tsrm_ls = ts_resource(0);
+#endif
+
+ sapi_startup(&cgi_sapi_module);
+ fastcgi = fcgi_is_fastcgi();
+ cgi_sapi_module.php_ini_path_override = NULL;
+
+#ifdef PHP_WIN32
+ _fmode = _O_BINARY; /* sets default for file streams to binary */
+ setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
+#endif
+
+ if (!fastcgi) {
+ /* Make sure we detect we are a cgi - a bit redundancy here,
+ * but the default case is that we have to check only the first one. */
+ if (getenv("SERVER_SOFTWARE") ||
+ getenv("SERVER_NAME") ||
+ getenv("GATEWAY_INTERFACE") ||
+ getenv("REQUEST_METHOD")
+ ) {
+ cgi = 1;
+ }
+ }
+
+ if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) {
+ /* we've got query string that has no = - apache CGI will pass it to command line */
+ unsigned char *p;
+ decoded_query_string = strdup(query_string);
+ php_url_decode(decoded_query_string, strlen(decoded_query_string));
+ for (p = decoded_query_string; *p && *p <= ' '; p++) {
+ /* skip all leading spaces */
+ }
+ if(*p == '-') {
+ skip_getopt = 1;
+ }
+ free(decoded_query_string);
+ }
+
+ while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
+ switch (c) {
+ case 'c':
+ if (cgi_sapi_module.php_ini_path_override) {
+ free(cgi_sapi_module.php_ini_path_override);
+ }
+ cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
+ break;
+ case 'n':
+ cgi_sapi_module.php_ini_ignore = 1;
+ break;
+ case 'd': {
+ /* define ini entries on command line */
+ int len = strlen(php_optarg);
+ char *val;
+
+ if ((val = strchr(php_optarg, '='))) {
+ val++;
+ if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
+ cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
+ ini_entries_len += (val - php_optarg);
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"", 1);
+ ini_entries_len++;
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, val, len - (val - php_optarg));
+ ini_entries_len += len - (val - php_optarg);
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
+ ini_entries_len += sizeof("\n\0\"") - 2;
+ } else {
+ cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\n\0"));
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
+ ini_entries_len += len + sizeof("\n\0") - 2;
+ }
+ } else {
+ cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
+ ini_entries_len += len + sizeof("=1\n\0") - 2;
+ }
+ break;
+ }
+ /* if we're started on command line, check to see if
+ * we are being started as an 'external' fastcgi
+ * server by accepting a bindpath parameter. */
+ case 'b':
+ if (!fastcgi) {
+ bindpath = strdup(php_optarg);
+ }
+ break;
+ case 's': /* generate highlighted HTML from source */
+ behavior = PHP_MODE_HIGHLIGHT;
+ break;
+ }
+ }
+ php_optind = orig_optind;
+ php_optarg = orig_optarg;
+
+ if (fastcgi || bindpath) {
+ /* Override SAPI callbacks */
+ cgi_sapi_module.ub_write = sapi_fcgi_ub_write;
+ cgi_sapi_module.flush = sapi_fcgi_flush;
+ cgi_sapi_module.read_post = sapi_fcgi_read_post;
+ cgi_sapi_module.getenv = sapi_fcgi_getenv;
+ cgi_sapi_module.read_cookies = sapi_fcgi_read_cookies;
+ }
+
+#ifdef ZTS
+ SG(request_info).path_translated = NULL;
+#endif
+
+ cgi_sapi_module.executable_location = argv[0];
+ if (!cgi && !fastcgi && !bindpath) {
+ cgi_sapi_module.additional_functions = additional_functions;
+ }
+
+ /* startup after we get the above ini override se we get things right */
+ if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ return FAILURE;
+ }
+
+ /* check force_cgi after startup, so we have proper output */
+ if (cgi && CGIG(force_redirect)) {
+ /* Apache will generate REDIRECT_STATUS,
+ * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS.
+ * redirect.so and installation instructions available from
+ * http://www.koehntopp.de/php.
+ * -- kk@netuse.de
+ */
+ if (!getenv("REDIRECT_STATUS") &&
+ !getenv ("HTTP_REDIRECT_STATUS") &&
+ /* this is to allow a different env var to be configured
+ * in case some server does something different than above */
+ (!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env)))
+ ) {
+ zend_try {
+ SG(sapi_headers).http_response_code = 400;
+ PUTS("<b>Security Alert!</b> The PHP CGI cannot be accessed directly.\n\n\
+<p>This PHP CGI binary was compiled with force-cgi-redirect enabled. This\n\
+means that a page will only be served up if the REDIRECT_STATUS CGI variable is\n\
+set, e.g. via an Apache Action directive.</p>\n\
+<p>For more information as to <i>why</i> this behaviour exists, see the <a href=\"http://php.net/security.cgi-bin\">\
+manual page for CGI security</a>.</p>\n\
+<p>For more information about changing this behaviour or re-enabling this webserver,\n\
+consult the installation file that came with this distribution, or visit \n\
+<a href=\"http://php.net/install.windows\">the manual page</a>.</p>\n");
+ } zend_catch {
+ } zend_end_try();
+#if defined(ZTS) && !defined(PHP_DEBUG)
+ /* XXX we're crashing here in msvc6 debug builds at
+ * php_message_handler_for_zend:839 because
+ * SG(request_info).path_translated is an invalid pointer.
+ * It still happens even though I set it to null, so something
+ * weird is going on.
+ */
+ tsrm_shutdown();
+#endif
+ return FAILURE;
+ }
+ }
+
+ if (bindpath) {
+ fcgi_fd = fcgi_listen(bindpath, 128);
+ if (fcgi_fd < 0) {
+ fprintf(stderr, "Couldn't create FastCGI listen socket on port %s\n", bindpath);
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ return FAILURE;
+ }
+ fastcgi = fcgi_is_fastcgi();
+ }
+ if (fastcgi) {
+ /* How many times to run PHP scripts before dying */
+ if (getenv("PHP_FCGI_MAX_REQUESTS")) {
+ max_requests = atoi(getenv("PHP_FCGI_MAX_REQUESTS"));
+ if (max_requests < 0) {
+ fprintf(stderr, "PHP_FCGI_MAX_REQUESTS is not valid\n");
+ return FAILURE;
+ }
+ }
+
+ /* make php call us to get _ENV vars */
+ php_php_import_environment_variables = php_import_environment_variables;
+ php_import_environment_variables = cgi_php_import_environment_variables;
+
+ /* library is already initialized, now init our request */
+ request = fcgi_init_request(fcgi_fd);
+
+#ifndef PHP_WIN32
+ /* Pre-fork, if required */
+ if (getenv("PHP_FCGI_CHILDREN")) {
+ char * children_str = getenv("PHP_FCGI_CHILDREN");
+ children = atoi(children_str);
+ if (children < 0) {
+ fprintf(stderr, "PHP_FCGI_CHILDREN is not valid\n");
+ return FAILURE;
+ }
+ fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, children_str, strlen(children_str));
+ /* This is the number of concurrent requests, equals FCGI_MAX_CONNS */
+ fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, children_str, strlen(children_str));
+ } else {
+ fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, "1", sizeof("1")-1);
+ fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, "1", sizeof("1")-1);
+ }
+
+ if (children) {
+ int running = 0;
+ pid_t pid;
+
+ /* Create a process group for ourself & children */
+ setsid();
+ pgroup = getpgrp();
+#ifdef DEBUG_FASTCGI
+ fprintf(stderr, "Process group %d\n", pgroup);
+#endif
+
+ /* Set up handler to kill children upon exit */
+ act.sa_flags = 0;
+ act.sa_handler = fastcgi_cleanup;
+ if (sigaction(SIGTERM, &act, &old_term) ||
+ sigaction(SIGINT, &act, &old_int) ||
+ sigaction(SIGQUIT, &act, &old_quit)
+ ) {
+ perror("Can't set signals");
+ exit(1);
+ }
+
+ if (fcgi_in_shutdown()) {
+ goto parent_out;
+ }
+
+ while (parent) {
+ do {
+#ifdef DEBUG_FASTCGI
+ fprintf(stderr, "Forking, %d running\n", running);
+#endif
+ pid = fork();
+ switch (pid) {
+ case 0:
+ /* One of the children.
+ * Make sure we don't go round the
+ * fork loop any more
+ */
+ parent = 0;
+
+ /* don't catch our signals */
+ sigaction(SIGTERM, &old_term, 0);
+ sigaction(SIGQUIT, &old_quit, 0);
+ sigaction(SIGINT, &old_int, 0);
+ break;
+ case -1:
+ perror("php (pre-forking)");
+ exit(1);
+ break;
+ default:
+ /* Fine */
+ running++;
+ break;
+ }
+ } while (parent && (running < children));
+
+ if (parent) {
+#ifdef DEBUG_FASTCGI
+ fprintf(stderr, "Wait for kids, pid %d\n", getpid());
+#endif
+ parent_waiting = 1;
+ while (1) {
+ if (wait(&status) >= 0) {
+ running--;
+ break;
+ } else if (exit_signal) {
+ break;
+ }
+ }
+ if (exit_signal) {
+#if 0
+ while (running > 0) {
+ while (wait(&status) < 0) {
+ }
+ running--;
+ }
+#endif
+ goto parent_out;
+ }
+ }
+ }
+ } else {
+ parent = 0;
+ }
+
+#endif /* WIN32 */
+ }
+
+ zend_first_try {
+ while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 1, 2)) != -1) {
+ switch (c) {
+ case 'T':
+ benchmark = 1;
+ repeats = atoi(php_optarg);
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday(&start, NULL);
+#else
+ time(&start);
+#endif
+ break;
+ case 'h':
+ case '?':
+ if (request) {
+ fcgi_destroy_request(request);
+ }
+ fcgi_shutdown();
+ no_headers = 1;
+ SG(headers_sent) = 1;
+ php_cgi_usage(argv[0]);
+ php_output_end_all(TSRMLS_C);
+ exit_status = 0;
+ goto out;
+ }
+ }
+ php_optind = orig_optind;
+ php_optarg = orig_optarg;
+
+ /* start of FAST CGI loop */
+ /* Initialise FastCGI request structure */
+#ifdef PHP_WIN32
+ /* attempt to set security impersonation for fastcgi
+ * will only happen on NT based OS, others will ignore it. */
+ if (fastcgi && CGIG(impersonate)) {
+ fcgi_impersonate();
+ }
+#endif
+ while (!fastcgi || fcgi_accept_request(request) >= 0) {
+ SG(server_context) = fastcgi ? (void *) request : (void *) 1;
+ init_request_info(request TSRMLS_CC);
+ CG(interactive) = 0;
+
+ if (!cgi && !fastcgi) {
+ while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
+ switch (c) {
+
+ case 'a': /* interactive mode */
+ printf("Interactive mode enabled\n\n");
+ CG(interactive) = 1;
+ break;
+
+ case 'C': /* don't chdir to the script directory */
+ SG(options) |= SAPI_OPTION_NO_CHDIR;
+ break;
+
+ case 'e': /* enable extended info output */
+ CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
+ break;
+
+ case 'f': /* parse file */
+ if (script_file) {
+ efree(script_file);
+ }
+ script_file = estrdup(php_optarg);
+ no_headers = 1;
+ break;
+
+ case 'i': /* php info & quit */
+ if (script_file) {
+ efree(script_file);
+ }
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ SG(server_context) = NULL;
+ php_module_shutdown(TSRMLS_C);
+ return FAILURE;
+ }
+ if (no_headers) {
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+ }
+ php_print_info(0xFFFFFFFF TSRMLS_CC);
+ php_request_shutdown((void *) 0);
+ fcgi_shutdown();
+ exit_status = 0;
+ goto out;
+
+ case 'l': /* syntax check mode */
+ no_headers = 1;
+ behavior = PHP_MODE_LINT;
+ break;
+
+ case 'm': /* list compiled in modules */
+ if (script_file) {
+ efree(script_file);
+ }
+ SG(headers_sent) = 1;
+ php_printf("[PHP Modules]\n");
+ print_modules(TSRMLS_C);
+ php_printf("\n[Zend Modules]\n");
+ print_extensions(TSRMLS_C);
+ php_printf("\n");
+ php_output_end_all(TSRMLS_C);
+ fcgi_shutdown();
+ exit_status = 0;
+ goto out;
+
+#if 0 /* not yet operational, see also below ... */
+ case '': /* generate indented source mode*/
+ behavior=PHP_MODE_INDENT;
+ break;
+#endif
+
+ case 'q': /* do not generate HTTP headers */
+ no_headers = 1;
+ break;
+
+ case 'v': /* show php version & quit */
+ if (script_file) {
+ efree(script_file);
+ }
+ no_headers = 1;
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ SG(server_context) = NULL;
+ php_module_shutdown(TSRMLS_C);
+ return FAILURE;
+ }
+ if (no_headers) {
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+ }
+#if ZEND_DEBUG
+ php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2013 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
+#else
+ php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2013 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
+#endif
+ php_request_shutdown((void *) 0);
+ fcgi_shutdown();
+ exit_status = 0;
+ goto out;
+
+ case 'w':
+ behavior = PHP_MODE_STRIP;
+ break;
+
+ case 'z': /* load extension file */
+ zend_load_extension(php_optarg);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (script_file) {
+ /* override path_translated if -f on command line */
+ STR_FREE(SG(request_info).path_translated);
+ SG(request_info).path_translated = script_file;
+ /* before registering argv to module exchange the *new* argv[0] */
+ /* we can achieve this without allocating more memory */
+ SG(request_info).argc = argc - (php_optind - 1);
+ SG(request_info).argv = &argv[php_optind - 1];
+ SG(request_info).argv[0] = script_file;
+ } else if (argc > php_optind) {
+ /* file is on command line, but not in -f opt */
+ STR_FREE(SG(request_info).path_translated);
+ SG(request_info).path_translated = estrdup(argv[php_optind]);
+ /* arguments after the file are considered script args */
+ SG(request_info).argc = argc - php_optind;
+ SG(request_info).argv = &argv[php_optind];
+ }
+
+ if (no_headers) {
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+ }
+
+ /* all remaining arguments are part of the query string
+ * this section of code concatenates all remaining arguments
+ * into a single string, seperating args with a &
+ * this allows command lines like:
+ *
+ * test.php v1=test v2=hello+world!
+ * test.php "v1=test&v2=hello world!"
+ * test.php v1=test "v2=hello world!"
+ */
+ if (!SG(request_info).query_string && argc > php_optind) {
+ int slen = strlen(PG(arg_separator).input);
+ len = 0;
+ for (i = php_optind; i < argc; i++) {
+ if (i < (argc - 1)) {
+ len += strlen(argv[i]) + slen;
+ } else {
+ len += strlen(argv[i]);
+ }
+ }
+
+ len += 2;
+ s = malloc(len);
+ *s = '\0'; /* we are pretending it came from the environment */
+ for (i = php_optind; i < argc; i++) {
+ strlcat(s, argv[i], len);
+ if (i < (argc - 1)) {
+ strlcat(s, PG(arg_separator).input, len);
+ }
+ }
+ SG(request_info).query_string = s;
+ free_query_string = 1;
+ }
+ } /* end !cgi && !fastcgi */
+
+ /*
+ we never take stdin if we're (f)cgi, always
+ rely on the web server giving us the info
+ we need in the environment.
+ */
+ if (SG(request_info).path_translated || cgi || fastcgi) {
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.handle.fp = NULL;
+ } else {
+ file_handle.filename = "-";
+ file_handle.type = ZEND_HANDLE_FP;
+ file_handle.handle.fp = stdin;
+ }
+
+ file_handle.opened_path = NULL;
+ file_handle.free_filename = 0;
+
+ /* request startup only after we've done all we can to
+ * get path_translated */
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ if (fastcgi) {
+ fcgi_finish_request(request, 1);
+ }
+ SG(server_context) = NULL;
+ php_module_shutdown(TSRMLS_C);
+ return FAILURE;
+ }
+ if (no_headers) {
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+ }
+
+ /*
+ at this point path_translated will be set if:
+ 1. we are running from shell and got filename was there
+ 2. we are running as cgi or fastcgi
+ */
+ if (cgi || fastcgi || SG(request_info).path_translated) {
+ if (php_fopen_primary_script(&file_handle TSRMLS_CC) == FAILURE) {
+ zend_try {
+ if (errno == EACCES) {
+ SG(sapi_headers).http_response_code = 403;
+ PUTS("Access denied.\n");
+ } else {
+ SG(sapi_headers).http_response_code = 404;
+ PUTS("No input file specified.\n");
+ }
+ } zend_catch {
+ } zend_end_try();
+ /* we want to serve more requests if this is fastcgi
+ * so cleanup and continue, request shutdown is
+ * handled later */
+ if (fastcgi) {
+ goto fastcgi_request_done;
+ }
+
+ STR_FREE(SG(request_info).path_translated);
+
+ if (free_query_string && SG(request_info).query_string) {
+ free(SG(request_info).query_string);
+ SG(request_info).query_string = NULL;
+ }
+
+ php_request_shutdown((void *) 0);
+ SG(server_context) = NULL;
+ php_module_shutdown(TSRMLS_C);
+ sapi_shutdown();
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ return FAILURE;
+ }
+ }
+
+ if (CGIG(check_shebang_line)) {
+ /* #!php support */
+ switch (file_handle.type) {
+ case ZEND_HANDLE_FD:
+ if (file_handle.handle.fd < 0) {
+ break;
+ }
+ file_handle.type = ZEND_HANDLE_FP;
+ file_handle.handle.fp = fdopen(file_handle.handle.fd, "rb");
+ /* break missing intentionally */
+ case ZEND_HANDLE_FP:
+ if (!file_handle.handle.fp ||
+ (file_handle.handle.fp == stdin)) {
+ break;
+ }
+ c = fgetc(file_handle.handle.fp);
+ if (c == '#') {
+ while (c != '\n' && c != '\r' && c != EOF) {
+ c = fgetc(file_handle.handle.fp); /* skip to end of line */
+ }
+ /* handle situations where line is terminated by \r\n */
+ if (c == '\r') {
+ if (fgetc(file_handle.handle.fp) != '\n') {
+ long pos = ftell(file_handle.handle.fp);
+ fseek(file_handle.handle.fp, pos - 1, SEEK_SET);
+ }
+ }
+ CG(start_lineno) = 2;
+ } else {
+ rewind(file_handle.handle.fp);
+ }
+ break;
+ case ZEND_HANDLE_STREAM:
+ c = php_stream_getc((php_stream*)file_handle.handle.stream.handle);
+ if (c == '#') {
+ while (c != '\n' && c != '\r' && c != EOF) {
+ c = php_stream_getc((php_stream*)file_handle.handle.stream.handle); /* skip to end of line */
+ }
+ /* handle situations where line is terminated by \r\n */
+ if (c == '\r') {
+ if (php_stream_getc((php_stream*)file_handle.handle.stream.handle) != '\n') {
+ long pos = php_stream_tell((php_stream*)file_handle.handle.stream.handle);
+ php_stream_seek((php_stream*)file_handle.handle.stream.handle, pos - 1, SEEK_SET);
+ }
+ }
+ CG(start_lineno) = 2;
+ } else {
+ php_stream_rewind((php_stream*)file_handle.handle.stream.handle);
+ }
+ break;
+ case ZEND_HANDLE_MAPPED:
+ if (file_handle.handle.stream.mmap.buf[0] == '#') {
+ int i = 1;
+
+ c = file_handle.handle.stream.mmap.buf[i++];
+ while (c != '\n' && c != '\r' && c != EOF) {
+ c = file_handle.handle.stream.mmap.buf[i++];
+ }
+ if (c == '\r') {
+ if (file_handle.handle.stream.mmap.buf[i] == '\n') {
+ i++;
+ }
+ }
+ file_handle.handle.stream.mmap.buf += i;
+ file_handle.handle.stream.mmap.len -= i;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (behavior) {
+ case PHP_MODE_STANDARD:
+ php_execute_script(&file_handle TSRMLS_CC);
+ break;
+ case PHP_MODE_LINT:
+ PG(during_request_startup) = 0;
+ exit_status = php_lint_script(&file_handle TSRMLS_CC);
+ if (exit_status == SUCCESS) {
+ zend_printf("No syntax errors detected in %s\n", file_handle.filename);
+ } else {
+ zend_printf("Errors parsing %s\n", file_handle.filename);
+ }
+ break;
+ case PHP_MODE_STRIP:
+ if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) {
+ zend_strip(TSRMLS_C);
+ zend_file_handle_dtor(&file_handle TSRMLS_CC);
+ php_output_teardown();
+ }
+ return SUCCESS;
+ break;
+ case PHP_MODE_HIGHLIGHT:
+ {
+ zend_syntax_highlighter_ini syntax_highlighter_ini;
+
+ if (open_file_for_scanning(&file_handle TSRMLS_CC) == SUCCESS) {
+ php_get_highlight_struct(&syntax_highlighter_ini);
+ zend_highlight(&syntax_highlighter_ini TSRMLS_CC);
+ if (fastcgi) {
+ goto fastcgi_request_done;
+ }
+ zend_file_handle_dtor(&file_handle TSRMLS_CC);
+ php_output_teardown();
+ }
+ return SUCCESS;
+ }
+ break;
+#if 0
+ /* Zeev might want to do something with this one day */
+ case PHP_MODE_INDENT:
+ open_file_for_scanning(&file_handle TSRMLS_CC);
+ zend_indent();
+ zend_file_handle_dtor(&file_handle TSRMLS_CC);
+ php_output_teardown();
+ return SUCCESS;
+ break;
+#endif
+ }
+
+fastcgi_request_done:
+ {
+ STR_FREE(SG(request_info).path_translated);
+
+ php_request_shutdown((void *) 0);
+
+ if (exit_status == 0) {
+ exit_status = EG(exit_status);
+ }
+
+ if (free_query_string && SG(request_info).query_string) {
+ free(SG(request_info).query_string);
+ SG(request_info).query_string = NULL;
+ }
+ }
+
+ if (!fastcgi) {
+ if (benchmark) {
+ repeats--;
+ if (repeats > 0) {
+ script_file = NULL;
+ php_optind = orig_optind;
+ php_optarg = orig_optarg;
+ continue;
+ }
+ }
+ break;
+ }
+
+ /* only fastcgi will get here */
+ requests++;
+ if (max_requests && (requests == max_requests)) {
+ fcgi_finish_request(request, 1);
+ if (bindpath) {
+ free(bindpath);
+ }
+ if (max_requests != 1) {
+ /* no need to return exit_status of the last request */
+ exit_status = 0;
+ }
+ break;
+ }
+ /* end of fastcgi loop */
+ }
+ if (request) {
+ fcgi_destroy_request(request);
+ }
+ fcgi_shutdown();
+
+ if (cgi_sapi_module.php_ini_path_override) {
+ free(cgi_sapi_module.php_ini_path_override);
+ }
+ if (cgi_sapi_module.ini_entries) {
+ free(cgi_sapi_module.ini_entries);
+ }
+ } zend_catch {
+ exit_status = 255;
+ } zend_end_try();
+
+out:
+ if (benchmark) {
+ int sec;
+#ifdef HAVE_GETTIMEOFDAY
+ int usec;
+
+ gettimeofday(&end, NULL);
+ sec = (int)(end.tv_sec - start.tv_sec);
+ if (end.tv_usec >= start.tv_usec) {
+ usec = (int)(end.tv_usec - start.tv_usec);
+ } else {
+ sec -= 1;
+ usec = (int)(end.tv_usec + 1000000 - start.tv_usec);
+ }
+ fprintf(stderr, "\nElapsed time: %d.%06d sec\n", sec, usec);
+#else
+ time(&end);
+ sec = (int)(end - start);
+ fprintf(stderr, "\nElapsed time: %d sec\n", sec);
+#endif
+ }
+
+#ifndef PHP_WIN32
+parent_out:
+#endif
+
+ SG(server_context) = NULL;
+ php_module_shutdown(TSRMLS_C);
+ sapi_shutdown();
+
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+
+#if defined(PHP_WIN32) && ZEND_DEBUG && 0
+ _CrtDumpMemoryLeaks();
+#endif
+
+ return exit_status;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/cgi/config.w32 b/sapi/cgi/config.w32
new file mode 100644
index 0000000..8d1d431
--- /dev/null
+++ b/sapi/cgi/config.w32
@@ -0,0 +1,10 @@
+// vim:ft=javascript
+// $Id$
+
+ARG_ENABLE('cgi', 'Build CGI version of PHP', 'yes');
+
+if (PHP_CGI == "yes") {
+ ADD_FLAG("LDFLAGS_CGI", "/stack:8388608");
+ SAPI('cgi', 'cgi_main.c fastcgi.c', 'php-cgi.exe');
+ ADD_FLAG('LIBS_CGI', 'ws2_32.lib kernel32.lib advapi32.lib');
+}
diff --git a/sapi/cgi/config9.m4 b/sapi/cgi/config9.m4
new file mode 100644
index 0000000..67251ae
--- /dev/null
+++ b/sapi/cgi/config9.m4
@@ -0,0 +1,76 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_ENABLE(cgi,,
+[ --disable-cgi Disable building CGI version of PHP], yes, no)
+
+dnl
+dnl CGI setup
+dnl
+AC_MSG_CHECKING(for CGI build)
+if test "$PHP_CGI" != "no"; then
+ AC_MSG_RESULT(yes)
+ AC_MSG_CHECKING([for socklen_t in sys/socket.h])
+ AC_EGREP_HEADER([socklen_t], [sys/socket.h],
+ [AC_MSG_RESULT([yes])
+ AC_DEFINE([HAVE_SOCKLEN_T], [1],
+ [Define if the socklen_t typedef is in sys/socket.h])],
+ AC_MSG_RESULT([no]))
+
+ AC_MSG_CHECKING([for sun_len in sys/un.h])
+ AC_EGREP_HEADER([sun_len], [sys/un.h],
+ [AC_MSG_RESULT([yes])
+ AC_DEFINE([HAVE_SOCKADDR_UN_SUN_LEN], [1],
+ [Define if sockaddr_un in sys/un.h contains a sun_len component])],
+ AC_MSG_RESULT([no]))
+
+ AC_MSG_CHECKING([whether cross-process locking is required by accept()])
+ case "`uname -sr`" in
+ IRIX\ 5.* | SunOS\ 5.* | UNIX_System_V\ 4.0)
+ AC_MSG_RESULT([yes])
+ AC_DEFINE([USE_LOCKING], [1],
+ [Define if cross-process locking is required by accept()])
+ ;;
+ *)
+ AC_MSG_RESULT([no])
+ ;;
+ esac
+
+ PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/sapi/cgi/Makefile.frag)
+
+ dnl Set filename
+ case $host_alias in
+ *cygwin* )
+ SAPI_CGI_PATH=sapi/cgi/php-cgi.exe
+ ;;
+ * )
+ SAPI_CGI_PATH=sapi/cgi/php-cgi
+ ;;
+ esac
+
+ dnl Select SAPI
+ PHP_SELECT_SAPI(cgi, program, cgi_main.c fastcgi.c,, '$(SAPI_CGI_PATH)')
+
+ case $host_alias in
+ *aix*)
+ if test "$php_sapi_module" = "shared"; then
+ BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/.libs\/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)"
+ else
+ BUILD_CGI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)"
+ fi
+ ;;
+ *darwin*)
+ BUILD_CGI="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_CGI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)"
+ ;;
+ *)
+ BUILD_CGI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CGI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CGI_PATH)"
+ ;;
+ esac
+
+ dnl Expose to Makefile
+ PHP_SUBST(SAPI_CGI_PATH)
+ PHP_SUBST(BUILD_CGI)
+else
+ AC_MSG_RESULT(yes)
+fi
diff --git a/sapi/cgi/fastcgi.c b/sapi/cgi/fastcgi.c
new file mode 100644
index 0000000..72977b6
--- /dev/null
+++ b/sapi/cgi/fastcgi.c
@@ -0,0 +1,1535 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "php.h"
+#include "fastcgi.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+ typedef unsigned int in_addr_t;
+
+ struct sockaddr_un {
+ short sun_family;
+ char sun_path[MAXPATHLEN];
+ };
+
+ static HANDLE fcgi_accept_mutex = INVALID_HANDLE_VALUE;
+ static int is_impersonate = 0;
+
+#define FCGI_LOCK(fd) \
+ if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \
+ DWORD ret; \
+ while ((ret = WaitForSingleObject(fcgi_accept_mutex, 1000)) == WAIT_TIMEOUT) { \
+ if (in_shutdown) return -1; \
+ } \
+ if (ret == WAIT_FAILED) { \
+ fprintf(stderr, "WaitForSingleObject() failed\n"); \
+ return -1; \
+ } \
+ }
+
+#define FCGI_UNLOCK(fd) \
+ if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \
+ ReleaseMutex(fcgi_accept_mutex); \
+ }
+
+#else
+
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+# include <fcntl.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <netinet/in.h>
+# include <netinet/tcp.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <signal.h>
+
+# define closesocket(s) close(s)
+
+# if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
+# include <sys/poll.h>
+# endif
+# if defined(HAVE_SYS_SELECT_H)
+# include <sys/select.h>
+# endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned long) -1)
+#endif
+
+# ifndef HAVE_SOCKLEN_T
+ typedef unsigned int socklen_t;
+# endif
+
+# ifdef USE_LOCKING
+# define FCGI_LOCK(fd) \
+ do { \
+ struct flock lock; \
+ lock.l_type = F_WRLCK; \
+ lock.l_start = 0; \
+ lock.l_whence = SEEK_SET; \
+ lock.l_len = 0; \
+ if (fcntl(fd, F_SETLKW, &lock) != -1) { \
+ break; \
+ } else if (errno != EINTR || in_shutdown) { \
+ return -1; \
+ } \
+ } while (1)
+
+# define FCGI_UNLOCK(fd) \
+ do { \
+ int orig_errno = errno; \
+ while (1) { \
+ struct flock lock; \
+ lock.l_type = F_UNLCK; \
+ lock.l_start = 0; \
+ lock.l_whence = SEEK_SET; \
+ lock.l_len = 0; \
+ if (fcntl(fd, F_SETLK, &lock) != -1) { \
+ break; \
+ } else if (errno != EINTR) { \
+ return -1; \
+ } \
+ } \
+ errno = orig_errno; \
+ } while (0)
+# else
+# define FCGI_LOCK(fd)
+# define FCGI_UNLOCK(fd)
+# endif
+
+#endif
+
+typedef union _sa_t {
+ struct sockaddr sa;
+ struct sockaddr_un sa_unix;
+ struct sockaddr_in sa_inet;
+} sa_t;
+
+static HashTable fcgi_mgmt_vars;
+
+static int is_initialized = 0;
+static int is_fastcgi = 0;
+static int in_shutdown = 0;
+static in_addr_t *allowed_clients = NULL;
+
+/* hash table */
+
+#define FCGI_HASH_TABLE_SIZE 128
+#define FCGI_HASH_TABLE_MASK (FCGI_HASH_TABLE_SIZE - 1)
+#define FCGI_HASH_SEG_SIZE 4096
+
+typedef struct _fcgi_hash_bucket {
+ unsigned int hash_value;
+ unsigned int var_len;
+ char *var;
+ unsigned int val_len;
+ char *val;
+ struct _fcgi_hash_bucket *next;
+ struct _fcgi_hash_bucket *list_next;
+} fcgi_hash_bucket;
+
+typedef struct _fcgi_hash_buckets {
+ unsigned int idx;
+ struct _fcgi_hash_buckets *next;
+ struct _fcgi_hash_bucket data[FCGI_HASH_TABLE_SIZE];
+} fcgi_hash_buckets;
+
+typedef struct _fcgi_data_seg {
+ char *pos;
+ char *end;
+ struct _fcgi_data_seg *next;
+ char data[1];
+} fcgi_data_seg;
+
+typedef struct _fcgi_hash {
+ fcgi_hash_bucket *hash_table[FCGI_HASH_TABLE_SIZE];
+ fcgi_hash_bucket *list;
+ fcgi_hash_buckets *buckets;
+ fcgi_data_seg *data;
+} fcgi_hash;
+
+static void fcgi_hash_init(fcgi_hash *h)
+{
+ memset(h->hash_table, 0, sizeof(h->hash_table));
+ h->list = NULL;
+ h->buckets = (fcgi_hash_buckets*)malloc(sizeof(fcgi_hash_buckets));
+ h->buckets->idx = 0;
+ h->buckets->next = NULL;
+ h->data = (fcgi_data_seg*)malloc(sizeof(fcgi_data_seg) - 1 + FCGI_HASH_SEG_SIZE);
+ h->data->pos = h->data->data;
+ h->data->end = h->data->pos + FCGI_HASH_SEG_SIZE;
+ h->data->next = NULL;
+}
+
+static void fcgi_hash_destroy(fcgi_hash *h)
+{
+ fcgi_hash_buckets *b;
+ fcgi_data_seg *p;
+
+ b = h->buckets;
+ while (b) {
+ fcgi_hash_buckets *q = b;
+ b = b->next;
+ free(q);
+ }
+ p = h->data;
+ while (p) {
+ fcgi_data_seg *q = p;
+ p = p->next;
+ free(q);
+ }
+}
+
+static void fcgi_hash_clean(fcgi_hash *h)
+{
+ memset(h->hash_table, 0, sizeof(h->hash_table));
+ h->list = NULL;
+ /* delete all bucket blocks except the first one */
+ while (h->buckets->next) {
+ fcgi_hash_buckets *q = h->buckets;
+
+ h->buckets = h->buckets->next;
+ free(q);
+ }
+ h->buckets->idx = 0;
+ /* delete all data segments except the first one */
+ while (h->data->next) {
+ fcgi_data_seg *q = h->data;
+
+ h->data = h->data->next;
+ free(q);
+ }
+ h->data->pos = h->data->data;
+}
+
+static inline char* fcgi_hash_strndup(fcgi_hash *h, char *str, unsigned int str_len)
+{
+ char *ret;
+
+ if (UNEXPECTED(h->data->pos + str_len + 1 >= h->data->end)) {
+ unsigned int seg_size = (str_len + 1 > FCGI_HASH_SEG_SIZE) ? str_len + 1 : FCGI_HASH_SEG_SIZE;
+ fcgi_data_seg *p = (fcgi_data_seg*)malloc(sizeof(fcgi_data_seg) - 1 + seg_size);
+
+ p->pos = p->data;
+ p->end = p->pos + seg_size;
+ p->next = h->data;
+ h->data = p;
+ }
+ ret = h->data->pos;
+ memcpy(ret, str, str_len);
+ ret[str_len] = 0;
+ h->data->pos += str_len + 1;
+ return ret;
+}
+
+static char* fcgi_hash_set(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len, char *val, unsigned int val_len)
+{
+ unsigned int idx = hash_value & FCGI_HASH_TABLE_MASK;
+ fcgi_hash_bucket *p = h->hash_table[idx];
+
+ while (UNEXPECTED(p != NULL)) {
+ if (UNEXPECTED(p->hash_value == hash_value) &&
+ p->var_len == var_len &&
+ memcmp(p->var, var, var_len) == 0) {
+
+ p->val_len = val_len;
+ p->val = fcgi_hash_strndup(h, val, val_len);
+ return p->val;
+ }
+ p = p->next;
+ }
+
+ if (UNEXPECTED(h->buckets->idx >= FCGI_HASH_TABLE_SIZE)) {
+ fcgi_hash_buckets *b = (fcgi_hash_buckets*)malloc(sizeof(fcgi_hash_buckets));
+ b->idx = 0;
+ b->next = h->buckets;
+ h->buckets = b;
+ }
+ p = h->buckets->data + h->buckets->idx;
+ h->buckets->idx++;
+ p->next = h->hash_table[idx];
+ h->hash_table[idx] = p;
+ p->list_next = h->list;
+ h->list = p;
+ p->hash_value = hash_value;
+ p->var_len = var_len;
+ p->var = fcgi_hash_strndup(h, var, var_len);
+ p->val_len = val_len;
+ p->val = fcgi_hash_strndup(h, val, val_len);
+ return p->val;
+}
+
+static void fcgi_hash_del(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len)
+{
+ unsigned int idx = hash_value & FCGI_HASH_TABLE_MASK;
+ fcgi_hash_bucket **p = &h->hash_table[idx];
+
+ while (*p != NULL) {
+ if ((*p)->hash_value == hash_value &&
+ (*p)->var_len == var_len &&
+ memcmp((*p)->var, var, var_len) == 0) {
+
+ (*p)->val = NULL; /* NULL value means deleted */
+ (*p)->val_len = 0;
+ *p = (*p)->next;
+ return;
+ }
+ p = &(*p)->next;
+ }
+}
+
+static char *fcgi_hash_get(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len, unsigned int *val_len)
+{
+ unsigned int idx = hash_value & FCGI_HASH_TABLE_MASK;
+ fcgi_hash_bucket *p = h->hash_table[idx];
+
+ while (p != NULL) {
+ if (p->hash_value == hash_value &&
+ p->var_len == var_len &&
+ memcmp(p->var, var, var_len) == 0) {
+ *val_len = p->val_len;
+ return p->val;
+ }
+ p = p->next;
+ }
+ return NULL;
+}
+
+static void fcgi_hash_apply(fcgi_hash *h, fcgi_apply_func func, void *arg TSRMLS_DC)
+{
+ fcgi_hash_bucket *p = h->list;
+
+ while (p) {
+ if (EXPECTED(p->val != NULL)) {
+ func(p->var, p->var_len, p->val, p->val_len, arg TSRMLS_CC);
+ }
+ p = p->list_next;
+ }
+}
+
+struct _fcgi_request {
+ int listen_socket;
+ int tcp;
+ int fd;
+ int id;
+ int keep;
+#ifdef TCP_NODELAY
+ int nodelay;
+#endif
+ int closed;
+
+ int in_len;
+ int in_pad;
+
+ fcgi_header *out_hdr;
+ unsigned char *out_pos;
+ unsigned char out_buf[1024*8];
+ unsigned char reserved[sizeof(fcgi_end_request_rec)];
+
+ int has_env;
+ fcgi_hash env;
+};
+
+#ifdef _WIN32
+
+static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg)
+{
+ HANDLE shutdown_event = (HANDLE) arg;
+ WaitForSingleObject(shutdown_event, INFINITE);
+ in_shutdown = 1;
+ return 0;
+}
+
+#else
+
+static void fcgi_signal_handler(int signo)
+{
+ if (signo == SIGUSR1 || signo == SIGTERM) {
+ in_shutdown = 1;
+ }
+}
+
+static void fcgi_setup_signals(void)
+{
+ struct sigaction new_sa, old_sa;
+
+ sigemptyset(&new_sa.sa_mask);
+ new_sa.sa_flags = 0;
+ new_sa.sa_handler = fcgi_signal_handler;
+ sigaction(SIGUSR1, &new_sa, NULL);
+ sigaction(SIGTERM, &new_sa, NULL);
+ sigaction(SIGPIPE, NULL, &old_sa);
+ if (old_sa.sa_handler == SIG_DFL) {
+ sigaction(SIGPIPE, &new_sa, NULL);
+ }
+}
+#endif
+
+int fcgi_in_shutdown(void)
+{
+ return in_shutdown;
+}
+
+void fcgi_terminate(void)
+{
+ in_shutdown = 1;
+}
+
+int fcgi_init(void)
+{
+ if (!is_initialized) {
+#ifndef _WIN32
+ sa_t sa;
+ socklen_t len = sizeof(sa);
+#endif
+ zend_hash_init(&fcgi_mgmt_vars, 0, NULL, fcgi_free_mgmt_var_cb, 1);
+ fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS")-1, "0", sizeof("0")-1);
+
+ is_initialized = 1;
+#ifdef _WIN32
+# if 0
+ /* TODO: Support for TCP sockets */
+ WSADATA wsaData;
+
+ if (WSAStartup(MAKEWORD(2,0), &wsaData)) {
+ fprintf(stderr, "Error starting Windows Sockets. Error: %d", WSAGetLastError());
+ return 0;
+ }
+# endif
+ if ((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
+ (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) &&
+ (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE)) {
+ char *str;
+ DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT;
+ HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE);
+
+ SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL);
+
+ str = getenv("_FCGI_SHUTDOWN_EVENT_");
+ if (str != NULL) {
+ HANDLE shutdown_event = (HANDLE) atoi(str);
+ if (!CreateThread(NULL, 0, fcgi_shutdown_thread,
+ shutdown_event, 0, NULL)) {
+ return -1;
+ }
+ }
+ str = getenv("_FCGI_MUTEX_");
+ if (str != NULL) {
+ fcgi_accept_mutex = (HANDLE) atoi(str);
+ }
+ return is_fastcgi = 1;
+ } else {
+ return is_fastcgi = 0;
+ }
+#else
+ errno = 0;
+ if (getpeername(0, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) {
+ fcgi_setup_signals();
+ return is_fastcgi = 1;
+ } else {
+ return is_fastcgi = 0;
+ }
+#endif
+ }
+ return is_fastcgi;
+}
+
+
+int fcgi_is_fastcgi(void)
+{
+ if (!is_initialized) {
+ return fcgi_init();
+ } else {
+ return is_fastcgi;
+ }
+}
+
+void fcgi_shutdown(void)
+{
+ if (is_initialized) {
+ zend_hash_destroy(&fcgi_mgmt_vars);
+ }
+ is_fastcgi = 0;
+ if (allowed_clients) {
+ free(allowed_clients);
+ }
+}
+
+#ifdef _WIN32
+/* Do some black magic with the NT security API.
+ * We prepare a DACL (Discretionary Access Control List) so that
+ * we, the creator, are allowed all access, while "Everyone Else"
+ * is only allowed to read and write to the pipe.
+ * This avoids security issues on shared hosts where a luser messes
+ * with the lower-level pipe settings and screws up the FastCGI service.
+ */
+static PACL prepare_named_pipe_acl(PSECURITY_DESCRIPTOR sd, LPSECURITY_ATTRIBUTES sa)
+{
+ DWORD req_acl_size;
+ char everyone_buf[32], owner_buf[32];
+ PSID sid_everyone, sid_owner;
+ SID_IDENTIFIER_AUTHORITY
+ siaWorld = SECURITY_WORLD_SID_AUTHORITY,
+ siaCreator = SECURITY_CREATOR_SID_AUTHORITY;
+ PACL acl;
+
+ sid_everyone = (PSID)&everyone_buf;
+ sid_owner = (PSID)&owner_buf;
+
+ req_acl_size = sizeof(ACL) +
+ (2 * ((sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetSidLengthRequired(1)));
+
+ acl = malloc(req_acl_size);
+
+ if (acl == NULL) {
+ return NULL;
+ }
+
+ if (!InitializeSid(sid_everyone, &siaWorld, 1)) {
+ goto out_fail;
+ }
+ *GetSidSubAuthority(sid_everyone, 0) = SECURITY_WORLD_RID;
+
+ if (!InitializeSid(sid_owner, &siaCreator, 1)) {
+ goto out_fail;
+ }
+ *GetSidSubAuthority(sid_owner, 0) = SECURITY_CREATOR_OWNER_RID;
+
+ if (!InitializeAcl(acl, req_acl_size, ACL_REVISION)) {
+ goto out_fail;
+ }
+
+ if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_GENERIC_READ | FILE_GENERIC_WRITE, sid_everyone)) {
+ goto out_fail;
+ }
+
+ if (!AddAccessAllowedAce(acl, ACL_REVISION, FILE_ALL_ACCESS, sid_owner)) {
+ goto out_fail;
+ }
+
+ if (!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) {
+ goto out_fail;
+ }
+
+ if (!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE)) {
+ goto out_fail;
+ }
+
+ sa->lpSecurityDescriptor = sd;
+
+ return acl;
+
+out_fail:
+ free(acl);
+ return NULL;
+}
+#endif
+
+static int is_port_number(const char *bindpath)
+{
+ while (*bindpath) {
+ if (*bindpath < '0' || *bindpath > '9') {
+ return 0;
+ }
+ bindpath++;
+ }
+ return 1;
+}
+
+int fcgi_listen(const char *path, int backlog)
+{
+ char *s;
+ int tcp = 0;
+ char host[MAXPATHLEN];
+ short port = 0;
+ int listen_socket;
+ sa_t sa;
+ socklen_t sock_len;
+#ifdef SO_REUSEADDR
+# ifdef _WIN32
+ BOOL reuse = 1;
+# else
+ int reuse = 1;
+# endif
+#endif
+
+ if ((s = strchr(path, ':'))) {
+ port = atoi(s+1);
+ if (port != 0 && (s-path) < MAXPATHLEN) {
+ strncpy(host, path, s-path);
+ host[s-path] = '\0';
+ tcp = 1;
+ }
+ } else if (is_port_number(path)) {
+ port = atoi(path);
+ if (port != 0) {
+ host[0] = '\0';
+ tcp = 1;
+ }
+ }
+
+ /* Prepare socket address */
+ if (tcp) {
+ memset(&sa.sa_inet, 0, sizeof(sa.sa_inet));
+ sa.sa_inet.sin_family = AF_INET;
+ sa.sa_inet.sin_port = htons(port);
+ sock_len = sizeof(sa.sa_inet);
+
+ if (!*host || !strncmp(host, "*", sizeof("*")-1)) {
+ sa.sa_inet.sin_addr.s_addr = htonl(INADDR_ANY);
+ } else {
+ sa.sa_inet.sin_addr.s_addr = inet_addr(host);
+ if (sa.sa_inet.sin_addr.s_addr == INADDR_NONE) {
+ struct hostent *hep;
+
+ hep = gethostbyname(host);
+ if (!hep || hep->h_addrtype != AF_INET || !hep->h_addr_list[0]) {
+ fprintf(stderr, "Cannot resolve host name '%s'!\n", host);
+ return -1;
+ } else if (hep->h_addr_list[1]) {
+ fprintf(stderr, "Host '%s' has multiple addresses. You must choose one explicitly!\n", host);
+ return -1;
+ }
+ sa.sa_inet.sin_addr.s_addr = ((struct in_addr*)hep->h_addr_list[0])->s_addr;
+ }
+ }
+ } else {
+#ifdef _WIN32
+ SECURITY_DESCRIPTOR sd;
+ SECURITY_ATTRIBUTES saw;
+ PACL acl;
+ HANDLE namedPipe;
+
+ memset(&sa, 0, sizeof(saw));
+ saw.nLength = sizeof(saw);
+ saw.bInheritHandle = FALSE;
+ acl = prepare_named_pipe_acl(&sd, &saw);
+
+ namedPipe = CreateNamedPipe(path,
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
+ PIPE_UNLIMITED_INSTANCES,
+ 8192, 8192, 0, &saw);
+ if (namedPipe == INVALID_HANDLE_VALUE) {
+ return -1;
+ }
+ listen_socket = _open_osfhandle((long)namedPipe, 0);
+ if (!is_initialized) {
+ fcgi_init();
+ }
+ is_fastcgi = 1;
+ return listen_socket;
+
+#else
+ int path_len = strlen(path);
+
+ if (path_len >= sizeof(sa.sa_unix.sun_path)) {
+ fprintf(stderr, "Listening socket's path name is too long.\n");
+ return -1;
+ }
+
+ memset(&sa.sa_unix, 0, sizeof(sa.sa_unix));
+ sa.sa_unix.sun_family = AF_UNIX;
+ memcpy(sa.sa_unix.sun_path, path, path_len + 1);
+ sock_len = (size_t)(((struct sockaddr_un *)0)->sun_path) + path_len;
+#ifdef HAVE_SOCKADDR_UN_SUN_LEN
+ sa.sa_unix.sun_len = sock_len;
+#endif
+ unlink(path);
+#endif
+ }
+
+ /* Create, bind socket and start listen on it */
+ if ((listen_socket = socket(sa.sa.sa_family, SOCK_STREAM, 0)) < 0 ||
+#ifdef SO_REUSEADDR
+ setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) < 0 ||
+#endif
+ bind(listen_socket, (struct sockaddr *) &sa, sock_len) < 0 ||
+ listen(listen_socket, backlog) < 0) {
+
+ fprintf(stderr, "Cannot bind/listen socket - [%d] %s.\n",errno, strerror(errno));
+ return -1;
+ }
+
+ if (!tcp) {
+ chmod(path, 0777);
+ } else {
+ char *ip = getenv("FCGI_WEB_SERVER_ADDRS");
+ char *cur, *end;
+ int n;
+
+ if (ip) {
+ ip = strdup(ip);
+ cur = ip;
+ n = 0;
+ while (*cur) {
+ if (*cur == ',') n++;
+ cur++;
+ }
+ allowed_clients = malloc(sizeof(in_addr_t) * (n+2));
+ n = 0;
+ cur = ip;
+ while (cur) {
+ end = strchr(cur, ',');
+ if (end) {
+ *end = 0;
+ end++;
+ }
+ allowed_clients[n] = inet_addr(cur);
+ if (allowed_clients[n] == INADDR_NONE) {
+ fprintf(stderr, "Wrong IP address '%s' in FCGI_WEB_SERVER_ADDRS\n", cur);
+ }
+ n++;
+ cur = end;
+ }
+ allowed_clients[n] = INADDR_NONE;
+ free(ip);
+ }
+ }
+
+ if (!is_initialized) {
+ fcgi_init();
+ }
+ is_fastcgi = 1;
+
+#ifdef _WIN32
+ if (tcp) {
+ listen_socket = _open_osfhandle((long)listen_socket, 0);
+ }
+#else
+ fcgi_setup_signals();
+#endif
+ return listen_socket;
+}
+
+fcgi_request *fcgi_init_request(int listen_socket)
+{
+ fcgi_request *req = (fcgi_request*)calloc(1, sizeof(fcgi_request));
+ req->listen_socket = listen_socket;
+ req->fd = -1;
+ req->id = -1;
+
+ req->in_len = 0;
+ req->in_pad = 0;
+
+ req->out_hdr = NULL;
+ req->out_pos = req->out_buf;
+
+#ifdef _WIN32
+ req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL);
+#endif
+
+#ifdef TCP_NODELAY
+ req->nodelay = 0;
+#endif
+
+ fcgi_hash_init(&req->env);
+
+ return req;
+}
+
+void fcgi_destroy_request(fcgi_request *req)
+{
+ fcgi_hash_destroy(&req->env);
+ free(req);
+}
+
+static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count)
+{
+ int ret;
+ size_t n = 0;
+
+ do {
+ errno = 0;
+#ifdef _WIN32
+ if (!req->tcp) {
+ ret = write(req->fd, ((char*)buf)+n, count-n);
+ } else {
+ ret = send(req->fd, ((char*)buf)+n, count-n, 0);
+ if (ret <= 0) {
+ errno = WSAGetLastError();
+ }
+ }
+#else
+ ret = write(req->fd, ((char*)buf)+n, count-n);
+#endif
+ if (ret > 0) {
+ n += ret;
+ } else if (ret <= 0 && errno != 0 && errno != EINTR) {
+ return ret;
+ }
+ } while (n != count);
+ return n;
+}
+
+static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count)
+{
+ int ret;
+ size_t n = 0;
+
+ do {
+ errno = 0;
+#ifdef _WIN32
+ if (!req->tcp) {
+ ret = read(req->fd, ((char*)buf)+n, count-n);
+ } else {
+ ret = recv(req->fd, ((char*)buf)+n, count-n, 0);
+ if (ret <= 0) {
+ errno = WSAGetLastError();
+ }
+ }
+#else
+ ret = read(req->fd, ((char*)buf)+n, count-n);
+#endif
+ if (ret > 0) {
+ n += ret;
+ } else if (ret == 0 && errno == 0) {
+ return n;
+ } else if (ret <= 0 && errno != 0 && errno != EINTR) {
+ return ret;
+ }
+ } while (n != count);
+ return n;
+}
+
+static inline int fcgi_make_header(fcgi_header *hdr, fcgi_request_type type, int req_id, int len)
+{
+ int pad = ((len + 7) & ~7) - len;
+
+ hdr->contentLengthB0 = (unsigned char)(len & 0xff);
+ hdr->contentLengthB1 = (unsigned char)((len >> 8) & 0xff);
+ hdr->paddingLength = (unsigned char)pad;
+ hdr->requestIdB0 = (unsigned char)(req_id & 0xff);
+ hdr->requestIdB1 = (unsigned char)((req_id >> 8) & 0xff);
+ hdr->reserved = 0;
+ hdr->type = type;
+ hdr->version = FCGI_VERSION_1;
+ if (pad) {
+ memset(((unsigned char*)hdr) + sizeof(fcgi_header) + len, 0, pad);
+ }
+ return pad;
+}
+
+static int fcgi_get_params(fcgi_request *req, unsigned char *p, unsigned char *end)
+{
+ unsigned int name_len, val_len;
+
+ while (p < end) {
+ name_len = *p++;
+ if (UNEXPECTED(name_len >= 128)) {
+ if (UNEXPECTED(p + 3 >= end)) return 0;
+ name_len = ((name_len & 0x7f) << 24);
+ name_len |= (*p++ << 16);
+ name_len |= (*p++ << 8);
+ name_len |= *p++;
+ }
+ if (UNEXPECTED(p >= end)) return 0;
+ val_len = *p++;
+ if (UNEXPECTED(val_len >= 128)) {
+ if (UNEXPECTED(p + 3 >= end)) return 0;
+ val_len = ((val_len & 0x7f) << 24);
+ val_len |= (*p++ << 16);
+ val_len |= (*p++ << 8);
+ val_len |= *p++;
+ }
+ if (UNEXPECTED(name_len + val_len > (unsigned int) (end - p))) {
+ /* Malformated request */
+ return 0;
+ }
+ fcgi_hash_set(&req->env, FCGI_HASH_FUNC(p, name_len), (char*)p, name_len, (char*)p + name_len, val_len);
+ p += name_len + val_len;
+ }
+ return 1;
+}
+
+static int fcgi_read_request(fcgi_request *req)
+{
+ fcgi_header hdr;
+ int len, padding;
+ unsigned char buf[FCGI_MAX_LENGTH+8];
+
+ req->keep = 0;
+ req->closed = 0;
+ req->in_len = 0;
+ req->out_hdr = NULL;
+ req->out_pos = req->out_buf;
+ req->has_env = 1;
+
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1) {
+ return 0;
+ }
+
+ len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ padding = hdr.paddingLength;
+
+ while (hdr.type == FCGI_STDIN && len == 0) {
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1) {
+ return 0;
+ }
+
+ len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ padding = hdr.paddingLength;
+ }
+
+ if (len + padding > FCGI_MAX_LENGTH) {
+ return 0;
+ }
+
+ req->id = (hdr.requestIdB1 << 8) + hdr.requestIdB0;
+
+ if (hdr.type == FCGI_BEGIN_REQUEST && len == sizeof(fcgi_begin_request)) {
+ if (safe_read(req, buf, len+padding) != len+padding) {
+ return 0;
+ }
+
+ req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN);
+#ifdef TCP_NODELAY
+ if (req->keep && req->tcp && !req->nodelay) {
+# ifdef _WIN32
+ BOOL on = 1;
+# else
+ int on = 1;
+# endif
+
+ setsockopt(req->fd, IPPROTO_TCP, TCP_NODELAY, (char*)&on, sizeof(on));
+ req->nodelay = 1;
+ }
+#endif
+ switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) {
+ case FCGI_RESPONDER:
+ fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "RESPONDER", sizeof("RESPONDER")-1);
+ break;
+ case FCGI_AUTHORIZER:
+ fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "AUTHORIZER", sizeof("AUTHORIZER")-1);
+ break;
+ case FCGI_FILTER:
+ fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "FILTER", sizeof("FILTER")-1);
+ break;
+ default:
+ return 0;
+ }
+
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1) {
+ return 0;
+ }
+
+ len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ padding = hdr.paddingLength;
+
+ while (hdr.type == FCGI_PARAMS && len > 0) {
+ if (len + padding > FCGI_MAX_LENGTH) {
+ return 0;
+ }
+
+ if (safe_read(req, buf, len+padding) != len+padding) {
+ req->keep = 0;
+ return 0;
+ }
+
+ if (!fcgi_get_params(req, buf, buf+len)) {
+ req->keep = 0;
+ return 0;
+ }
+
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1) {
+ req->keep = 0;
+ return 0;
+ }
+ len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ padding = hdr.paddingLength;
+ }
+ } else if (hdr.type == FCGI_GET_VALUES) {
+ unsigned char *p = buf + sizeof(fcgi_header);
+ zval ** value;
+ unsigned int zlen;
+ fcgi_hash_bucket *q;
+
+ if (safe_read(req, buf, len+padding) != len+padding) {
+ req->keep = 0;
+ return 0;
+ }
+
+ if (!fcgi_get_params(req, buf, buf+len)) {
+ req->keep = 0;
+ return 0;
+ }
+
+ q = req->env.list;
+ while (q != NULL) {
+ if (zend_hash_find(&fcgi_mgmt_vars, q->var, q->var_len, (void**) &value) != SUCCESS) {
+ continue;
+ }
+ zlen = Z_STRLEN_PP(value);
+ if ((p + 4 + 4 + q->var_len + zlen) >= (buf + sizeof(buf))) {
+ break;
+ }
+ if (q->var_len < 0x80) {
+ *p++ = q->var_len;
+ } else {
+ *p++ = ((q->var_len >> 24) & 0xff) | 0x80;
+ *p++ = (q->var_len >> 16) & 0xff;
+ *p++ = (q->var_len >> 8) & 0xff;
+ *p++ = q->var_len & 0xff;
+ }
+ if (zlen < 0x80) {
+ *p++ = zlen;
+ } else {
+ *p++ = ((zlen >> 24) & 0xff) | 0x80;
+ *p++ = (zlen >> 16) & 0xff;
+ *p++ = (zlen >> 8) & 0xff;
+ *p++ = zlen & 0xff;
+ }
+ memcpy(p, q->var, q->var_len);
+ p += q->var_len;
+ memcpy(p, Z_STRVAL_PP(value), zlen);
+ p += zlen;
+ }
+ len = p - buf - sizeof(fcgi_header);
+ len += fcgi_make_header((fcgi_header*)buf, FCGI_GET_VALUES_RESULT, 0, len);
+ if (safe_write(req, buf, sizeof(fcgi_header)+len) != (int)sizeof(fcgi_header)+len) {
+ req->keep = 0;
+ return 0;
+ }
+ return 0;
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+int fcgi_read(fcgi_request *req, char *str, int len)
+{
+ int ret, n, rest;
+ fcgi_header hdr;
+ unsigned char buf[255];
+
+ n = 0;
+ rest = len;
+ while (rest > 0) {
+ if (req->in_len == 0) {
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1 ||
+ hdr.type != FCGI_STDIN) {
+ req->keep = 0;
+ return 0;
+ }
+ req->in_len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ req->in_pad = hdr.paddingLength;
+ if (req->in_len == 0) {
+ return n;
+ }
+ }
+
+ if (req->in_len >= rest) {
+ ret = safe_read(req, str, rest);
+ } else {
+ ret = safe_read(req, str, req->in_len);
+ }
+ if (ret < 0) {
+ req->keep = 0;
+ return ret;
+ } else if (ret > 0) {
+ req->in_len -= ret;
+ rest -= ret;
+ n += ret;
+ str += ret;
+ if (req->in_len == 0) {
+ if (req->in_pad) {
+ if (safe_read(req, buf, req->in_pad) != req->in_pad) {
+ req->keep = 0;
+ return ret;
+ }
+ }
+ } else {
+ return n;
+ }
+ } else {
+ return n;
+ }
+ }
+ return n;
+}
+
+static inline void fcgi_close(fcgi_request *req, int force, int destroy)
+{
+ if (destroy && req->has_env) {
+ fcgi_hash_clean(&req->env);
+ req->has_env = 0;
+ }
+
+#ifdef _WIN32
+ if (is_impersonate && !req->tcp) {
+ RevertToSelf();
+ }
+#endif
+
+ if ((force || !req->keep) && req->fd >= 0) {
+#ifdef _WIN32
+ if (!req->tcp) {
+ HANDLE pipe = (HANDLE)_get_osfhandle(req->fd);
+
+ if (!force) {
+ FlushFileBuffers(pipe);
+ }
+ DisconnectNamedPipe(pipe);
+ } else {
+ if (!force) {
+ fcgi_header buf;
+
+ shutdown(req->fd, 1);
+ /* read the last FCGI_STDIN header (it may be omitted) */
+ recv(req->fd, &buf, sizeof(buf), 0);
+ }
+ closesocket(req->fd);
+ }
+#else
+ if (!force) {
+ fcgi_header buf;
+
+ shutdown(req->fd, 1);
+ /* read the last FCGI_STDIN header (it may be omitted) */
+ recv(req->fd, &buf, sizeof(buf), 0);
+ }
+ close(req->fd);
+#endif
+#ifdef TCP_NODELAY
+ req->nodelay = 0;
+#endif
+ req->fd = -1;
+ }
+}
+
+int fcgi_accept_request(fcgi_request *req)
+{
+#ifdef _WIN32
+ HANDLE pipe;
+ OVERLAPPED ov;
+#endif
+
+ while (1) {
+ if (req->fd < 0) {
+ while (1) {
+ if (in_shutdown) {
+ return -1;
+ }
+#ifdef _WIN32
+ if (!req->tcp) {
+ pipe = (HANDLE)_get_osfhandle(req->listen_socket);
+ FCGI_LOCK(req->listen_socket);
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!ConnectNamedPipe(pipe, &ov)) {
+ errno = GetLastError();
+ if (errno == ERROR_IO_PENDING) {
+ while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) {
+ if (in_shutdown) {
+ CloseHandle(ov.hEvent);
+ FCGI_UNLOCK(req->listen_socket);
+ return -1;
+ }
+ }
+ } else if (errno != ERROR_PIPE_CONNECTED) {
+ }
+ }
+ CloseHandle(ov.hEvent);
+ req->fd = req->listen_socket;
+ FCGI_UNLOCK(req->listen_socket);
+ } else {
+ SOCKET listen_socket = (SOCKET)_get_osfhandle(req->listen_socket);
+#else
+ {
+ int listen_socket = req->listen_socket;
+#endif
+ sa_t sa;
+ socklen_t len = sizeof(sa);
+
+ FCGI_LOCK(req->listen_socket);
+ req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len);
+ FCGI_UNLOCK(req->listen_socket);
+ if (req->fd >= 0) {
+ if (((struct sockaddr *)&sa)->sa_family == AF_INET) {
+#ifndef _WIN32
+ req->tcp = 1;
+#endif
+ if (allowed_clients) {
+ int n = 0;
+ int allowed = 0;
+
+ while (allowed_clients[n] != INADDR_NONE) {
+ if (allowed_clients[n] == sa.sa_inet.sin_addr.s_addr) {
+ allowed = 1;
+ break;
+ }
+ n++;
+ }
+ if (!allowed) {
+ fprintf(stderr, "Connection from disallowed IP address '%s' is dropped.\n", inet_ntoa(sa.sa_inet.sin_addr));
+ closesocket(req->fd);
+ req->fd = -1;
+ continue;
+ }
+ }
+#ifndef _WIN32
+ } else {
+ req->tcp = 0;
+#endif
+ }
+ }
+ }
+
+#ifdef _WIN32
+ if (req->fd < 0 && (in_shutdown || errno != EINTR)) {
+#else
+ if (req->fd < 0 && (in_shutdown || (errno != EINTR && errno != ECONNABORTED))) {
+#endif
+ return -1;
+ }
+
+#ifdef _WIN32
+ break;
+#else
+ if (req->fd >= 0) {
+#if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
+ struct pollfd fds;
+ int ret;
+
+ fds.fd = req->fd;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ do {
+ errno = 0;
+ ret = poll(&fds, 1, 5000);
+ } while (ret < 0 && errno == EINTR);
+ if (ret > 0 && (fds.revents & POLLIN)) {
+ break;
+ }
+ fcgi_close(req, 1, 0);
+#else
+ if (req->fd < FD_SETSIZE) {
+ struct timeval tv = {5,0};
+ fd_set set;
+ int ret;
+
+ FD_ZERO(&set);
+ FD_SET(req->fd, &set);
+ do {
+ errno = 0;
+ ret = select(req->fd + 1, &set, NULL, NULL, &tv) >= 0;
+ } while (ret < 0 && errno == EINTR);
+ if (ret > 0 && FD_ISSET(req->fd, &set)) {
+ break;
+ }
+ fcgi_close(req, 1, 0);
+ } else {
+ fprintf(stderr, "Too many open file descriptors. FD_SETSIZE limit exceeded.");
+ fcgi_close(req, 1, 0);
+ }
+#endif
+ }
+#endif
+ }
+ } else if (in_shutdown) {
+ return -1;
+ }
+ if (fcgi_read_request(req)) {
+#ifdef _WIN32
+ if (is_impersonate && !req->tcp) {
+ pipe = (HANDLE)_get_osfhandle(req->fd);
+ if (!ImpersonateNamedPipeClient(pipe)) {
+ fcgi_close(req, 1, 1);
+ continue;
+ }
+ }
+#endif
+ return req->fd;
+ } else {
+ fcgi_close(req, 1, 1);
+ }
+ }
+}
+
+static inline fcgi_header* open_packet(fcgi_request *req, fcgi_request_type type)
+{
+ req->out_hdr = (fcgi_header*) req->out_pos;
+ req->out_hdr->type = type;
+ req->out_pos += sizeof(fcgi_header);
+ return req->out_hdr;
+}
+
+static inline void close_packet(fcgi_request *req)
+{
+ if (req->out_hdr) {
+ int len = req->out_pos - ((unsigned char*)req->out_hdr + sizeof(fcgi_header));
+
+ req->out_pos += fcgi_make_header(req->out_hdr, (fcgi_request_type)req->out_hdr->type, req->id, len);
+ req->out_hdr = NULL;
+ }
+}
+
+int fcgi_flush(fcgi_request *req, int close)
+{
+ int len;
+
+ close_packet(req);
+
+ len = req->out_pos - req->out_buf;
+
+ if (close) {
+ fcgi_end_request_rec *rec = (fcgi_end_request_rec*)(req->out_pos);
+
+ fcgi_make_header(&rec->hdr, FCGI_END_REQUEST, req->id, sizeof(fcgi_end_request));
+ rec->body.appStatusB3 = 0;
+ rec->body.appStatusB2 = 0;
+ rec->body.appStatusB1 = 0;
+ rec->body.appStatusB0 = 0;
+ rec->body.protocolStatus = FCGI_REQUEST_COMPLETE;
+ len += sizeof(fcgi_end_request_rec);
+ }
+
+ if (safe_write(req, req->out_buf, len) != len) {
+ req->keep = 0;
+ return 0;
+ }
+
+ req->out_pos = req->out_buf;
+ return 1;
+}
+
+int fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len)
+{
+ int limit, rest;
+
+ if (len <= 0) {
+ return 0;
+ }
+
+ if (req->out_hdr && req->out_hdr->type != type) {
+ close_packet(req);
+ }
+#if 0
+ /* Unoptimized, but clear version */
+ rest = len;
+ while (rest > 0) {
+ limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
+
+ if (!req->out_hdr) {
+ if (limit < sizeof(fcgi_header)) {
+ if (!fcgi_flush(req, 0)) {
+ return -1;
+ }
+ }
+ open_packet(req, type);
+ }
+ limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
+ if (rest < limit) {
+ memcpy(req->out_pos, str, rest);
+ req->out_pos += rest;
+ return len;
+ } else {
+ memcpy(req->out_pos, str, limit);
+ req->out_pos += limit;
+ rest -= limit;
+ str += limit;
+ if (!fcgi_flush(req, 0)) {
+ return -1;
+ }
+ }
+ }
+#else
+ /* Optimized version */
+ limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
+ if (!req->out_hdr) {
+ limit -= sizeof(fcgi_header);
+ if (limit < 0) limit = 0;
+ }
+
+ if (len < limit) {
+ if (!req->out_hdr) {
+ open_packet(req, type);
+ }
+ memcpy(req->out_pos, str, len);
+ req->out_pos += len;
+ } else if (len - limit < sizeof(req->out_buf) - sizeof(fcgi_header)) {
+ if (!req->out_hdr) {
+ open_packet(req, type);
+ }
+ if (limit > 0) {
+ memcpy(req->out_pos, str, limit);
+ req->out_pos += limit;
+ }
+ if (!fcgi_flush(req, 0)) {
+ return -1;
+ }
+ if (len > limit) {
+ open_packet(req, type);
+ memcpy(req->out_pos, str + limit, len - limit);
+ req->out_pos += len - limit;
+ }
+ } else {
+ int pos = 0;
+ int pad;
+
+ close_packet(req);
+ while ((len - pos) > 0xffff) {
+ open_packet(req, type);
+ fcgi_make_header(req->out_hdr, type, req->id, 0xfff8);
+ req->out_hdr = NULL;
+ if (!fcgi_flush(req, 0)) {
+ return -1;
+ }
+ if (safe_write(req, str + pos, 0xfff8) != 0xfff8) {
+ req->keep = 0;
+ return -1;
+ }
+ pos += 0xfff8;
+ }
+
+ pad = (((len - pos) + 7) & ~7) - (len - pos);
+ rest = pad ? 8 - pad : 0;
+
+ open_packet(req, type);
+ fcgi_make_header(req->out_hdr, type, req->id, (len - pos) - rest);
+ req->out_hdr = NULL;
+ if (!fcgi_flush(req, 0)) {
+ return -1;
+ }
+ if (safe_write(req, str + pos, (len - pos) - rest) != (len - pos) - rest) {
+ req->keep = 0;
+ return -1;
+ }
+ if (pad) {
+ open_packet(req, type);
+ memcpy(req->out_pos, str + len - rest, rest);
+ req->out_pos += rest;
+ }
+ }
+#endif
+ return len;
+}
+
+int fcgi_finish_request(fcgi_request *req, int force_close)
+{
+ int ret = 1;
+
+ if (req->fd >= 0) {
+ if (!req->closed) {
+ ret = fcgi_flush(req, 1);
+ req->closed = 1;
+ }
+ fcgi_close(req, force_close, 1);
+ }
+ return ret;
+}
+
+char* fcgi_getenv(fcgi_request *req, const char* var, int var_len)
+{
+ unsigned int val_len;
+
+ if (!req) return NULL;
+
+ return fcgi_hash_get(&req->env, FCGI_HASH_FUNC(var, var_len), (char*)var, var_len, &val_len);
+}
+
+char* fcgi_quick_getenv(fcgi_request *req, const char* var, int var_len, unsigned int hash_value)
+{
+ unsigned int val_len;
+
+ return fcgi_hash_get(&req->env, hash_value, (char*)var, var_len, &val_len);
+}
+
+char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val)
+{
+ if (!req) return NULL;
+ if (val == NULL) {
+ fcgi_hash_del(&req->env, FCGI_HASH_FUNC(var, var_len), var, var_len);
+ return NULL;
+ } else {
+ return fcgi_hash_set(&req->env, FCGI_HASH_FUNC(var, var_len), var, var_len, val, strlen(val));
+ }
+}
+
+char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val)
+{
+ if (val == NULL) {
+ fcgi_hash_del(&req->env, hash_value, var, var_len);
+ return NULL;
+ } else {
+ return fcgi_hash_set(&req->env, hash_value, var, var_len, val, strlen(val));
+ }
+}
+
+void fcgi_loadenv(fcgi_request *req, fcgi_apply_func func, zval *array TSRMLS_DC)
+{
+ fcgi_hash_apply(&req->env, func, array TSRMLS_CC);
+}
+
+#ifdef _WIN32
+void fcgi_impersonate(void)
+{
+ char *os_name;
+
+ os_name = getenv("OS");
+ if (os_name && stricmp(os_name, "Windows_NT") == 0) {
+ is_impersonate = 1;
+ }
+}
+#endif
+
+void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len)
+{
+ zval * zvalue;
+ zvalue = pemalloc(sizeof(*zvalue), 1);
+ Z_TYPE_P(zvalue) = IS_STRING;
+ Z_STRVAL_P(zvalue) = pestrndup(value, value_len, 1);
+ Z_STRLEN_P(zvalue) = value_len;
+ zend_hash_add(&fcgi_mgmt_vars, name, name_len, &zvalue, sizeof(zvalue), NULL);
+}
+
+void fcgi_free_mgmt_var_cb(void * ptr)
+{
+ zval ** var = (zval **)ptr;
+ pefree(Z_STRVAL_PP(var), 1);
+ pefree(*var, 1);
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/cgi/fastcgi.h b/sapi/cgi/fastcgi.h
new file mode 100644
index 0000000..f1f464d
--- /dev/null
+++ b/sapi/cgi/fastcgi.h
@@ -0,0 +1,151 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+/* FastCGI protocol */
+
+#define FCGI_VERSION_1 1
+
+#define FCGI_MAX_LENGTH 0xffff
+
+#define FCGI_KEEP_CONN 1
+
+/* this is near the perfect hash function for most useful FastCGI variables
+ * which combines efficiency and minimal hash collisions
+ */
+
+#define FCGI_HASH_FUNC(var, var_len) \
+ (UNEXPECTED(var_len < 3) ? var_len : \
+ (((unsigned int)var[3]) << 2) + \
+ (((unsigned int)var[var_len-2]) << 4) + \
+ (((unsigned int)var[var_len-1]) << 2) + \
+ var_len)
+
+#define FCGI_GETENV(request, name) \
+ fcgi_quick_getenv(request, name, sizeof(name)-1, FCGI_HASH_FUNC(name, sizeof(name)-1))
+
+#define FCGI_PUTENV(request, name, value) \
+ fcgi_quick_putenv(request, name, sizeof(name)-1, FCGI_HASH_FUNC(name, sizeof(name)-1), value)
+
+typedef enum _fcgi_role {
+ FCGI_RESPONDER = 1,
+ FCGI_AUTHORIZER = 2,
+ FCGI_FILTER = 3
+} fcgi_role;
+
+typedef enum _fcgi_request_type {
+ FCGI_BEGIN_REQUEST = 1, /* [in] */
+ FCGI_ABORT_REQUEST = 2, /* [in] (not supported) */
+ FCGI_END_REQUEST = 3, /* [out] */
+ FCGI_PARAMS = 4, /* [in] environment variables */
+ FCGI_STDIN = 5, /* [in] post data */
+ FCGI_STDOUT = 6, /* [out] response */
+ FCGI_STDERR = 7, /* [out] errors */
+ FCGI_DATA = 8, /* [in] filter data (not supported) */
+ FCGI_GET_VALUES = 9, /* [in] */
+ FCGI_GET_VALUES_RESULT = 10 /* [out] */
+} fcgi_request_type;
+
+typedef enum _fcgi_protocol_status {
+ FCGI_REQUEST_COMPLETE = 0,
+ FCGI_CANT_MPX_CONN = 1,
+ FCGI_OVERLOADED = 2,
+ FCGI_UNKNOWN_ROLE = 3
+} dcgi_protocol_status;
+
+typedef struct _fcgi_header {
+ unsigned char version;
+ unsigned char type;
+ unsigned char requestIdB1;
+ unsigned char requestIdB0;
+ unsigned char contentLengthB1;
+ unsigned char contentLengthB0;
+ unsigned char paddingLength;
+ unsigned char reserved;
+} fcgi_header;
+
+typedef struct _fcgi_begin_request {
+ unsigned char roleB1;
+ unsigned char roleB0;
+ unsigned char flags;
+ unsigned char reserved[5];
+} fcgi_begin_request;
+
+typedef struct _fcgi_begin_request_rec {
+ fcgi_header hdr;
+ fcgi_begin_request body;
+} fcgi_begin_request_rec;
+
+typedef struct _fcgi_end_request {
+ unsigned char appStatusB3;
+ unsigned char appStatusB2;
+ unsigned char appStatusB1;
+ unsigned char appStatusB0;
+ unsigned char protocolStatus;
+ unsigned char reserved[3];
+} fcgi_end_request;
+
+typedef struct _fcgi_end_request_rec {
+ fcgi_header hdr;
+ fcgi_end_request body;
+} fcgi_end_request_rec;
+
+/* FastCGI client API */
+
+typedef void (*fcgi_apply_func)(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg TSRMLS_DC);
+
+typedef struct _fcgi_request fcgi_request;
+
+int fcgi_init(void);
+void fcgi_shutdown(void);
+int fcgi_is_fastcgi(void);
+int fcgi_in_shutdown(void);
+void fcgi_terminate(void);
+int fcgi_listen(const char *path, int backlog);
+fcgi_request* fcgi_init_request(int listen_socket);
+void fcgi_destroy_request(fcgi_request *req);
+int fcgi_accept_request(fcgi_request *req);
+int fcgi_finish_request(fcgi_request *req, int force_close);
+
+char* fcgi_getenv(fcgi_request *req, const char* var, int var_len);
+char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val);
+char* fcgi_quick_getenv(fcgi_request *req, const char* var, int var_len, unsigned int hash_value);
+char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val);
+void fcgi_loadenv(fcgi_request *req, fcgi_apply_func load_func, zval *array TSRMLS_DC);
+
+int fcgi_read(fcgi_request *req, char *str, int len);
+
+int fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len);
+int fcgi_flush(fcgi_request *req, int close);
+
+#ifdef PHP_WIN32
+void fcgi_impersonate(void);
+#endif
+
+void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len);
+void fcgi_free_mgmt_var_cb(void * ptr);
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/cgi/php.sym b/sapi/cgi/php.sym
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sapi/cgi/php.sym
diff --git a/sapi/cgi/tests/001.phpt b/sapi/cgi/tests/001.phpt
new file mode 100644
index 0000000..74c694f
--- /dev/null
+++ b/sapi/cgi/tests/001.phpt
@@ -0,0 +1,22 @@
+--TEST--
+version string
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+var_dump(`$php -n -v`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "PHP %s (cgi%s (built: %s
+Copyright (c) 1997-20%s The PHP Group
+Zend Engine v%s, Copyright (c) 1998-20%s Zend Technologies
+"
+Done
diff --git a/sapi/cgi/tests/002.phpt b/sapi/cgi/tests/002.phpt
new file mode 100644
index 0000000..884e652
--- /dev/null
+++ b/sapi/cgi/tests/002.phpt
@@ -0,0 +1,52 @@
+--TEST--
+defining INI options with -d
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$file = dirname(__FILE__)."/002.test.php";
+
+file_put_contents($file, '<?php var_dump(ini_get("max_execution_time")); ?>');
+
+var_dump(`$php -n -d max_execution_time=111 $file`);
+var_dump(`$php -n -d max_execution_time=500 $file`);
+var_dump(`$php -n -d max_execution_time=500 -d max_execution_time=555 $file`);
+
+file_put_contents($file, '<?php var_dump(ini_get("max_execution_time")); var_dump(ini_get("upload_tmp_dir")); ?>');
+
+var_dump(`$php -n -d upload_tmp_dir=/test/path -d max_execution_time=555 $file`);
+
+unlink($file);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html%r; charset=.*|%r
+
+%unicode|string%(3) "111"
+"
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html%r; charset=.*|%r
+
+%unicode|string%(3) "500"
+"
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html%r; charset=.*|%r
+
+%unicode|string%(3) "555"
+"
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html%r; charset=.*|%r
+
+%unicode|string%(3) "555"
+%unicode|string%(10) "/test/path"
+"
+Done
diff --git a/sapi/cgi/tests/003.phpt b/sapi/cgi/tests/003.phpt
new file mode 100644
index 0000000..5337433
--- /dev/null
+++ b/sapi/cgi/tests/003.phpt
@@ -0,0 +1,62 @@
+--TEST--
+strip comments and whitespace with -w
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$filename = dirname(__FILE__).'/003.test.php';
+$code ='
+<?php
+/* some test script */
+
+class test { /* {{{ */
+ public $var = "test"; //test var
+#perl style comment
+ private $pri; /* private attr */
+
+ function foo(/* void */) {
+ }
+}
+/* }}} */
+
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`$php -n -w "$filename"`);
+var_dump(`$php -n -w "wrong"`);
+var_dump(`echo "<?php /* comment */ class test {\n // comment \n function foo() {} } ?>" | $php -n -w`);
+
+@unlink($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html%r; charset=.*|%r
+
+
+<?php
+ class test { public $var = "test"; private $pri; function foo() { } } ?>
+"
+string(%d) "Status: 404 Not Found
+X-Powered-By: PHP/%s
+Content-type: text/html%r; charset=.*|%r
+
+No input file specified.
+"
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html%r; charset=.*|%r
+
+<?php class test { function foo() {} } ?>
+"
+Done
diff --git a/sapi/cgi/tests/004.phpt b/sapi/cgi/tests/004.phpt
new file mode 100644
index 0000000..c841b68
--- /dev/null
+++ b/sapi/cgi/tests/004.phpt
@@ -0,0 +1,43 @@
+--TEST--
+execute a file with -f
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$filename = dirname(__FILE__).'/004.test.php';
+$code ='
+<?php
+
+class test {
+ private $pri;
+}
+
+var_dump(test::$pri);
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`$php -n -f "$filename" 2>/dev/null`);
+var_dump(`$php -n -f "wrong"`);
+
+@unlink($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "
+<br />
+<b>Fatal error</b>: Cannot access private property test::$pri in <b>%s004.test.php</b> on line <b>8</b><br />
+"
+string(25) "No input file specified.
+"
+Done
diff --git a/sapi/cgi/tests/005.phpt b/sapi/cgi/tests/005.phpt
new file mode 100644
index 0000000..34a28f9
--- /dev/null
+++ b/sapi/cgi/tests/005.phpt
@@ -0,0 +1,27 @@
+--TEST--
+using invalid combinations of cmdline options
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+var_dump(`$php -n -a -f 'wrong'`);
+var_dump(`$php -n -f 'wrong' -a`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(51) "No input file specified.
+Interactive mode enabled
+
+"
+string(51) "No input file specified.
+Interactive mode enabled
+
+"
+Done
diff --git a/sapi/cgi/tests/006.phpt b/sapi/cgi/tests/006.phpt
new file mode 100644
index 0000000..a2b2b29
--- /dev/null
+++ b/sapi/cgi/tests/006.phpt
@@ -0,0 +1,62 @@
+--TEST--
+syntax check
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--INI--
+display_errors=stdout
+--FILE--
+<?php
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$filename = dirname(__FILE__)."/006.test.php";
+
+$code = '
+<?php
+
+$test = "var";
+
+class test {
+ private $var;
+}
+
+echo test::$var;
+
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`"$php" -n -l "$filename"`);
+var_dump(`"$php" -n -l some.unknown`);
+
+$code = '
+<?php
+
+class test
+ private $var;
+}
+
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`"$php" -n -l "$filename" 2>/dev/null`);
+
+@unlink($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "No syntax errors detected in %s006.test.php
+"
+string(%d) "No input file specified.
+"
+string(%d) "<br />
+<b>Parse error</b>: %s expecting %s{%s in <b>%s006.test.php</b> on line <b>5</b><br />
+Errors parsing %s006.test.php
+"
+Done
diff --git a/sapi/cgi/tests/007.phpt b/sapi/cgi/tests/007.phpt
new file mode 100644
index 0000000..f2c9c02
--- /dev/null
+++ b/sapi/cgi/tests/007.phpt
@@ -0,0 +1,22 @@
+--TEST--
+invalid arguments and error messages
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+var_dump(`"$php" -n -f some.php -f some.php`);
+var_dump(`"$php" -n -s -w -l`);
+
+?>
+===DONE===
+--EXPECTF--
+string(25) "No input file specified.
+"
+string(31) "No syntax errors detected in -
+"
+===DONE===
diff --git a/sapi/cgi/tests/008.phpt b/sapi/cgi/tests/008.phpt
new file mode 100644
index 0000000..7537664
--- /dev/null
+++ b/sapi/cgi/tests/008.phpt
@@ -0,0 +1,54 @@
+--TEST--
+syntax highlighting
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$filename = dirname(__FILE__)."/008.test.php";
+$code = '
+<?php
+$test = "var"; //var
+/* test class */
+class test {
+ private $var = array();
+
+ public static function foo(Test $arg) {
+ echo "hello";
+ var_dump($this);
+ }
+}
+
+$o = new test;
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`"$php" -n -s "$filename"`);
+var_dump(`"$php" -n -s "unknown"`);
+
+@unlink($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "X-Powered-By: PHP/%s
+Content-type: text/html%r; charset=.*|%r
+
+<code><span style="color: #000000">
+<br /><span style="color: #0000BB">&lt;?php<br />$test&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #DD0000">"var"</span><span style="color: #007700">;&nbsp;</span><span style="color: #FF8000">//var<br />/*&nbsp;test&nbsp;class&nbsp;*/<br /></span><span style="color: #007700">class&nbsp;</span><span style="color: #0000BB">test&nbsp;</span><span style="color: #007700">{<br />&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;</span><span style="color: #0000BB">$var&nbsp;</span><span style="color: #007700">=&nbsp;array();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;function&nbsp;</span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">Test&nbsp;$arg</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;</span><span style="color: #DD0000">"hello"</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$this</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /><br /></span><span style="color: #0000BB">$o&nbsp;</span><span style="color: #007700">=&nbsp;new&nbsp;</span><span style="color: #0000BB">test</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">?&gt;<br /></span>
+</span>
+</code>"
+string(%d) "Status: 404 Not Found
+X-Powered-By: PHP/%s
+Content-type: text/html%r; charset=.*|%r
+
+No input file specified.
+"
+Done
diff --git a/sapi/cgi/tests/009.phpt b/sapi/cgi/tests/009.phpt
new file mode 100644
index 0000000..c8e9ae1
--- /dev/null
+++ b/sapi/cgi/tests/009.phpt
@@ -0,0 +1,30 @@
+--TEST--
+path info request without exported PATH_INFO
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$f = tempnam(sys_get_temp_dir(), 'cgitest');
+
+putenv("TRANSLATED_PATH=".$f."/x");
+putenv("SCRIPT_FILENAME=".$f."/x");
+file_put_contents($f, '<?php var_dump($_SERVER["TRANSLATED_PATH"]); ?>');
+
+echo (`$php -n $f`);
+
+echo "Done\n";
+
+@unlink($f);
+?>
+--EXPECTF--
+X-Powered-By: PHP/%s
+Content-type: text/html%r; charset=.*|%r
+
+string(%d) "%s/x"
+Done
diff --git a/sapi/cgi/tests/010.phpt b/sapi/cgi/tests/010.phpt
new file mode 100644
index 0000000..e633ad2
--- /dev/null
+++ b/sapi/cgi/tests/010.phpt
@@ -0,0 +1,53 @@
+--TEST--
+Bug #45860 (header() function fails to correctly replace all Status lines)
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$f = tempnam(sys_get_temp_dir(), 'cgitest');
+
+putenv("TRANSLATED_PATH=".$f."/x");
+putenv("SCRIPT_FILENAME=".$f."/x");
+file_put_contents($f, '<?php
+header("HTTP/1.1 403 Forbidden");
+header("Status: 403 Also Forbidden");
+?>');
+
+echo (`$php -n $f`);
+
+file_put_contents($f, '<?php
+header("HTTP/1.1 403 Forbidden");
+?>');
+
+echo (`$php -n $f`);
+
+file_put_contents($f, '<?php
+header("Status: 403 Also Forbidden");
+?>');
+
+echo (`$php -n $f`);
+
+echo "Done\n";
+
+@unlink($f);
+?>
+--EXPECTF--
+Status: 403 Forbidden
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+Status: 403 Forbidden
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+X-Powered-By: PHP/%s
+Status: 403 Also Forbidden
+Content-type: text/html
+
+Done
diff --git a/sapi/cgi/tests/011.phpt b/sapi/cgi/tests/011.phpt
new file mode 100644
index 0000000..177df02
--- /dev/null
+++ b/sapi/cgi/tests/011.phpt
@@ -0,0 +1,165 @@
+--TEST--
+header_remove()
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$f = tempnam(sys_get_temp_dir(), 'cgitest');
+
+function test($script) {
+ file_put_contents($GLOBALS['f'], $script);
+ $cmd = escapeshellcmd($GLOBALS['php']);
+ $cmd .= ' -n -dreport_zend_debug=0 -dhtml_errors=0 ' . escapeshellarg($GLOBALS['f']);
+ echo "----------\n";
+ echo rtrim($script) . "\n";
+ echo "----------\n";
+ passthru($cmd);
+}
+
+test('<?php ?>');
+test('<?php header_remove(); ?>');
+test('<?php header_remove("X-Foo"); ?>');
+test('<?php
+header("X-Foo: Bar");
+?>');
+test('<?php
+header("X-Foo: Bar");
+header("X-Bar: Baz");
+header_remove("X-Foo");
+?>');
+test('<?php
+header("X-Foo: Bar");
+header_remove("X-Foo: Bar");
+?>');
+test('<?php
+header("X-Foo: Bar");
+header_remove("X-Foo:");
+?>');
+test('<?php
+header("X-Foo: Bar");
+header_remove();
+?>');
+test('<?php
+header_remove("");
+?>');
+test('<?php
+header_remove(":");
+?>');
+test('<?php
+header("X-Foo: Bar");
+echo "flush\n";
+flush();
+header_remove("X-Foo");
+?>');
+
+@unlink($f);
+?>
+--EXPECTF--
+----------
+<?php ?>
+----------
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+----------
+<?php header_remove(); ?>
+----------
+Content-type: text/html
+
+----------
+<?php header_remove("X-Foo"); ?>
+----------
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+----------
+<?php
+header("X-Foo: Bar");
+?>
+----------
+X-Powered-By: PHP/%s
+X-Foo: Bar
+Content-type: text/html
+
+----------
+<?php
+header("X-Foo: Bar");
+header("X-Bar: Baz");
+header_remove("X-Foo");
+?>
+----------
+X-Powered-By: PHP/%s
+X-Bar: Baz
+Content-type: text/html
+
+----------
+<?php
+header("X-Foo: Bar");
+header_remove("X-Foo: Bar");
+?>
+----------
+X-Powered-By: PHP/%s
+X-Foo: Bar
+Content-type: text/html
+
+
+Warning: Header to delete may not contain colon. in %s on line 3
+----------
+<?php
+header("X-Foo: Bar");
+header_remove("X-Foo:");
+?>
+----------
+X-Powered-By: PHP/%s
+X-Foo: Bar
+Content-type: text/html
+
+
+Warning: Header to delete may not contain colon. in %s on line 3
+----------
+<?php
+header("X-Foo: Bar");
+header_remove();
+?>
+----------
+Content-type: text/html
+
+----------
+<?php
+header_remove("");
+?>
+----------
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+----------
+<?php
+header_remove(":");
+?>
+----------
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+
+Warning: Header to delete may not contain colon. in %s on line 2
+----------
+<?php
+header("X-Foo: Bar");
+echo "flush\n";
+flush();
+header_remove("X-Foo");
+?>
+----------
+X-Powered-By: PHP/%s
+X-Foo: Bar
+Content-type: text/html
+
+flush
+
+Warning: Cannot modify header information - headers already sent by (output started at %s:3) in %s on line 5
diff --git a/sapi/cgi/tests/apache_request_headers.phpt b/sapi/cgi/tests/apache_request_headers.phpt
new file mode 100644
index 0000000..fd36e30
--- /dev/null
+++ b/sapi/cgi/tests/apache_request_headers.phpt
@@ -0,0 +1,51 @@
+--TEST--
+apache_request_headers() stack overflow.
+--INI--
+default_charset="UTF-8"
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "include.inc";
+
+$php = get_cgi_path();
+reset_env_vars();
+
+$file = dirname(__FILE__)."/012.test.php";
+
+file_put_contents($file, '<?php print_r(apache_request_headers()); ?>');
+
+passthru("$php -n $file");
+
+$names = array('HTTP_X_TEST', 'HTTP_X__TEST', 'HTTP_X_');
+foreach ($names as $name) {
+ putenv($name."=".str_repeat("A", 256));
+ passthru("$php -n -q $file");
+ putenv($name);
+}
+unlink($file);
+
+echo "Done\n";
+?>
+--EXPECTF--
+X-Powered-By: PHP/%s
+Content-type: text/%s
+
+Array
+(
+)
+Array
+(
+ [X-Test] => AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+)
+Array
+(
+ [X-_test] => AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+)
+Array
+(
+ [X-] => AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+)
+Done
diff --git a/sapi/cgi/tests/bug61605.phpt b/sapi/cgi/tests/bug61605.phpt
new file mode 100644
index 0000000..c6e4cf2
--- /dev/null
+++ b/sapi/cgi/tests/bug61605.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Bug #61605 (header_remove() does not remove all headers)
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--GET--
+foo=bar
+--FILE--
+<?php
+header("A: first");
+header("A: second", TRUE);
+$headers1 = headers_list();
+header("A: third", FALSE);
+$headers2 = headers_list();
+header_remove("A");
+$headers3 = headers_list();
+print_r($headers1);
+print_r($headers2);
+print_r($headers3);
+--EXPECTF--
+Array
+(
+ [0] => X-Powered-By: %s
+ [1] => A: second
+)
+Array
+(
+ [0] => X-Powered-By: %s
+ [1] => A: second
+ [2] => A: third
+)
+Array
+(
+ [0] => X-Powered-By: %s
+)
diff --git a/sapi/cgi/tests/include.inc b/sapi/cgi/tests/include.inc
new file mode 100644
index 0000000..2d8ed8a
--- /dev/null
+++ b/sapi/cgi/tests/include.inc
@@ -0,0 +1,57 @@
+<?php
+
+function get_cgi_path() /* {{{ */
+{
+ $php = getenv("TEST_PHP_EXECUTABLE");
+
+ $cli = false;
+ $cgi = false;
+
+ if (file_exists($php) && is_executable($php)) {
+ $version = `$php -n -v`;
+ if (strstr($version, "(cli)")) {
+ /* that's cli */
+ $cli = true;
+ } else if (strpos($version, "(cgi")) {
+ /* that's cgi */
+ return $php;
+ }
+ }
+
+ if ($cli) {
+ /* trying to guess ... */
+ $php_path = $php;
+ for ($i = 0; $i < 2; $i++) {
+ $slash_pos = strrpos($php_path, "/");
+ if ($slash_pos) {
+ $php_path = substr($php_path, 0, $slash_pos);
+ } else {
+ return FALSE;
+ }
+ }
+
+ if ($php_path && is_dir($php_path) && file_exists($php_path."/cgi/php-cgi") && is_executable($php_path."/cgi/php-cgi")) {
+ /* gotcha */
+ return $php_path."/cgi/php-cgi";
+ }
+ return false;
+ }
+ /* uhm? what's that then? */
+ return false;
+}
+/* }}} */
+
+function reset_env_vars() /* {{{ */
+{
+ putenv("REDIRECT_STATUS");
+ putenv("QUERY_STRING");
+ putenv("PATH_TRANSLATED");
+ putenv("SCRIPT_FILENAME");
+ putenv("SERVER_SOFTWARE");
+ putenv("SERVER_NAME");
+ putenv("GATEWAY_INTERFACE");
+ putenv("REQUEST_METHOD");
+}
+/* }}} */
+
+?>
diff --git a/sapi/cgi/tests/skipif.inc b/sapi/cgi/tests/skipif.inc
new file mode 100644
index 0000000..9da8b79
--- /dev/null
+++ b/sapi/cgi/tests/skipif.inc
@@ -0,0 +1,17 @@
+<?php
+
+if (substr(php_sapi_name(), 0, 3) == "cgi") {
+ exit;
+}
+
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+
+include dirname(__FILE__)."/include.inc";
+
+if (!get_cgi_path()) {
+ die("skip CGI not found");
+}
+
+?>
diff --git a/sapi/cli/CREDITS b/sapi/cli/CREDITS
new file mode 100644
index 0000000..463d20a
--- /dev/null
+++ b/sapi/cli/CREDITS
@@ -0,0 +1,2 @@
+CLI
+Edin Kadribasic, Marcus Boerger, Johannes Schlueter, Moriyoshi Koizumi, Xinchen Hui
diff --git a/sapi/cli/Makefile.frag b/sapi/cli/Makefile.frag
new file mode 100644
index 0000000..8f4f400
--- /dev/null
+++ b/sapi/cli/Makefile.frag
@@ -0,0 +1,13 @@
+cli: $(SAPI_CLI_PATH)
+
+$(SAPI_CLI_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_CLI_OBJS)
+ $(BUILD_CLI)
+
+install-cli: $(SAPI_CLI_PATH)
+ @echo "Installing PHP CLI binary: $(INSTALL_ROOT)$(bindir)/"
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(bindir)
+ @$(INSTALL) -m 0755 $(SAPI_CLI_PATH) $(INSTALL_ROOT)$(bindir)/$(program_prefix)php$(program_suffix)$(EXEEXT)
+ @echo "Installing PHP CLI man page: $(INSTALL_ROOT)$(mandir)/man1/"
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(mandir)/man1
+ @$(INSTALL_DATA) sapi/cli/php.1 $(INSTALL_ROOT)$(mandir)/man1/$(program_prefix)php$(program_suffix).1
+
diff --git a/sapi/cli/README b/sapi/cli/README
new file mode 100644
index 0000000..8720250
--- /dev/null
+++ b/sapi/cli/README
@@ -0,0 +1,20 @@
+The CLI (command line interface) SAPI has been introduced
+with a goal of making PHP better at supporting the creation of
+stand alone applications.
+
+It is based on CGI SAPI with all CGI specific things removed.
+
+The main differences between the two:
+
+* CLI is started up in quiet mode by default.
+ (-q switch kept for compatibility)
+* It does not change the working directory to that of the script.
+ (-C switch kept for compatibility)
+* Plain text error message
+* $argc and $argv registered irrespective of the register_argc_argv
+ php.ini setting.
+* implicit_flush always on
+* -r option which allows execution of PHP code directly from
+ the command line (e.g. php -r 'echo md5("test");' )
+* Other more sophisticated command line switches (see: man php)
+* max_execution_time is set to unlimited, overriding php.ini setting.
diff --git a/sapi/cli/TODO b/sapi/cli/TODO
new file mode 100644
index 0000000..22e6689
--- /dev/null
+++ b/sapi/cli/TODO
@@ -0,0 +1,2 @@
+TODO:
+
diff --git a/sapi/cli/cli.h b/sapi/cli/cli.h
new file mode 100644
index 0000000..7686522
--- /dev/null
+++ b/sapi/cli/cli.h
@@ -0,0 +1,52 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Johannes Schlueter <johannes@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef CLI_H
+#define CLI_H
+
+#ifdef PHP_WIN32
+# define PHP_CLI_API __declspec(dllexport)
+#elif defined(__GNUC__) && __GNUC__ >= 4
+# define PHP_CLI_API __attribute__ ((visibility("default")))
+#else
+# define PHP_CLI_API
+#endif
+
+
+extern PHP_CLI_API size_t sapi_cli_single_write(const char *str, uint str_length TSRMLS_DC);
+
+typedef struct {
+ size_t (*cli_shell_write)(const char *str, uint str_length TSRMLS_DC);
+ int (*cli_shell_ub_write)(const char *str, uint str_length TSRMLS_DC);
+ int (*cli_shell_run)(TSRMLS_D);
+} cli_shell_callbacks_t;
+
+extern PHP_CLI_API cli_shell_callbacks_t *php_cli_get_shell_callbacks();
+
+#endif /* CLI_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/sapi/cli/cli_win32.c b/sapi/cli/cli_win32.c
new file mode 100644
index 0000000..4407fd0
--- /dev/null
+++ b/sapi/cli/cli_win32.c
@@ -0,0 +1,2 @@
+#define PHP_CLI_WIN32_NO_CONSOLE 1
+#include "php_cli.c"
diff --git a/sapi/cli/config.m4 b/sapi/cli/config.m4
new file mode 100644
index 0000000..cdfa1f7
--- /dev/null
+++ b/sapi/cli/config.m4
@@ -0,0 +1,50 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_ENABLE(cli,,
+[ --disable-cli Disable building CLI version of PHP
+ (this forces --without-pear)], yes, no)
+
+AC_MSG_CHECKING(for CLI build)
+if test "$PHP_CLI" != "no"; then
+ PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/sapi/cli/Makefile.frag)
+
+ dnl Set filename
+ SAPI_CLI_PATH=sapi/cli/php
+
+ dnl Select SAPI
+ PHP_SELECT_SAPI(cli, program, php_cli.c php_http_parser.c php_cli_server.c,, '$(SAPI_CLI_PATH)')
+
+ case $host_alias in
+ *aix*)
+ if test "$php_sapi_module" = "shared"; then
+ BUILD_CLI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/.libs\/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)"
+ else
+ BUILD_CLI="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)"
+ fi
+ ;;
+ *darwin*)
+ BUILD_CLI="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_CLI_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)"
+ ;;
+ *netware*)
+ BUILD_CLI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -Lnetware -lphp5lib -o \$(SAPI_CLI_PATH)"
+ ;;
+ *)
+ BUILD_CLI="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_CLI_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_CLI_PATH)"
+ ;;
+ esac
+
+ dnl Set executable for tests
+ PHP_EXECUTABLE="\$(top_builddir)/\$(SAPI_CLI_PATH)"
+ PHP_SUBST(PHP_EXECUTABLE)
+
+ dnl Expose to Makefile
+ PHP_SUBST(SAPI_CLI_PATH)
+ PHP_SUBST(BUILD_CLI)
+
+ PHP_OUTPUT(sapi/cli/php.1)
+
+ PHP_INSTALL_HEADERS([sapi/cli/cli.h])
+fi
+AC_MSG_RESULT($PHP_CLI)
diff --git a/sapi/cli/config.w32 b/sapi/cli/config.w32
new file mode 100644
index 0000000..4d0dad5
--- /dev/null
+++ b/sapi/cli/config.w32
@@ -0,0 +1,21 @@
+// vim:ft=javascript
+// $Id$
+
+ARG_ENABLE('cli', 'Build CLI version of PHP', 'yes');
+ARG_ENABLE('crt-debug', 'Enable CRT memory dumps for debugging sent to STDERR', 'no');
+ARG_ENABLE('cli-win32', 'Build console-less CLI version of PHP', 'no');
+
+if (PHP_CLI == "yes") {
+ SAPI('cli', 'php_cli.c php_http_parser.c php_cli_server.c', 'php.exe');
+ ADD_FLAG("LIBS_CLI", "ws2_32.lib");
+ if (PHP_CRT_DEBUG == "yes") {
+ ADD_FLAG("CFLAGS_CLI", "/D PHP_WIN32_DEBUG_HEAP");
+ }
+ ADD_FLAG("LDFLAGS_CLI", "/stack:8388608");
+}
+
+if (PHP_CLI_WIN32 == "yes") {
+ SAPI('cli_win32', 'cli_win32.c', 'php-win.exe');
+ ADD_FLAG("LDFLAGS_CLI_WIN32", "/stack:8388608");
+}
+
diff --git a/sapi/cli/php.1.in b/sapi/cli/php.1.in
new file mode 100644
index 0000000..186b128
--- /dev/null
+++ b/sapi/cli/php.1.in
@@ -0,0 +1,451 @@
+.TH PHP 1 "2010" "The PHP Group" "Scripting Language"
+.SH NAME
+php \- PHP Command Line Interface 'CLI'
+.SH SYNOPSIS
+.B php
+[options] [
+.B \-f\fP ]
+.IR file
+[[\-\-]
+.IR args.\|.\|. ]
+.LP
+.B php
+[options]
+.B \-r
+.IR code
+[[\-\-]
+.IR args.\|.\|. ]
+.LP
+.B php
+[options] [\-B
+.IR code ]
+.B \-R
+.IR code
+[\-E
+.IR code ]
+[[\-\-]
+.IR args.\|.\|. ]
+.LP
+.B php
+[options] [\-B
+.IR code ]
+.B \-F
+.IR file
+[\-E
+.IR code ]
+[[\-\-]
+.IR args.\|.\|. ]
+.LP
+.B php
+[options] \-\- [
+.IR args.\|.\|. ]
+.LP
+\fBphp \fP[options] \fB\-a\fP
+.LP
+.SH DESCRIPTION
+\fBPHP\fP is a widely\-used general\-purpose scripting language that is especially suited for
+Web development and can be embedded into HTML. This is the command line interface
+that enables you to do the following:
+.P
+You can parse and execute files by using parameter \-f followed by the name of the
+.IR file
+to be executed.
+.LP
+Using parameter \-r you can directly execute PHP
+.IR code
+simply as you would do inside a
+.B \.php
+file when using the
+.B eval()
+function.
+.LP
+It is also possible to process the standard input line by line using either
+the parameter \-R or \-F. In this mode each separate input line causes the
+.IR code
+specified by \-R or the
+.IR file
+specified by \-F to be executed.
+You can access the input line by \fB$argn\fP. While processing the input lines
+.B $argi
+contains the number of the actual line being processed. Further more
+the parameters \-B and \-E can be used to execute
+.IR code
+(see \-r) before and
+after all input lines have been processed respectively. Notice that the
+input is read from
+.B STDIN
+and therefore reading from
+.B STDIN
+explicitly changes the next input line or skips input lines.
+.LP
+If none of \-r \-f \-B \-R \-F or \-E is present but a single parameter is given
+then this parameter is taken as the filename to parse and execute (same as
+with \-f). If no parameter is present then the standard input is read and
+executed.
+.SH OPTIONS
+.TP 15
+.PD 0
+.B \-\-interactive
+.TP
+.PD 1
+.B \-a
+Run PHP interactively. This lets you enter snippets of PHP code that directly
+get executed. When readline support is enabled you can edit the lines and also
+have history support.
+.TP
+.PD 0
+.B \-\-bindpath \fIaddress:port\fP|\fIport\fP
+.TP
+.PD 1
+.B \-b \fIaddress:port\fP|\fIport\fP
+Bind Path for external FASTCGI Server mode (CGI only).
+.TP
+.PD 0
+.B \-\-no\-chdir
+.TP
+.PD 1
+.B \-C
+Do not chdir to the script's directory (CGI only).
+.TP
+.PD 0
+.B \-\-no\-header
+.TP
+.PD 1
+.B \-q
+Quiet-mode. Suppress HTTP header output (CGI only).
+.TP
+.PD 0
+.B \-\-timing \fIcount\fP
+.TP
+.PD 1
+.B \-T \fIcount\fP
+Measure execution time of script repeated count times (CGI only).
+.TP
+.PD 0
+.B \-\-php\-ini \fIpath\fP|\fIfile\fP
+.TP
+.PD 1
+.B \-c \fIpath\fP|\fIfile\fP
+Look for
+.B php.ini
+file in the directory
+.IR path
+or use the specified
+.IR file
+.TP
+.PD 0
+.B \-\-no\-php\-ini
+.TP
+.PD 1
+.B \-n
+No
+.B php.ini
+file will be used
+.TP
+.PD 0
+.B \-\-define \fIfoo\fP[=\fIbar\fP]
+.TP
+.PD 1
+.B \-d \fIfoo\fP[=\fIbar\fP]
+Define INI entry
+.IR foo
+with value
+.IR bar
+.TP
+.B \-e
+Generate extended information for debugger/profiler
+.TP
+.PD 0
+.B \-\-file \fIfile\fP
+.TP
+.PD 1
+.B \-f \fIfile\fP
+Parse and execute
+.IR file
+.TP
+.PD 0
+.B \-\-global \fIname\fP
+.TP
+.PD 1
+.B \-g \fIname\fP
+Make variable
+.IR name
+global in script.
+.TP
+.PD 0
+.B \-\-help
+.TP
+.PD 1
+.B \-h
+This help
+.TP
+.PD 0
+.B \-\-hide\-args
+.TP
+.PD 1
+.B \-H
+Hide script name (\fIfile\fP) and parameters (\fIargs\.\.\.\fP) from external
+tools. For example you may want to use this when a php script is started as
+a daemon and the command line contains sensitive data such as passwords.
+.TP
+.PD 0
+.B \-\-info
+.TP
+.PD 1
+.B \-i
+PHP information and configuration
+.TP
+.PD 0
+.B \-\-syntax\-check
+.TP
+.PD 1
+.B \-l
+Syntax check only (lint)
+.TP
+.PD 0
+.B \-\-modules
+.TP
+.PD 1
+.B \-m
+Show compiled in modules
+.TP
+.PD 0
+.B \-\-run \fIcode\fP
+.TP
+.PD 1
+.B \-r \fIcode\fP
+Run PHP
+.IR code
+without using script tags
+.B '<?..?>'
+.TP
+.PD 0
+.B \-\-process\-begin \fIcode\fP
+.TP
+.PD 1
+.B \-B \fIcode\fP
+Run PHP
+.IR code
+before processing input lines
+.TP
+.PD 0
+.B \-\-process\-code \fIcode\fP
+.TP
+.PD 1
+.B \-R \fIcode\fP
+Run PHP
+.IR code
+for every input line
+.TP
+.PD 0
+.B \-\-process\-file \fIfile\fP
+.TP
+.PD 1
+.B \-F \fIfile\fP
+Parse and execute
+.IR file
+for every input line
+.TP
+.PD 0
+.B \-\-process\-end \fIcode\fP
+.TP
+.PD 1
+.B \-E \fIcode\fP
+Run PHP
+.IR code
+after processing all input lines
+.TP
+.PD 0
+.B \-\-syntax\-highlight
+.TP
+.PD 1
+.B \-s
+Output HTML syntax highlighted source
+.TP
+.PD 0
+.B \-\-version
+.TP
+.PD 1
+.B \-v
+Version number
+.TP
+.PD 0
+.B \-\-stripped
+.TP
+.PD 1
+.B \-w
+Output source with stripped comments and whitespace
+.TP
+.PD 0
+.B \-\-zend\-extension \fIfile\fP
+.TP
+.PD 1
+.B \-z \fIfile\fP
+Load Zend extension
+.IR file
+.TP
+.IR args.\|.\|.
+Arguments passed to script. Use
+.B '\-\-'
+.IR args
+when first argument starts with
+.B '\-'
+or script is read from stdin
+.TP
+.PD 0
+.B \-\-rfunction
+.IR name
+.TP
+.PD 1
+.B \-\-rf
+.IR name
+Shows information about function
+.B name
+.TP
+.PD 0
+.B \-\-rclass
+.IR name
+.TP
+.PD 1
+.B \-\-rc
+.IR name
+Shows information about class
+.B name
+.TP
+.PD 0
+.B \-\-rextension
+.IR name
+.TP
+.PD 1
+.B \-\-re
+.IR name
+Shows information about extension
+.B name
+.TP
+.PD 0
+.B \-\-rzendextension
+.IR name
+.TP
+.PD 1
+.B \-\-rz
+.IR name
+Shows information about Zend extension
+.B name
+.TP
+.PD 0
+.B \-\-rextinfo
+.IR name
+.TP
+.PD 1
+.B \-\-ri
+.IR name
+Shows configuration for extension
+.B name
+.TP
+.B \-\-ini
+Show configuration file names
+.SH FILES
+.TP 15
+.B php\-cli.ini
+The configuration file for the CLI version of PHP.
+.TP
+.B php.ini
+The standard configuration file will only be used when
+.B php\-cli.ini
+cannot be found.
+.SH EXAMPLES
+.TP 5
+\fIphp \-r 'echo "Hello World\\n";'\fP
+This command simply writes the text "Hello World" to standard out.
+.TP
+\fIphp \-r 'print_r(gd_info());'\fP
+This shows the configuration of your gd extension. You can use this
+to easily check which image formats you can use. If you have any
+dynamic modules you may want to use the same ini file that php uses
+when executed from your webserver. There are more extensions which
+have such a function. For dba use:
+.RS
+\fIphp \-r 'print_r(dba_handlers(1));'\fP
+.RE
+.TP
+\fIphp \-R 'echo strip_tags($argn)."\\n";'\fP
+This PHP command strips off the HTML tags line by line and outputs the
+result. To see how it works you can first look at the following PHP command
+\'\fIphp \-d html_errors=1 \-i\fP\' which uses PHP to output HTML formatted
+configuration information. If you then combine those two
+\'\fIphp \.\.\.|php \.\.\.\fP\' you'll see what happens.
+.TP
+\fIphp \-E 'echo "Lines: $argi\\n";'\fP
+Using this PHP command you can count the lines being input.
+.TP
+\fIphp \-R '@$l+=count(file($argn));' \-E 'echo "Lines:$l\\n";'\fP
+In this example PHP expects each input line being a file. It counts all lines
+of the files specified by each input line and shows the summarized result.
+You may combine this with tools like find and change the php scriptlet.
+.TP
+\fIphp \-R 'echo "$argn\\n"; fgets(STDIN);'\fP
+Since you have access to STDIN from within \-B \-R \-F and \-E you can skip
+certain input lines with your code. But note that in such cases $argi only
+counts the lines being processed by php itself. Having read this you will
+guess what the above program does: skipping every second input line.
+.SH TIPS
+You can use a shebang line to automatically invoke php
+from scripts. Only the CLI version of PHP will ignore
+such a first line as shown below:
+.P
+.PD 0
+.RS
+#!/bin/php
+.P
+<?php
+.P
+ // your script
+.P
+?>
+.RE
+.PD 1
+.P
+.SH SEE ALSO
+For a more or less complete description of PHP look here:
+.PD 0
+.P
+.B http://www.php.net/manual/
+.PD 1
+.P
+.SH BUGS
+You can view the list of known bugs or report any new bug you
+found at:
+.PD 0
+.P
+.B http://bugs.php.net
+.PD 1
+.SH AUTHORS
+The PHP Group: Thies C. Arntzen, Stig Bakken, Andi Gutmans, Rasmus Lerdorf, Sam Ruby, Sascha Schumann, Zeev Suraski, Jim Winstead, Andrei Zmievski.
+.P
+Additional work for the CLI sapi was done by Edin Kadribasic, Marcus Boerger and Johannes Schlueter.
+.P
+A List of active developers can be found here:
+.PD 0
+.P
+.B http://www.php.net/credits.php
+.PD 1
+.P
+And last but not least PHP was developed with the help of a huge amount of
+contributors all around the world.
+.SH VERSION INFORMATION
+This manpage describes \fBphp\fP, version @PHP_VERSION@.
+.SH COPYRIGHT
+Copyright \(co 1997\-2010 The PHP Group
+.LP
+This source file is subject to version 3.01 of the PHP license,
+that is bundled with this package in the file LICENSE, and is
+available through the world-wide-web at the following url:
+.PD 0
+.P
+.B http://www.php.net/license/3_01.txt
+.PD 1
+.P
+If you did not receive a copy of the PHP license and are unable to
+obtain it through the world-wide-web, please send a note to
+.B license@php.net
+so we can mail you a copy immediately.
diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c
new file mode 100644
index 0000000..c01f370
--- /dev/null
+++ b/sapi/cli/php_cli.c
@@ -0,0 +1,1399 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Edin Kadribasic <edink@php.net> |
+ | Marcus Boerger <helly@php.net> |
+ | Johannes Schlueter <johannes@php.net> |
+ | Parts based on CGI SAPI Module by |
+ | Rasmus Lerdorf, Stig Bakken and Zeev Suraski |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "php.h"
+#include "php_globals.h"
+#include "php_variables.h"
+#include "zend_hash.h"
+#include "zend_modules.h"
+#include "zend_interfaces.h"
+
+#include "ext/reflection/php_reflection.h"
+
+#include "SAPI.h"
+
+#include <stdio.h>
+#include "php.h"
+#ifdef PHP_WIN32
+#include "win32/time.h"
+#include "win32/signal.h"
+#include <process.h>
+#endif
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#if HAVE_SETLOCALE
+#include <locale.h>
+#endif
+#include "zend.h"
+#include "zend_extensions.h"
+#include "php_ini.h"
+#include "php_globals.h"
+#include "php_main.h"
+#include "fopen_wrappers.h"
+#include "ext/standard/php_standard.h"
+#include "cli.h"
+#ifdef PHP_WIN32
+#include <io.h>
+#include <fcntl.h>
+#include "win32/php_registry.h"
+#endif
+
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef __riscos__
+#include <unixlib/local.h>
+#endif
+
+#include "zend_compile.h"
+#include "zend_execute.h"
+#include "zend_highlight.h"
+#include "zend_indent.h"
+#include "zend_exceptions.h"
+
+#include "php_getopt.h"
+
+#ifndef PHP_CLI_WIN32_NO_CONSOLE
+#include "php_cli_server.h"
+#endif
+
+#ifndef PHP_WIN32
+# define php_select(m, r, w, e, t) select(m, r, w, e, t)
+#else
+# include "win32/select.h"
+#endif
+
+PHPAPI extern char *php_ini_opened_path;
+PHPAPI extern char *php_ini_scanned_path;
+PHPAPI extern char *php_ini_scanned_files;
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#define PHP_MODE_STANDARD 1
+#define PHP_MODE_HIGHLIGHT 2
+#define PHP_MODE_INDENT 3
+#define PHP_MODE_LINT 4
+#define PHP_MODE_STRIP 5
+#define PHP_MODE_CLI_DIRECT 6
+#define PHP_MODE_PROCESS_STDIN 7
+#define PHP_MODE_REFLECTION_FUNCTION 8
+#define PHP_MODE_REFLECTION_CLASS 9
+#define PHP_MODE_REFLECTION_EXTENSION 10
+#define PHP_MODE_REFLECTION_EXT_INFO 11
+#define PHP_MODE_REFLECTION_ZEND_EXTENSION 12
+#define PHP_MODE_SHOW_INI_CONFIG 13
+
+cli_shell_callbacks_t cli_shell_callbacks = { NULL, NULL, NULL };
+PHP_CLI_API cli_shell_callbacks_t *php_cli_get_shell_callbacks()
+{
+ return &cli_shell_callbacks;
+}
+
+const char HARDCODED_INI[] =
+ "html_errors=0\n"
+ "register_argc_argv=1\n"
+ "implicit_flush=1\n"
+ "output_buffering=0\n"
+ "max_execution_time=0\n"
+ "max_input_time=-1\n\0";
+
+
+const opt_struct OPTIONS[] = {
+ {'a', 0, "interactive"},
+ {'B', 1, "process-begin"},
+ {'C', 0, "no-chdir"}, /* for compatibility with CGI (do not chdir to script directory) */
+ {'c', 1, "php-ini"},
+ {'d', 1, "define"},
+ {'E', 1, "process-end"},
+ {'e', 0, "profile-info"},
+ {'F', 1, "process-file"},
+ {'f', 1, "file"},
+ {'h', 0, "help"},
+ {'i', 0, "info"},
+ {'l', 0, "syntax-check"},
+ {'m', 0, "modules"},
+ {'n', 0, "no-php-ini"},
+ {'q', 0, "no-header"}, /* for compatibility with CGI (do not generate HTTP headers) */
+ {'R', 1, "process-code"},
+ {'H', 0, "hide-args"},
+ {'r', 1, "run"},
+ {'s', 0, "syntax-highlight"},
+ {'s', 0, "syntax-highlighting"},
+ {'S', 1, "server"},
+ {'t', 1, "docroot"},
+ {'w', 0, "strip"},
+ {'?', 0, "usage"},/* help alias (both '?' and 'usage') */
+ {'v', 0, "version"},
+ {'z', 1, "zend-extension"},
+ {10, 1, "rf"},
+ {10, 1, "rfunction"},
+ {11, 1, "rc"},
+ {11, 1, "rclass"},
+ {12, 1, "re"},
+ {12, 1, "rextension"},
+ {13, 1, "rz"},
+ {13, 1, "rzendextension"},
+ {14, 1, "ri"},
+ {14, 1, "rextinfo"},
+ {15, 0, "ini"},
+ {'-', 0, NULL} /* end of args */
+};
+
+static int print_module_info(zend_module_entry *module TSRMLS_DC) /* {{{ */
+{
+ php_printf("%s\n", module->name);
+ return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+
+static int module_name_cmp(const void *a, const void *b TSRMLS_DC) /* {{{ */
+{
+ Bucket *f = *((Bucket **) a);
+ Bucket *s = *((Bucket **) b);
+
+ return strcasecmp(((zend_module_entry *)f->pData)->name,
+ ((zend_module_entry *)s->pData)->name);
+}
+/* }}} */
+
+static void print_modules(TSRMLS_D) /* {{{ */
+{
+ HashTable sorted_registry;
+ zend_module_entry tmp;
+
+ zend_hash_init(&sorted_registry, 50, NULL, NULL, 1);
+ zend_hash_copy(&sorted_registry, &module_registry, NULL, &tmp, sizeof(zend_module_entry));
+ zend_hash_sort(&sorted_registry, zend_qsort, module_name_cmp, 0 TSRMLS_CC);
+ zend_hash_apply(&sorted_registry, (apply_func_t) print_module_info TSRMLS_CC);
+ zend_hash_destroy(&sorted_registry);
+}
+/* }}} */
+
+static int print_extension_info(zend_extension *ext, void *arg TSRMLS_DC) /* {{{ */
+{
+ php_printf("%s\n", ext->name);
+ return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+
+static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s TSRMLS_DC) /* {{{ */
+{
+ return strcmp(((zend_extension *)(*f)->data)->name,
+ ((zend_extension *)(*s)->data)->name);
+}
+/* }}} */
+
+static void print_extensions(TSRMLS_D) /* {{{ */
+{
+ zend_llist sorted_exts;
+
+ zend_llist_copy(&sorted_exts, &zend_extensions);
+ sorted_exts.dtor = NULL;
+ zend_llist_sort(&sorted_exts, extension_name_cmp TSRMLS_CC);
+ zend_llist_apply(&sorted_exts, (llist_apply_func_t) print_extension_info TSRMLS_CC);
+ zend_llist_destroy(&sorted_exts);
+}
+/* }}} */
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+
+static inline int sapi_cli_select(int fd TSRMLS_DC)
+{
+ fd_set wfd, dfd;
+ struct timeval tv;
+ int ret;
+
+ FD_ZERO(&wfd);
+ FD_ZERO(&dfd);
+
+ PHP_SAFE_FD_SET(fd, &wfd);
+
+ tv.tv_sec = FG(default_socket_timeout);
+ tv.tv_usec = 0;
+
+ ret = php_select(fd+1, &dfd, &wfd, &dfd, &tv);
+
+ return ret != -1;
+}
+
+PHP_CLI_API size_t sapi_cli_single_write(const char *str, uint str_length TSRMLS_DC) /* {{{ */
+{
+#ifdef PHP_WRITE_STDOUT
+ long ret;
+#else
+ size_t ret;
+#endif
+
+ if (cli_shell_callbacks.cli_shell_write) {
+ size_t shell_wrote;
+ shell_wrote = cli_shell_callbacks.cli_shell_write(str, str_length TSRMLS_CC);
+ if (shell_wrote > -1) {
+ return shell_wrote;
+ }
+ }
+
+#ifdef PHP_WRITE_STDOUT
+ do {
+ ret = write(STDOUT_FILENO, str, str_length);
+ } while (ret <= 0 && errno == EAGAIN && sapi_cli_select(STDOUT_FILENO TSRMLS_CC));
+
+ if (ret <= 0) {
+ return 0;
+ }
+
+ return ret;
+#else
+ ret = fwrite(str, 1, MIN(str_length, 16384), stdout);
+ return ret;
+#endif
+}
+/* }}} */
+
+static int sapi_cli_ub_write(const char *str, uint str_length TSRMLS_DC) /* {{{ */
+{
+ const char *ptr = str;
+ uint remaining = str_length;
+ size_t ret;
+
+ if (!str_length) {
+ return 0;
+ }
+
+ if (cli_shell_callbacks.cli_shell_ub_write) {
+ int ub_wrote;
+ ub_wrote = cli_shell_callbacks.cli_shell_ub_write(str, str_length TSRMLS_CC);
+ if (ub_wrote > -1) {
+ return ub_wrote;
+ }
+ }
+
+ while (remaining > 0)
+ {
+ ret = sapi_cli_single_write(ptr, remaining TSRMLS_CC);
+ if (!ret) {
+#ifndef PHP_CLI_WIN32_NO_CONSOLE
+ php_handle_aborted_connection();
+#endif
+ break;
+ }
+ ptr += ret;
+ remaining -= ret;
+ }
+
+ return (ptr - str);
+}
+/* }}} */
+
+static void sapi_cli_flush(void *server_context) /* {{{ */
+{
+ /* Ignore EBADF here, it's caused by the fact that STDIN/STDOUT/STDERR streams
+ * are/could be closed before fflush() is called.
+ */
+ if (fflush(stdout)==EOF && errno!=EBADF) {
+#ifndef PHP_CLI_WIN32_NO_CONSOLE
+ php_handle_aborted_connection();
+#endif
+ }
+}
+/* }}} */
+
+static char *php_self = "";
+static char *script_filename = "";
+
+static void sapi_cli_register_variables(zval *track_vars_array TSRMLS_DC) /* {{{ */
+{
+ unsigned int len;
+ char *docroot = "";
+
+ /* In CGI mode, we consider the environment to be a part of the server
+ * variables
+ */
+ php_import_environment_variables(track_vars_array TSRMLS_CC);
+
+ /* Build the special-case PHP_SELF variable for the CLI version */
+ len = strlen(php_self);
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, len, &len TSRMLS_CC)) {
+ php_register_variable("PHP_SELF", php_self, track_vars_array TSRMLS_CC);
+ }
+ if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME", &php_self, len, &len TSRMLS_CC)) {
+ php_register_variable("SCRIPT_NAME", php_self, track_vars_array TSRMLS_CC);
+ }
+ /* filenames are empty for stdin */
+ len = strlen(script_filename);
+ if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME", &script_filename, len, &len TSRMLS_CC)) {
+ php_register_variable("SCRIPT_FILENAME", script_filename, track_vars_array TSRMLS_CC);
+ }
+ if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED", &script_filename, len, &len TSRMLS_CC)) {
+ php_register_variable("PATH_TRANSLATED", script_filename, track_vars_array TSRMLS_CC);
+ }
+ /* just make it available */
+ len = 0U;
+ if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT", &docroot, len, &len TSRMLS_CC)) {
+ php_register_variable("DOCUMENT_ROOT", docroot, track_vars_array TSRMLS_CC);
+ }
+}
+/* }}} */
+
+static void sapi_cli_log_message(char *message TSRMLS_DC) /* {{{ */
+{
+ fprintf(stderr, "%s\n", message);
+}
+/* }}} */
+
+static int sapi_cli_deactivate(TSRMLS_D) /* {{{ */
+{
+ fflush(stdout);
+ if(SG(request_info).argv0) {
+ free(SG(request_info).argv0);
+ SG(request_info).argv0 = NULL;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+static char* sapi_cli_read_cookies(TSRMLS_D) /* {{{ */
+{
+ return NULL;
+}
+/* }}} */
+
+static int sapi_cli_header_handler(sapi_header_struct *h, sapi_header_op_enum op, sapi_headers_struct *s TSRMLS_DC) /* {{{ */
+{
+ return 0;
+}
+/* }}} */
+
+static int sapi_cli_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */
+{
+ /* We do nothing here, this function is needed to prevent that the fallback
+ * header handling is called. */
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+/* }}} */
+
+static void sapi_cli_send_header(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC) /* {{{ */
+{
+}
+/* }}} */
+
+static int php_cli_startup(sapi_module_struct *sapi_module) /* {{{ */
+{
+ if (php_module_startup(sapi_module, NULL, 0)==FAILURE) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ sapi_cli_ini_defaults */
+
+/* overwriteable ini defaults must be set in sapi_cli_ini_defaults() */
+#define INI_DEFAULT(name,value)\
+ Z_SET_REFCOUNT(tmp, 0);\
+ Z_UNSET_ISREF(tmp); \
+ ZVAL_STRINGL(&tmp, zend_strndup(value, sizeof(value)-1), sizeof(value)-1, 0);\
+ zend_hash_update(configuration_hash, name, sizeof(name), &tmp, sizeof(zval), NULL);\
+
+static void sapi_cli_ini_defaults(HashTable *configuration_hash)
+{
+ zval tmp;
+ INI_DEFAULT("report_zend_debug", "0");
+ INI_DEFAULT("display_errors", "1");
+}
+/* }}} */
+
+/* {{{ sapi_module_struct cli_sapi_module
+ */
+static sapi_module_struct cli_sapi_module = {
+ "cli", /* name */
+ "Command Line Interface", /* pretty name */
+
+ php_cli_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ sapi_cli_deactivate, /* deactivate */
+
+ sapi_cli_ub_write, /* unbuffered write */
+ sapi_cli_flush, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ sapi_cli_header_handler, /* header handler */
+ sapi_cli_send_headers, /* send headers handler */
+ sapi_cli_send_header, /* send header handler */
+
+ NULL, /* read POST data */
+ sapi_cli_read_cookies, /* read Cookies */
+
+ sapi_cli_register_variables, /* register server variables */
+ sapi_cli_log_message, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+/* }}} */
+
+/* {{{ arginfo ext/standard/dl.c */
+ZEND_BEGIN_ARG_INFO(arginfo_dl, 0)
+ ZEND_ARG_INFO(0, extension_filename)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+static const zend_function_entry additional_functions[] = {
+ ZEND_FE(dl, arginfo_dl)
+ {NULL, NULL, NULL}
+};
+
+/* {{{ php_cli_usage
+ */
+static void php_cli_usage(char *argv0)
+{
+ char *prog;
+
+ prog = strrchr(argv0, '/');
+ if (prog) {
+ prog++;
+ } else {
+ prog = "php";
+ }
+
+ printf( "Usage: %s [options] [-f] <file> [--] [args...]\n"
+ " %s [options] -r <code> [--] [args...]\n"
+ " %s [options] [-B <begin_code>] -R <code> [-E <end_code>] [--] [args...]\n"
+ " %s [options] [-B <begin_code>] -F <file> [-E <end_code>] [--] [args...]\n"
+ " %s [options] -- [args...]\n"
+ " %s [options] -a\n"
+ "\n"
+#if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE)
+ " -a Run as interactive shell\n"
+#else
+ " -a Run interactively\n"
+#endif
+ " -c <path>|<file> Look for php.ini file in this directory\n"
+ " -n No php.ini file will be used\n"
+ " -d foo[=bar] Define INI entry foo with value 'bar'\n"
+ " -e Generate extended information for debugger/profiler\n"
+ " -f <file> Parse and execute <file>.\n"
+ " -h This help\n"
+ " -i PHP information\n"
+ " -l Syntax check only (lint)\n"
+ " -m Show compiled in modules\n"
+ " -r <code> Run PHP <code> without using script tags <?..?>\n"
+ " -B <begin_code> Run PHP <begin_code> before processing input lines\n"
+ " -R <code> Run PHP <code> for every input line\n"
+ " -F <file> Parse and execute <file> for every input line\n"
+ " -E <end_code> Run PHP <end_code> after processing all input lines\n"
+ " -H Hide any passed arguments from external tools.\n"
+ " -S <addr>:<port> Run with built-in web server.\n"
+ " -t <docroot> Specify document root <docroot> for built-in web server.\n"
+ " -s Output HTML syntax highlighted source.\n"
+ " -v Version number\n"
+ " -w Output source with stripped comments and whitespace.\n"
+ " -z <file> Load Zend extension <file>.\n"
+ "\n"
+ " args... Arguments passed to script. Use -- args when first argument\n"
+ " starts with - or script is read from stdin\n"
+ "\n"
+ " --ini Show configuration file names\n"
+ "\n"
+ " --rf <name> Show information about function <name>.\n"
+ " --rc <name> Show information about class <name>.\n"
+ " --re <name> Show information about extension <name>.\n"
+ " --rz <name> Show information about Zend extension <name>.\n"
+ " --ri <name> Show configuration for extension <name>.\n"
+ "\n"
+ , prog, prog, prog, prog, prog, prog);
+}
+/* }}} */
+
+static php_stream *s_in_process = NULL;
+
+static void cli_register_file_handles(TSRMLS_D) /* {{{ */
+{
+ zval *zin, *zout, *zerr;
+ php_stream *s_in, *s_out, *s_err;
+ php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL;
+ zend_constant ic, oc, ec;
+
+ MAKE_STD_ZVAL(zin);
+ MAKE_STD_ZVAL(zout);
+ MAKE_STD_ZVAL(zerr);
+
+ s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in);
+ s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out);
+ s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err);
+
+ if (s_in==NULL || s_out==NULL || s_err==NULL) {
+ FREE_ZVAL(zin);
+ FREE_ZVAL(zout);
+ FREE_ZVAL(zerr);
+ if (s_in) php_stream_close(s_in);
+ if (s_out) php_stream_close(s_out);
+ if (s_err) php_stream_close(s_err);
+ return;
+ }
+
+#if PHP_DEBUG
+ /* do not close stdout and stderr */
+ s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE;
+ s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE;
+#endif
+
+ s_in_process = s_in;
+
+ php_stream_to_zval(s_in, zin);
+ php_stream_to_zval(s_out, zout);
+ php_stream_to_zval(s_err, zerr);
+
+ ic.value = *zin;
+ ic.flags = CONST_CS;
+ ic.name = zend_strndup(ZEND_STRL("STDIN"));
+ ic.name_len = sizeof("STDIN");
+ ic.module_number = 0;
+ zend_register_constant(&ic TSRMLS_CC);
+
+ oc.value = *zout;
+ oc.flags = CONST_CS;
+ oc.name = zend_strndup(ZEND_STRL("STDOUT"));
+ oc.name_len = sizeof("STDOUT");
+ oc.module_number = 0;
+ zend_register_constant(&oc TSRMLS_CC);
+
+ ec.value = *zerr;
+ ec.flags = CONST_CS;
+ ec.name = zend_strndup(ZEND_STRL("STDERR"));
+ ec.name_len = sizeof("STDERR");
+ ec.module_number = 0;
+ zend_register_constant(&ec TSRMLS_CC);
+
+ FREE_ZVAL(zin);
+ FREE_ZVAL(zout);
+ FREE_ZVAL(zerr);
+}
+/* }}} */
+
+static const char *param_mode_conflict = "Either execute direct code, process stdin or use a file.\n";
+
+/* {{{ cli_seek_file_begin
+ */
+static int cli_seek_file_begin(zend_file_handle *file_handle, char *script_file, int *lineno TSRMLS_DC)
+{
+ int c;
+
+ *lineno = 1;
+
+ file_handle->type = ZEND_HANDLE_FP;
+ file_handle->opened_path = NULL;
+ file_handle->free_filename = 0;
+ if (!(file_handle->handle.fp = VCWD_FOPEN(script_file, "rb"))) {
+ php_printf("Could not open input file: %s\n", script_file);
+ return FAILURE;
+ }
+ file_handle->filename = script_file;
+
+ /* #!php support */
+ c = fgetc(file_handle->handle.fp);
+ if (c == '#' && (c = fgetc(file_handle->handle.fp)) == '!') {
+ while (c != '\n' && c != '\r' && c != EOF) {
+ c = fgetc(file_handle->handle.fp); /* skip to end of line */
+ }
+ /* handle situations where line is terminated by \r\n */
+ if (c == '\r') {
+ if (fgetc(file_handle->handle.fp) != '\n') {
+ long pos = ftell(file_handle->handle.fp);
+ fseek(file_handle->handle.fp, pos - 1, SEEK_SET);
+ }
+ }
+ *lineno = 2;
+ } else {
+ rewind(file_handle->handle.fp);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+static int do_cli(int argc, char **argv TSRMLS_DC) /* {{{ */
+{
+ int c;
+ zend_file_handle file_handle;
+ int behavior = PHP_MODE_STANDARD;
+ char *reflection_what = NULL;
+ volatile int request_started = 0;
+ volatile int exit_status = 0;
+ char *php_optarg = NULL, *orig_optarg = NULL;
+ int php_optind = 1, orig_optind = 1;
+ char *exec_direct=NULL, *exec_run=NULL, *exec_begin=NULL, *exec_end=NULL;
+ char *arg_free=NULL, **arg_excp=&arg_free;
+ char *script_file=NULL, *translated_path = NULL;
+ int interactive=0;
+ int lineno = 0;
+ const char *param_error=NULL;
+ int hide_argv = 0;
+
+ zend_try {
+
+ CG(in_compilation) = 0; /* not initialized but needed for several options */
+ EG(uninitialized_zval_ptr) = NULL;
+
+ while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
+ switch (c) {
+
+ case 'i': /* php info & quit */
+ if (php_request_startup(TSRMLS_C)==FAILURE) {
+ goto err;
+ }
+ request_started = 1;
+ php_print_info(0xFFFFFFFF TSRMLS_CC);
+ php_output_end_all(TSRMLS_C);
+ exit_status = (c == '?' && argc > 1 && !strchr(argv[1], c));
+ goto out;
+
+ case 'v': /* show php version & quit */
+ php_printf("PHP %s (%s) (built: %s %s) %s\nCopyright (c) 1997-2013 The PHP Group\n%s",
+ PHP_VERSION, cli_sapi_module.name, __DATE__, __TIME__,
+#if ZEND_DEBUG && defined(HAVE_GCOV)
+ "(DEBUG GCOV)",
+#elif ZEND_DEBUG
+ "(DEBUG)",
+#elif defined(HAVE_GCOV)
+ "(GCOV)",
+#else
+ "",
+#endif
+ get_zend_version()
+ );
+ sapi_deactivate(TSRMLS_C);
+ goto out;
+
+ case 'm': /* list compiled in modules */
+ if (php_request_startup(TSRMLS_C)==FAILURE) {
+ goto err;
+ }
+ request_started = 1;
+ php_printf("[PHP Modules]\n");
+ print_modules(TSRMLS_C);
+ php_printf("\n[Zend Modules]\n");
+ print_extensions(TSRMLS_C);
+ php_printf("\n");
+ php_output_end_all(TSRMLS_C);
+ exit_status=0;
+ goto out;
+
+ default:
+ break;
+ }
+ }
+
+ /* Set some CLI defaults */
+ SG(options) |= SAPI_OPTION_NO_CHDIR;
+
+ php_optind = orig_optind;
+ php_optarg = orig_optarg;
+ while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
+ switch (c) {
+
+ case 'a': /* interactive mode */
+ if (!interactive) {
+ if (behavior != PHP_MODE_STANDARD) {
+ param_error = param_mode_conflict;
+ break;
+ }
+
+ interactive=1;
+ }
+ break;
+
+ case 'C': /* don't chdir to the script directory */
+ /* This is default so NOP */
+ break;
+
+ case 'F':
+ if (behavior == PHP_MODE_PROCESS_STDIN) {
+ if (exec_run || script_file) {
+ param_error = "You can use -R or -F only once.\n";
+ break;
+ }
+ } else if (behavior != PHP_MODE_STANDARD) {
+ param_error = param_mode_conflict;
+ break;
+ }
+ behavior=PHP_MODE_PROCESS_STDIN;
+ script_file = php_optarg;
+ break;
+
+ case 'f': /* parse file */
+ if (behavior == PHP_MODE_CLI_DIRECT || behavior == PHP_MODE_PROCESS_STDIN) {
+ param_error = param_mode_conflict;
+ break;
+ } else if (script_file) {
+ param_error = "You can use -f only once.\n";
+ break;
+ }
+ script_file = php_optarg;
+ break;
+
+ case 'l': /* syntax check mode */
+ if (behavior != PHP_MODE_STANDARD) {
+ break;
+ }
+ behavior=PHP_MODE_LINT;
+ break;
+
+#if 0 /* not yet operational, see also below ... */
+ case '': /* generate indented source mode*/
+ if (behavior == PHP_MODE_CLI_DIRECT || behavior == PHP_MODE_PROCESS_STDIN) {
+ param_error = "Source indenting only works for files.\n";
+ break;
+ }
+ behavior=PHP_MODE_INDENT;
+ break;
+#endif
+
+ case 'q': /* do not generate HTTP headers */
+ /* This is default so NOP */
+ break;
+
+ case 'r': /* run code from command line */
+ if (behavior == PHP_MODE_CLI_DIRECT) {
+ if (exec_direct || script_file) {
+ param_error = "You can use -r only once.\n";
+ break;
+ }
+ } else if (behavior != PHP_MODE_STANDARD || interactive) {
+ param_error = param_mode_conflict;
+ break;
+ }
+ behavior=PHP_MODE_CLI_DIRECT;
+ exec_direct=php_optarg;
+ break;
+
+ case 'R':
+ if (behavior == PHP_MODE_PROCESS_STDIN) {
+ if (exec_run || script_file) {
+ param_error = "You can use -R or -F only once.\n";
+ break;
+ }
+ } else if (behavior != PHP_MODE_STANDARD) {
+ param_error = param_mode_conflict;
+ break;
+ }
+ behavior=PHP_MODE_PROCESS_STDIN;
+ exec_run=php_optarg;
+ break;
+
+ case 'B':
+ if (behavior == PHP_MODE_PROCESS_STDIN) {
+ if (exec_begin) {
+ param_error = "You can use -B only once.\n";
+ break;
+ }
+ } else if (behavior != PHP_MODE_STANDARD || interactive) {
+ param_error = param_mode_conflict;
+ break;
+ }
+ behavior=PHP_MODE_PROCESS_STDIN;
+ exec_begin=php_optarg;
+ break;
+
+ case 'E':
+ if (behavior == PHP_MODE_PROCESS_STDIN) {
+ if (exec_end) {
+ param_error = "You can use -E only once.\n";
+ break;
+ }
+ } else if (behavior != PHP_MODE_STANDARD || interactive) {
+ param_error = param_mode_conflict;
+ break;
+ }
+ behavior=PHP_MODE_PROCESS_STDIN;
+ exec_end=php_optarg;
+ break;
+
+ case 's': /* generate highlighted HTML from source */
+ if (behavior == PHP_MODE_CLI_DIRECT || behavior == PHP_MODE_PROCESS_STDIN) {
+ param_error = "Source highlighting only works for files.\n";
+ break;
+ }
+ behavior=PHP_MODE_HIGHLIGHT;
+ break;
+
+ case 'w':
+ if (behavior == PHP_MODE_CLI_DIRECT || behavior == PHP_MODE_PROCESS_STDIN) {
+ param_error = "Source stripping only works for files.\n";
+ break;
+ }
+ behavior=PHP_MODE_STRIP;
+ break;
+
+ case 'z': /* load extension file */
+ zend_load_extension(php_optarg);
+ break;
+ case 'H':
+ hide_argv = 1;
+ break;
+ case 10:
+ behavior=PHP_MODE_REFLECTION_FUNCTION;
+ reflection_what = php_optarg;
+ break;
+ case 11:
+ behavior=PHP_MODE_REFLECTION_CLASS;
+ reflection_what = php_optarg;
+ break;
+ case 12:
+ behavior=PHP_MODE_REFLECTION_EXTENSION;
+ reflection_what = php_optarg;
+ break;
+ case 13:
+ behavior=PHP_MODE_REFLECTION_ZEND_EXTENSION;
+ reflection_what = php_optarg;
+ break;
+ case 14:
+ behavior=PHP_MODE_REFLECTION_EXT_INFO;
+ reflection_what = php_optarg;
+ break;
+ case 15:
+ behavior = PHP_MODE_SHOW_INI_CONFIG;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (param_error) {
+ PUTS(param_error);
+ exit_status=1;
+ goto err;
+ }
+
+ if (interactive) {
+#if (HAVE_LIBREADLINE || HAVE_LIBEDIT) && !defined(COMPILE_DL_READLINE)
+ printf("Interactive shell\n\n");
+#else
+ printf("Interactive mode enabled\n\n");
+#endif
+ fflush(stdout);
+ }
+
+ CG(interactive) = interactive;
+
+ /* only set script_file if not set already and not in direct mode and not at end of parameter list */
+ if (argc > php_optind
+ && !script_file
+ && behavior!=PHP_MODE_CLI_DIRECT
+ && behavior!=PHP_MODE_PROCESS_STDIN
+ && strcmp(argv[php_optind-1],"--"))
+ {
+ script_file=argv[php_optind];
+ php_optind++;
+ }
+ if (script_file) {
+ if (cli_seek_file_begin(&file_handle, script_file, &lineno TSRMLS_CC) != SUCCESS) {
+ goto err;
+ } else {
+ char real_path[MAXPATHLEN];
+ if (VCWD_REALPATH(script_file, real_path)) {
+ translated_path = strdup(real_path);
+ }
+ script_filename = script_file;
+ }
+ } else {
+ /* We could handle PHP_MODE_PROCESS_STDIN in a different manner */
+ /* here but this would make things only more complicated. And it */
+ /* is consitent with the way -R works where the stdin file handle*/
+ /* is also accessible. */
+ file_handle.filename = "-";
+ file_handle.handle.fp = stdin;
+ }
+ file_handle.type = ZEND_HANDLE_FP;
+ file_handle.opened_path = NULL;
+ file_handle.free_filename = 0;
+ php_self = (char*)file_handle.filename;
+
+ /* before registering argv to module exchange the *new* argv[0] */
+ /* we can achieve this without allocating more memory */
+ SG(request_info).argc=argc-php_optind+1;
+ arg_excp = argv+php_optind-1;
+ arg_free = argv[php_optind-1];
+ SG(request_info).path_translated = translated_path? translated_path: (char*)file_handle.filename;
+ argv[php_optind-1] = (char*)file_handle.filename;
+ SG(request_info).argv=argv+php_optind-1;
+
+ if (php_request_startup(TSRMLS_C)==FAILURE) {
+ *arg_excp = arg_free;
+ fclose(file_handle.handle.fp);
+ PUTS("Could not startup.\n");
+ goto err;
+ }
+ request_started = 1;
+ CG(start_lineno) = lineno;
+ *arg_excp = arg_free; /* reconstuct argv */
+
+ if (hide_argv) {
+ int i;
+ for (i = 1; i < argc; i++) {
+ memset(argv[i], 0, strlen(argv[i]));
+ }
+ }
+
+ zend_is_auto_global("_SERVER", sizeof("_SERVER")-1 TSRMLS_CC);
+
+ PG(during_request_startup) = 0;
+ switch (behavior) {
+ case PHP_MODE_STANDARD:
+ if (strcmp(file_handle.filename, "-")) {
+ cli_register_file_handles(TSRMLS_C);
+ }
+
+ if (interactive && cli_shell_callbacks.cli_shell_run) {
+ exit_status = cli_shell_callbacks.cli_shell_run(TSRMLS_C);
+ } else {
+ php_execute_script(&file_handle TSRMLS_CC);
+ exit_status = EG(exit_status);
+ }
+ break;
+ case PHP_MODE_LINT:
+ exit_status = php_lint_script(&file_handle TSRMLS_CC);
+ if (exit_status==SUCCESS) {
+ zend_printf("No syntax errors detected in %s\n", file_handle.filename);
+ } else {
+ zend_printf("Errors parsing %s\n", file_handle.filename);
+ }
+ break;
+ case PHP_MODE_STRIP:
+ if (open_file_for_scanning(&file_handle TSRMLS_CC)==SUCCESS) {
+ zend_strip(TSRMLS_C);
+ }
+ goto out;
+ break;
+ case PHP_MODE_HIGHLIGHT:
+ {
+ zend_syntax_highlighter_ini syntax_highlighter_ini;
+
+ if (open_file_for_scanning(&file_handle TSRMLS_CC)==SUCCESS) {
+ php_get_highlight_struct(&syntax_highlighter_ini);
+ zend_highlight(&syntax_highlighter_ini TSRMLS_CC);
+ }
+ goto out;
+ }
+ break;
+#if 0
+ /* Zeev might want to do something with this one day */
+ case PHP_MODE_INDENT:
+ open_file_for_scanning(&file_handle TSRMLS_CC);
+ zend_indent();
+ zend_file_handle_dtor(file_handle.handle TSRMLS_CC);
+ goto out;
+ break;
+#endif
+ case PHP_MODE_CLI_DIRECT:
+ cli_register_file_handles(TSRMLS_C);
+ if (zend_eval_string_ex(exec_direct, NULL, "Command line code", 1 TSRMLS_CC) == FAILURE) {
+ exit_status=254;
+ }
+ break;
+
+ case PHP_MODE_PROCESS_STDIN:
+ {
+ char *input;
+ size_t len, index = 0;
+ zval *argn, *argi;
+
+ cli_register_file_handles(TSRMLS_C);
+
+ if (exec_begin && zend_eval_string_ex(exec_begin, NULL, "Command line begin code", 1 TSRMLS_CC) == FAILURE) {
+ exit_status=254;
+ }
+ ALLOC_ZVAL(argi);
+ Z_TYPE_P(argi) = IS_LONG;
+ Z_LVAL_P(argi) = index;
+ INIT_PZVAL(argi);
+ zend_hash_update(&EG(symbol_table), "argi", sizeof("argi"), &argi, sizeof(zval *), NULL);
+ while (exit_status == SUCCESS && (input=php_stream_gets(s_in_process, NULL, 0)) != NULL) {
+ len = strlen(input);
+ while (len-- && (input[len]=='\n' || input[len]=='\r')) {
+ input[len] = '\0';
+ }
+ ALLOC_ZVAL(argn);
+ Z_TYPE_P(argn) = IS_STRING;
+ Z_STRLEN_P(argn) = ++len;
+ Z_STRVAL_P(argn) = estrndup(input, len);
+ INIT_PZVAL(argn);
+ zend_hash_update(&EG(symbol_table), "argn", sizeof("argn"), &argn, sizeof(zval *), NULL);
+ Z_LVAL_P(argi) = ++index;
+ if (exec_run) {
+ if (zend_eval_string_ex(exec_run, NULL, "Command line run code", 1 TSRMLS_CC) == FAILURE) {
+ exit_status=254;
+ }
+ } else {
+ if (script_file) {
+ if (cli_seek_file_begin(&file_handle, script_file, &lineno TSRMLS_CC) != SUCCESS) {
+ exit_status = 1;
+ } else {
+ CG(start_lineno) = lineno;
+ php_execute_script(&file_handle TSRMLS_CC);
+ exit_status = EG(exit_status);
+ }
+ }
+ }
+ efree(input);
+ }
+ if (exec_end && zend_eval_string_ex(exec_end, NULL, "Command line end code", 1 TSRMLS_CC) == FAILURE) {
+ exit_status=254;
+ }
+
+ break;
+ }
+ case PHP_MODE_REFLECTION_FUNCTION:
+ case PHP_MODE_REFLECTION_CLASS:
+ case PHP_MODE_REFLECTION_EXTENSION:
+ case PHP_MODE_REFLECTION_ZEND_EXTENSION:
+ {
+ zend_class_entry *pce = NULL;
+ zval *arg, *ref;
+ zend_execute_data execute_data;
+
+ switch (behavior) {
+ default:
+ break;
+ case PHP_MODE_REFLECTION_FUNCTION:
+ if (strstr(reflection_what, "::")) {
+ pce = reflection_method_ptr;
+ } else {
+ pce = reflection_function_ptr;
+ }
+ break;
+ case PHP_MODE_REFLECTION_CLASS:
+ pce = reflection_class_ptr;
+ break;
+ case PHP_MODE_REFLECTION_EXTENSION:
+ pce = reflection_extension_ptr;
+ break;
+ case PHP_MODE_REFLECTION_ZEND_EXTENSION:
+ pce = reflection_zend_extension_ptr;
+ break;
+ }
+
+ MAKE_STD_ZVAL(arg);
+ ZVAL_STRING(arg, reflection_what, 1);
+ ALLOC_ZVAL(ref);
+ object_init_ex(ref, pce);
+ INIT_PZVAL(ref);
+
+ memset(&execute_data, 0, sizeof(zend_execute_data));
+ EG(current_execute_data) = &execute_data;
+ EX(function_state).function = pce->constructor;
+ zend_call_method_with_1_params(&ref, pce, &pce->constructor, "__construct", NULL, arg);
+
+ if (EG(exception)) {
+ zval *msg = zend_read_property(zend_exception_get_default(TSRMLS_C), EG(exception), "message", sizeof("message")-1, 0 TSRMLS_CC);
+ zend_printf("Exception: %s\n", Z_STRVAL_P(msg));
+ zval_ptr_dtor(&EG(exception));
+ EG(exception) = NULL;
+ } else {
+ zend_call_method_with_1_params(NULL, reflection_ptr, NULL, "export", NULL, ref);
+ }
+ zval_ptr_dtor(&ref);
+ zval_ptr_dtor(&arg);
+
+ break;
+ }
+ case PHP_MODE_REFLECTION_EXT_INFO:
+ {
+ int len = strlen(reflection_what);
+ char *lcname = zend_str_tolower_dup(reflection_what, len);
+ zend_module_entry *module;
+
+ if (zend_hash_find(&module_registry, lcname, len+1, (void**)&module) == FAILURE) {
+ if (!strcmp(reflection_what, "main")) {
+ display_ini_entries(NULL);
+ } else {
+ zend_printf("Extension '%s' not present.\n", reflection_what);
+ exit_status = 1;
+ }
+ } else {
+ php_info_print_module(module TSRMLS_CC);
+ }
+
+ efree(lcname);
+ break;
+ }
+ case PHP_MODE_SHOW_INI_CONFIG:
+ {
+ zend_printf("Configuration File (php.ini) Path: %s\n", PHP_CONFIG_FILE_PATH);
+ zend_printf("Loaded Configuration File: %s\n", php_ini_opened_path ? php_ini_opened_path : "(none)");
+ zend_printf("Scan for additional .ini files in: %s\n", php_ini_scanned_path ? php_ini_scanned_path : "(none)");
+ zend_printf("Additional .ini files parsed: %s\n", php_ini_scanned_files ? php_ini_scanned_files : "(none)");
+ break;
+ }
+ }
+ } zend_end_try();
+
+out:
+ if (request_started) {
+ php_request_shutdown((void *) 0);
+ }
+ if (translated_path) {
+ free(translated_path);
+ }
+ if (exit_status == 0) {
+ exit_status = EG(exit_status);
+ }
+ return exit_status;
+err:
+ sapi_deactivate(TSRMLS_C);
+ zend_ini_deactivate(TSRMLS_C);
+ exit_status = 1;
+ goto out;
+}
+/* }}} */
+
+/* {{{ main
+ */
+#ifdef PHP_CLI_WIN32_NO_CONSOLE
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
+#else
+int main(int argc, char *argv[])
+#endif
+{
+#ifdef ZTS
+ void ***tsrm_ls;
+#endif
+#ifdef PHP_CLI_WIN32_NO_CONSOLE
+ int argc = __argc;
+ char **argv = __argv;
+#endif
+ int c;
+ int exit_status = SUCCESS;
+ int module_started = 0, sapi_started = 0;
+ char *php_optarg = NULL;
+ int php_optind = 1, use_extended_info = 0;
+ char *ini_path_override = NULL;
+ char *ini_entries = NULL;
+ int ini_entries_len = 0;
+ int ini_ignore = 0;
+ sapi_module_struct *sapi_module = &cli_sapi_module;
+
+ cli_sapi_module.additional_functions = additional_functions;
+
+#if defined(PHP_WIN32) && defined(_DEBUG) && defined(PHP_WIN32_DEBUG_HEAP)
+ {
+ int tmp_flag;
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ tmp_flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+ tmp_flag |= _CRTDBG_DELAY_FREE_MEM_DF;
+ tmp_flag |= _CRTDBG_LEAK_CHECK_DF;
+
+ _CrtSetDbgFlag(tmp_flag);
+ }
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#if defined(SIGPIPE) && defined(SIG_IGN)
+ signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
+ that sockets created via fsockopen()
+ don't kill PHP if the remote site
+ closes it. in apache|apxs mode apache
+ does that for us! thies@thieso.net
+ 20000419 */
+#endif
+#endif
+
+
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+ tsrm_ls = ts_resource(0);
+#endif
+
+#ifdef PHP_WIN32
+ _fmode = _O_BINARY; /*sets default for file streams to binary */
+ setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
+#endif
+
+ while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2))!=-1) {
+ switch (c) {
+ case 'c':
+ if (ini_path_override) {
+ free(ini_path_override);
+ }
+ ini_path_override = strdup(php_optarg);
+ break;
+ case 'n':
+ ini_ignore = 1;
+ break;
+ case 'd': {
+ /* define ini entries on command line */
+ int len = strlen(php_optarg);
+ char *val;
+
+ if ((val = strchr(php_optarg, '='))) {
+ val++;
+ if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
+ ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
+ memcpy(ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
+ ini_entries_len += (val - php_optarg);
+ memcpy(ini_entries + ini_entries_len, "\"", 1);
+ ini_entries_len++;
+ memcpy(ini_entries + ini_entries_len, val, len - (val - php_optarg));
+ ini_entries_len += len - (val - php_optarg);
+ memcpy(ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
+ ini_entries_len += sizeof("\n\0\"") - 2;
+ } else {
+ ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("\n\0"));
+ memcpy(ini_entries + ini_entries_len, php_optarg, len);
+ memcpy(ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
+ ini_entries_len += len + sizeof("\n\0") - 2;
+ }
+ } else {
+ ini_entries = realloc(ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
+ memcpy(ini_entries + ini_entries_len, php_optarg, len);
+ memcpy(ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
+ ini_entries_len += len + sizeof("=1\n\0") - 2;
+ }
+ break;
+ }
+#ifndef PHP_CLI_WIN32_NO_CONSOLE
+ case 'S':
+ sapi_module = &cli_server_sapi_module;
+ break;
+#endif
+ case 'h': /* help & quit */
+ case '?':
+ php_cli_usage(argv[0]);
+ goto out;
+ case 'i': case 'v': case 'm':
+ sapi_module = &cli_sapi_module;
+ goto exit_loop;
+ case 'e': /* enable extended info output */
+ use_extended_info = 1;
+ break;
+ }
+ }
+exit_loop:
+
+ sapi_module->ini_defaults = sapi_cli_ini_defaults;
+ sapi_module->php_ini_path_override = ini_path_override;
+ sapi_module->phpinfo_as_text = 1;
+ sapi_module->php_ini_ignore_cwd = 1;
+ sapi_startup(sapi_module);
+ sapi_started = 1;
+
+ sapi_module->php_ini_ignore = ini_ignore;
+
+ sapi_module->executable_location = argv[0];
+
+ if (sapi_module == &cli_sapi_module) {
+ if (ini_entries) {
+ ini_entries = realloc(ini_entries, ini_entries_len + sizeof(HARDCODED_INI));
+ memmove(ini_entries + sizeof(HARDCODED_INI) - 2, ini_entries, ini_entries_len + 1);
+ memcpy(ini_entries, HARDCODED_INI, sizeof(HARDCODED_INI) - 2);
+ } else {
+ ini_entries = malloc(sizeof(HARDCODED_INI));
+ memcpy(ini_entries, HARDCODED_INI, sizeof(HARDCODED_INI));
+ }
+ ini_entries_len += sizeof(HARDCODED_INI) - 2;
+ }
+
+ sapi_module->ini_entries = ini_entries;
+
+ /* startup after we get the above ini override se we get things right */
+ if (sapi_module->startup(sapi_module) == FAILURE) {
+ /* there is no way to see if we must call zend_ini_deactivate()
+ * since we cannot check if EG(ini_directives) has been initialised
+ * because the executor's constructor does not set initialize it.
+ * Apart from that there seems no need for zend_ini_deactivate() yet.
+ * So we goto out_err.*/
+ exit_status = 1;
+ goto out;
+ }
+ module_started = 1;
+
+ /* -e option */
+ if (use_extended_info) {
+ CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
+ }
+
+ zend_first_try {
+#ifndef PHP_CLI_WIN32_NO_CONSOLE
+ if (sapi_module == &cli_sapi_module) {
+#endif
+ exit_status = do_cli(argc, argv TSRMLS_CC);
+#ifndef PHP_CLI_WIN32_NO_CONSOLE
+ } else {
+ exit_status = do_cli_server(argc, argv TSRMLS_CC);
+ }
+#endif
+ } zend_end_try();
+out:
+ if (ini_path_override) {
+ free(ini_path_override);
+ }
+ if (ini_entries) {
+ free(ini_entries);
+ }
+ if (module_started) {
+ php_module_shutdown(TSRMLS_C);
+ }
+ if (sapi_started) {
+ sapi_shutdown();
+ }
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+
+ exit(exit_status);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c
new file mode 100644
index 0000000..ab7f4cf
--- /dev/null
+++ b/sapi/cli/php_cli_server.c
@@ -0,0 +1,2425 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Moriyoshi Koizumi <moriyoshi@php.net> |
+ | Xinchen Hui <laruence@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: php_cli.c 306938 2011-01-01 02:17:06Z felipe $ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#ifdef PHP_WIN32
+#include <process.h>
+#include <io.h>
+#include "win32/time.h"
+#include "win32/signal.h"
+#include "win32/php_registry.h"
+#else
+# include "php_config.h"
+#endif
+
+#ifdef __riscos__
+#include <unixlib/local.h>
+#endif
+
+
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#if HAVE_SETLOCALE
+#include <locale.h>
+#endif
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include "SAPI.h"
+#include "php.h"
+#include "php_ini.h"
+#include "php_main.h"
+#include "php_globals.h"
+#include "php_variables.h"
+#include "zend_hash.h"
+#include "zend_modules.h"
+#include "fopen_wrappers.h"
+
+#include "zend_compile.h"
+#include "zend_execute.h"
+#include "zend_highlight.h"
+#include "zend_indent.h"
+#include "zend_exceptions.h"
+
+#include "php_getopt.h"
+
+#ifndef PHP_WIN32
+# define php_select(m, r, w, e, t) select(m, r, w, e, t)
+# define SOCK_EINVAL EINVAL
+# define SOCK_EAGAIN EAGAIN
+# define SOCK_EINTR EINTR
+# define SOCK_EADDRINUSE EADDRINUSE
+#else
+# include "win32/select.h"
+# define SOCK_EINVAL WSAEINVAL
+# define SOCK_EAGAIN WSAEWOULDBLOCK
+# define SOCK_EINTR WSAEINTR
+# define SOCK_EADDRINUSE WSAEADDRINUSE
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
+#endif
+
+#include "ext/standard/file.h" /* for php_set_sock_blocking() :-( */
+#include "ext/standard/php_smart_str.h"
+#include "ext/standard/html.h"
+#include "ext/standard/url.h" /* for php_url_decode() */
+#include "ext/standard/php_string.h" /* for php_dirname() */
+#include "php_network.h"
+
+#include "php_http_parser.h"
+#include "php_cli_server.h"
+
+#define OUTPUT_NOT_CHECKED -1
+#define OUTPUT_IS_TTY 1
+#define OUTPUT_NOT_TTY 0
+
+typedef struct php_cli_server_poller {
+ fd_set rfds, wfds;
+ struct {
+ fd_set rfds, wfds;
+ } active;
+ php_socket_t max_fd;
+} php_cli_server_poller;
+
+typedef struct php_cli_server_request {
+ enum php_http_method request_method;
+ int protocol_version;
+ char *request_uri;
+ size_t request_uri_len;
+ char *vpath;
+ size_t vpath_len;
+ char *path_translated;
+ size_t path_translated_len;
+ char *path_info;
+ size_t path_info_len;
+ char *query_string;
+ size_t query_string_len;
+ HashTable headers;
+ char *content;
+ size_t content_len;
+ const char *ext;
+ size_t ext_len;
+ struct stat sb;
+} php_cli_server_request;
+
+typedef struct php_cli_server_chunk {
+ struct php_cli_server_chunk *next;
+ enum php_cli_server_chunk_type {
+ PHP_CLI_SERVER_CHUNK_HEAP,
+ PHP_CLI_SERVER_CHUNK_IMMORTAL
+ } type;
+ union {
+ struct { void *block; char *p; size_t len; } heap;
+ struct { const char *p; size_t len; } immortal;
+ } data;
+} php_cli_server_chunk;
+
+typedef struct php_cli_server_buffer {
+ php_cli_server_chunk *first;
+ php_cli_server_chunk *last;
+} php_cli_server_buffer;
+
+typedef struct php_cli_server_content_sender {
+ php_cli_server_buffer buffer;
+} php_cli_server_content_sender;
+
+typedef struct php_cli_server_client {
+ struct php_cli_server *server;
+ php_socket_t sock;
+ struct sockaddr *addr;
+ socklen_t addr_len;
+ char *addr_str;
+ size_t addr_str_len;
+ php_http_parser parser;
+ unsigned int request_read:1;
+ char *current_header_name;
+ size_t current_header_name_len;
+ unsigned int current_header_name_allocated:1;
+ size_t post_read_offset;
+ php_cli_server_request request;
+ unsigned int content_sender_initialized:1;
+ php_cli_server_content_sender content_sender;
+ int file_fd;
+} php_cli_server_client;
+
+typedef struct php_cli_server {
+ php_socket_t server_sock;
+ php_cli_server_poller poller;
+ int is_running;
+ char *host;
+ int port;
+ int address_family;
+ char *document_root;
+ size_t document_root_len;
+ char *router;
+ size_t router_len;
+ socklen_t socklen;
+ HashTable clients;
+} php_cli_server;
+
+typedef struct php_cli_server_http_reponse_status_code_pair {
+ int code;
+ const char *str;
+} php_cli_server_http_reponse_status_code_pair;
+
+typedef struct php_cli_server_ext_mime_type_pair {
+ const char *ext;
+ const char *mime_type;
+} php_cli_server_ext_mime_type_pair;
+
+static php_cli_server_http_reponse_status_code_pair status_map[] = {
+ { 100, "Continue" },
+ { 101, "Switching Protocols" },
+ { 200, "OK" },
+ { 201, "Created" },
+ { 202, "Accepted" },
+ { 203, "Non-Authoritative Information" },
+ { 204, "No Content" },
+ { 205, "Reset Content" },
+ { 206, "Partial Content" },
+ { 300, "Multiple Choices" },
+ { 301, "Moved Permanently" },
+ { 302, "Found" },
+ { 303, "See Other" },
+ { 304, "Not Modified" },
+ { 305, "Use Proxy" },
+ { 307, "Temporary Redirect" },
+ { 400, "Bad Request" },
+ { 401, "Unauthorized" },
+ { 402, "Payment Required" },
+ { 403, "Forbidden" },
+ { 404, "Not Found" },
+ { 405, "Method Not Allowed" },
+ { 406, "Not Acceptable" },
+ { 407, "Proxy Authentication Required" },
+ { 408, "Request Timeout" },
+ { 409, "Conflict" },
+ { 410, "Gone" },
+ { 411, "Length Required" },
+ { 412, "Precondition Failed" },
+ { 413, "Request Entity Too Large" },
+ { 414, "Request-URI Too Long" },
+ { 415, "Unsupported Media Type" },
+ { 416, "Requested Range Not Satisfiable" },
+ { 417, "Expectation Failed" },
+ { 428, "Precondition Required" },
+ { 429, "Too Many Requests" },
+ { 431, "Request Header Fields Too Large" },
+ { 500, "Internal Server Error" },
+ { 501, "Not Implemented" },
+ { 502, "Bad Gateway" },
+ { 503, "Service Unavailable" },
+ { 504, "Gateway Timeout" },
+ { 505, "HTTP Version Not Supported" },
+ { 511, "Network Authentication Required" },
+};
+
+static php_cli_server_http_reponse_status_code_pair template_map[] = {
+ { 400, "<h1>%s</h1><p>Your browser sent a request that this server could not understand.</p>" },
+ { 404, "<h1>%s</h1><p>The requested resource %s was not found on this server.</p>" },
+ { 500, "<h1>%s</h1><p>The server is temporarily unavailable.</p>" },
+ { 501, "<h1>%s</h1><p>Request method not supported.</p>" }
+};
+
+static php_cli_server_ext_mime_type_pair mime_type_map[] = {
+ { "html", "text/html" },
+ { "htm", "text/html" },
+ { "js", "text/javascript" },
+ { "css", "text/css" },
+ { "gif", "image/gif" },
+ { "jpg", "image/jpeg" },
+ { "jpeg", "image/jpeg" },
+ { "jpe", "image/jpeg" },
+ { "png", "image/png" },
+ { "svg", "image/svg+xml" },
+ { "txt", "text/plain" },
+ { "webm", "video/webm" },
+ { "ogv", "video/ogg" },
+ { "ogg", "audio/ogg" },
+ { NULL, NULL }
+};
+
+static int php_cli_output_is_tty = OUTPUT_NOT_CHECKED;
+
+static size_t php_cli_server_client_send_through(php_cli_server_client *client, const char *str, size_t str_len);
+static php_cli_server_chunk *php_cli_server_chunk_heap_new_self_contained(size_t len);
+static void php_cli_server_buffer_append(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk);
+static void php_cli_server_logf(const char *format TSRMLS_DC, ...);
+static void php_cli_server_log_response(php_cli_server_client *client, int status, const char *message TSRMLS_DC);
+
+ZEND_DECLARE_MODULE_GLOBALS(cli_server);
+
+/* {{{ static char php_cli_server_css[]
+ * copied from ext/standard/info.c
+ */
+static const char php_cli_server_css[] = "<style>\n" \
+ "body { background-color: #ffffff; color: #000000; }\n" \
+ "h1 { font-family: sans-serif; font-size: 150%; background-color: #9999cc; font-weight: bold; color: #000000; margin-top: 0;}\n" \
+ "</style>\n";
+/* }}} */
+
+static void char_ptr_dtor_p(char **p) /* {{{ */
+{
+ pefree(*p, 1);
+} /* }}} */
+
+static char *get_last_error() /* {{{ */
+{
+ return pestrdup(strerror(errno), 1);
+} /* }}} */
+
+static const char *get_status_string(int code) /* {{{ */
+{
+ size_t e = (sizeof(status_map) / sizeof(php_cli_server_http_reponse_status_code_pair));
+ size_t s = 0;
+
+ while (e != s) {
+ size_t c = MIN((e + s + 1) / 2, e - 1);
+ int d = status_map[c].code;
+ if (d > code) {
+ e = c;
+ } else if (d < code) {
+ s = c;
+ } else {
+ return status_map[c].str;
+ }
+ }
+ return NULL;
+} /* }}} */
+
+static const char *get_template_string(int code) /* {{{ */
+{
+ size_t e = (sizeof(template_map) / sizeof(php_cli_server_http_reponse_status_code_pair));
+ size_t s = 0;
+
+ while (e != s) {
+ size_t c = MIN((e + s + 1) / 2, e - 1);
+ int d = template_map[c].code;
+ if (d > code) {
+ e = c;
+ } else if (d < code) {
+ s = c;
+ } else {
+ return template_map[c].str;
+ }
+ }
+ return NULL;
+} /* }}} */
+
+static void append_http_status_line(smart_str *buffer, int protocol_version, int response_code, int persistent) /* {{{ */
+{
+ if (!response_code) {
+ response_code = 200;
+ }
+ smart_str_appendl_ex(buffer, "HTTP", 4, persistent);
+ smart_str_appendc_ex(buffer, '/', persistent);
+ smart_str_append_generic_ex(buffer, protocol_version / 100, persistent, int, _unsigned);
+ smart_str_appendc_ex(buffer, '.', persistent);
+ smart_str_append_generic_ex(buffer, protocol_version % 100, persistent, int, _unsigned);
+ smart_str_appendc_ex(buffer, ' ', persistent);
+ smart_str_append_generic_ex(buffer, response_code, persistent, int, _unsigned);
+ smart_str_appendc_ex(buffer, ' ', persistent);
+ smart_str_appends_ex(buffer, get_status_string(response_code), persistent);
+ smart_str_appendl_ex(buffer, "\r\n", 2, persistent);
+} /* }}} */
+
+static void append_essential_headers(smart_str* buffer, php_cli_server_client *client, int persistent) /* {{{ */
+{
+ {
+ char **val;
+ if (SUCCESS == zend_hash_find(&client->request.headers, "Host", sizeof("Host"), (void**)&val)) {
+ smart_str_appendl_ex(buffer, "Host", sizeof("Host") - 1, persistent);
+ smart_str_appendl_ex(buffer, ": ", sizeof(": ") - 1, persistent);
+ smart_str_appends_ex(buffer, *val, persistent);
+ smart_str_appendl_ex(buffer, "\r\n", 2, persistent);
+ }
+ }
+ smart_str_appendl_ex(buffer, "Connection: close\r\n", sizeof("Connection: close\r\n") - 1, persistent);
+} /* }}} */
+
+static const char *get_mime_type(const char *ext, size_t ext_len) /* {{{ */
+{
+ php_cli_server_ext_mime_type_pair *pair;
+ for (pair = mime_type_map; pair->ext; pair++) {
+ size_t len = strlen(pair->ext);
+ if (len == ext_len && memcmp(pair->ext, ext, len) == 0) {
+ return pair->mime_type;
+ }
+ }
+ return NULL;
+} /* }}} */
+
+/* {{{ cli_server module
+ */
+
+static void cli_server_init_globals(zend_cli_server_globals *cg TSRMLS_DC)
+{
+ cg->color = 0;
+}
+
+PHP_INI_BEGIN()
+ STD_PHP_INI_BOOLEAN("cli_server.color", "0", PHP_INI_ALL, OnUpdateBool, color, zend_cli_server_globals, cli_server_globals)
+PHP_INI_END()
+
+static PHP_MINIT_FUNCTION(cli_server)
+{
+ ZEND_INIT_MODULE_GLOBALS(cli_server, cli_server_init_globals, NULL);
+ REGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+
+static PHP_MSHUTDOWN_FUNCTION(cli_server)
+{
+ UNREGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+
+static PHP_MINFO_FUNCTION(cli_server)
+{
+ DISPLAY_INI_ENTRIES();
+}
+
+zend_module_entry cli_server_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "cli_server",
+ NULL,
+ PHP_MINIT(cli_server),
+ PHP_MSHUTDOWN(cli_server),
+ NULL,
+ NULL,
+ PHP_MINFO(cli_server),
+ PHP_VERSION,
+ STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+static int sapi_cli_server_startup(sapi_module_struct *sapi_module) /* {{{ */
+{
+ if (php_module_startup(sapi_module, &cli_server_module_entry, 1) == FAILURE) {
+ return FAILURE;
+ }
+ return SUCCESS;
+} /* }}} */
+
+static int sapi_cli_server_ub_write(const char *str, uint str_length TSRMLS_DC) /* {{{ */
+{
+ php_cli_server_client *client = SG(server_context);
+ if (!client) {
+ return 0;
+ }
+ return php_cli_server_client_send_through(client, str, str_length);
+} /* }}} */
+
+static void sapi_cli_server_flush(void *server_context) /* {{{ */
+{
+ php_cli_server_client *client = server_context;
+ TSRMLS_FETCH();
+
+ if (!client) {
+ return;
+ }
+
+ if (client->sock < 0) {
+ php_handle_aborted_connection();
+ return;
+ }
+
+ if (!SG(headers_sent)) {
+ sapi_send_headers(TSRMLS_C);
+ SG(headers_sent) = 1;
+ }
+} /* }}} */
+
+static int sapi_cli_server_discard_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */{
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+/* }}} */
+
+static int sapi_cli_server_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC) /* {{{ */
+{
+ php_cli_server_client *client = SG(server_context);
+ smart_str buffer = { 0 };
+ sapi_header_struct *h;
+ zend_llist_position pos;
+
+ if (client == NULL || SG(request_info).no_headers) {
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+ }
+
+ if (SG(sapi_headers).http_status_line) {
+ smart_str_appends(&buffer, SG(sapi_headers).http_status_line);
+ smart_str_appendl(&buffer, "\r\n", 2);
+ } else {
+ append_http_status_line(&buffer, client->request.protocol_version, SG(sapi_headers).http_response_code, 0);
+ }
+
+ append_essential_headers(&buffer, client, 0);
+
+ h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+ if (!h->header_len) {
+ continue;
+ }
+ smart_str_appendl(&buffer, h->header, h->header_len);
+ smart_str_appendl(&buffer, "\r\n", 2);
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ }
+ smart_str_appendl(&buffer, "\r\n", 2);
+
+ php_cli_server_client_send_through(client, buffer.c, buffer.len);
+
+ smart_str_free(&buffer);
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+/* }}} */
+
+static char *sapi_cli_server_read_cookies(TSRMLS_D) /* {{{ */
+{
+ php_cli_server_client *client = SG(server_context);
+ char **val;
+ if (FAILURE == zend_hash_find(&client->request.headers, "Cookie", sizeof("Cookie"), (void**)&val)) {
+ return NULL;
+ }
+ return *val;
+} /* }}} */
+
+static int sapi_cli_server_read_post(char *buf, uint count_bytes TSRMLS_DC) /* {{{ */
+{
+ php_cli_server_client *client = SG(server_context);
+ if (client->request.content) {
+ size_t content_len = client->request.content_len;
+ size_t nbytes_copied = MIN(client->post_read_offset + count_bytes, content_len) - client->post_read_offset;
+ memmove(buf, client->request.content + client->post_read_offset, nbytes_copied);
+ client->post_read_offset += nbytes_copied;
+ return nbytes_copied;
+ }
+ return 0;
+} /* }}} */
+
+static void sapi_cli_server_register_variable(zval *track_vars_array, const char *key, const char *val TSRMLS_DC) /* {{{ */
+{
+ char *new_val = (char *)val;
+ uint new_val_len;
+ if (sapi_module.input_filter(PARSE_SERVER, (char*)key, &new_val, strlen(val), &new_val_len TSRMLS_CC)) {
+ php_register_variable_safe((char *)key, new_val, new_val_len, track_vars_array TSRMLS_CC);
+ }
+} /* }}} */
+
+static int sapi_cli_server_register_entry_cb(char **entry TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */ {
+ zval *track_vars_array = va_arg(args, zval *);
+ if (hash_key->nKeyLength) {
+ char *real_key, *key;
+ uint i;
+ key = estrndup(hash_key->arKey, hash_key->nKeyLength);
+ for(i=0; i<hash_key->nKeyLength; i++) {
+ if (key[i] == '-') {
+ key[i] = '_';
+ } else {
+ key[i] = toupper(key[i]);
+ }
+ }
+ spprintf(&real_key, 0, "%s_%s", "HTTP", key);
+ sapi_cli_server_register_variable(track_vars_array, real_key, *entry TSRMLS_CC);
+ efree(key);
+ efree(real_key);
+ }
+
+ return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+
+static void sapi_cli_server_register_variables(zval *track_vars_array TSRMLS_DC) /* {{{ */
+{
+ php_cli_server_client *client = SG(server_context);
+ sapi_cli_server_register_variable(track_vars_array, "DOCUMENT_ROOT", client->server->document_root TSRMLS_CC);
+ {
+ char *tmp;
+ if ((tmp = strrchr(client->addr_str, ':'))) {
+ char addr[64], port[8];
+ strncpy(port, tmp + 1, 8);
+ port[7] = '\0';
+ strncpy(addr, client->addr_str, tmp - client->addr_str);
+ addr[tmp - client->addr_str] = '\0';
+ sapi_cli_server_register_variable(track_vars_array, "REMOTE_ADDR", addr TSRMLS_CC);
+ sapi_cli_server_register_variable(track_vars_array, "REMOTE_PORT", port TSRMLS_CC);
+ } else {
+ sapi_cli_server_register_variable(track_vars_array, "REMOTE_ADDR", client->addr_str TSRMLS_CC);
+ }
+ }
+ {
+ char *tmp;
+ spprintf(&tmp, 0, "PHP %s Development Server", PHP_VERSION);
+ sapi_cli_server_register_variable(track_vars_array, "SERVER_SOFTWARE", tmp TSRMLS_CC);
+ efree(tmp);
+ }
+ {
+ char *tmp;
+ spprintf(&tmp, 0, "HTTP/%d.%d", client->request.protocol_version / 100, client->request.protocol_version % 100);
+ sapi_cli_server_register_variable(track_vars_array, "SERVER_PROTOCOL", tmp TSRMLS_CC);
+ efree(tmp);
+ }
+ sapi_cli_server_register_variable(track_vars_array, "SERVER_NAME", client->server->host TSRMLS_CC);
+ {
+ char *tmp;
+ spprintf(&tmp, 0, "%i", client->server->port);
+ sapi_cli_server_register_variable(track_vars_array, "SERVER_PORT", tmp TSRMLS_CC);
+ efree(tmp);
+ }
+
+ sapi_cli_server_register_variable(track_vars_array, "REQUEST_URI", client->request.request_uri TSRMLS_CC);
+ sapi_cli_server_register_variable(track_vars_array, "REQUEST_METHOD", SG(request_info).request_method TSRMLS_CC);
+ sapi_cli_server_register_variable(track_vars_array, "SCRIPT_NAME", client->request.vpath TSRMLS_CC);
+ if (SG(request_info).path_translated) {
+ sapi_cli_server_register_variable(track_vars_array, "SCRIPT_FILENAME", SG(request_info).path_translated TSRMLS_CC);
+ } else if (client->server->router) {
+ char *temp;
+ spprintf(&temp, 0, "%s/%s", client->server->document_root, client->server->router);
+ sapi_cli_server_register_variable(track_vars_array, "SCRIPT_FILENAME", temp TSRMLS_CC);
+ efree(temp);
+ }
+ if (client->request.path_info) {
+ sapi_cli_server_register_variable(track_vars_array, "PATH_INFO", client->request.path_info TSRMLS_CC);
+ }
+ if (client->request.path_info_len) {
+ char *tmp;
+ spprintf(&tmp, 0, "%s%s", client->request.vpath, client->request.path_info);
+ sapi_cli_server_register_variable(track_vars_array, "PHP_SELF", tmp TSRMLS_CC);
+ efree(tmp);
+ } else {
+ sapi_cli_server_register_variable(track_vars_array, "PHP_SELF", client->request.vpath TSRMLS_CC);
+ }
+ if (client->request.query_string) {
+ sapi_cli_server_register_variable(track_vars_array, "QUERY_STRING", client->request.query_string TSRMLS_CC);
+ }
+ zend_hash_apply_with_arguments(&client->request.headers TSRMLS_CC, (apply_func_args_t)sapi_cli_server_register_entry_cb, 1, track_vars_array);
+} /* }}} */
+
+static void sapi_cli_server_log_message(char *msg TSRMLS_DC) /* {{{ */
+{
+ struct timeval tv;
+ struct tm tm;
+ char buf[52];
+ gettimeofday(&tv, NULL);
+ php_localtime_r(&tv.tv_sec, &tm);
+ php_asctime_r(&tm, buf);
+ {
+ size_t l = strlen(buf);
+ if (l > 0) {
+ buf[l - 1] = '\0';
+ } else {
+ memmove(buf, "unknown", sizeof("unknown"));
+ }
+ }
+ fprintf(stderr, "[%s] %s\n", buf, msg);
+} /* }}} */
+
+/* {{{ sapi_module_struct cli_server_sapi_module
+ */
+sapi_module_struct cli_server_sapi_module = {
+ "cli-server", /* name */
+ "Built-in HTTP server", /* pretty name */
+
+ sapi_cli_server_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ sapi_cli_server_ub_write, /* unbuffered write */
+ sapi_cli_server_flush, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ NULL, /* header handler */
+ sapi_cli_server_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ sapi_cli_server_read_post, /* read POST data */
+ sapi_cli_server_read_cookies, /* read Cookies */
+
+ sapi_cli_server_register_variables, /* register server variables */
+ sapi_cli_server_log_message, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+}; /* }}} */
+
+static int php_cli_server_poller_ctor(php_cli_server_poller *poller) /* {{{ */
+{
+ FD_ZERO(&poller->rfds);
+ FD_ZERO(&poller->wfds);
+ poller->max_fd = -1;
+ return SUCCESS;
+} /* }}} */
+
+static void php_cli_server_poller_add(php_cli_server_poller *poller, int mode, int fd) /* {{{ */
+{
+ if (mode & POLLIN) {
+ PHP_SAFE_FD_SET(fd, &poller->rfds);
+ }
+ if (mode & POLLOUT) {
+ PHP_SAFE_FD_SET(fd, &poller->wfds);
+ }
+ if (fd > poller->max_fd) {
+ poller->max_fd = fd;
+ }
+} /* }}} */
+
+static void php_cli_server_poller_remove(php_cli_server_poller *poller, int mode, int fd) /* {{{ */
+{
+ if (mode & POLLIN) {
+ PHP_SAFE_FD_CLR(fd, &poller->rfds);
+ }
+ if (mode & POLLOUT) {
+ PHP_SAFE_FD_CLR(fd, &poller->wfds);
+ }
+#ifndef PHP_WIN32
+ if (fd == poller->max_fd) {
+ while (fd > 0) {
+ fd--;
+ if (PHP_SAFE_FD_ISSET(fd, &poller->rfds) || PHP_SAFE_FD_ISSET(fd, &poller->wfds)) {
+ break;
+ }
+ }
+ poller->max_fd = fd;
+ }
+#endif
+} /* }}} */
+
+static int php_cli_server_poller_poll(php_cli_server_poller *poller, const struct timeval *tv) /* {{{ */
+{
+ memmove(&poller->active.rfds, &poller->rfds, sizeof(poller->rfds));
+ memmove(&poller->active.wfds, &poller->wfds, sizeof(poller->wfds));
+ return php_select(poller->max_fd + 1, &poller->active.rfds, &poller->active.wfds, NULL, (struct timeval *)tv);
+} /* }}} */
+
+static int php_cli_server_poller_iter_on_active(php_cli_server_poller *poller, void *opaque, int(*callback)(void *, int fd, int events)) /* {{{ */
+{
+ int retval = SUCCESS;
+#ifdef PHP_WIN32
+ struct socket_entry {
+ SOCKET fd;
+ int events;
+ } entries[FD_SETSIZE * 2];
+ php_socket_t fd = 0;
+ size_t i;
+ struct socket_entry *n = entries, *m;
+
+ for (i = 0; i < poller->active.rfds.fd_count; i++) {
+ n->events = POLLIN;
+ n->fd = poller->active.rfds.fd_array[i];
+ n++;
+ }
+
+ m = n;
+ for (i = 0; i < poller->active.wfds.fd_count; i++) {
+ struct socket_entry *e;
+ SOCKET fd = poller->active.wfds.fd_array[i];
+ for (e = entries; e < m; e++) {
+ if (e->fd == fd) {
+ e->events |= POLLOUT;
+ }
+ }
+ if (e == m) {
+ assert(n < entries + FD_SETSIZE * 2);
+ n->events = POLLOUT;
+ n->fd = fd;
+ n++;
+ }
+ }
+
+ {
+ struct socket_entry *e = entries;
+ for (; e < n; e++) {
+ if (SUCCESS != callback(opaque, e->fd, e->events)) {
+ retval = FAILURE;
+ }
+ }
+ }
+
+#else
+ php_socket_t fd;
+ const php_socket_t max_fd = poller->max_fd;
+
+ for (fd=0 ; fd<=max_fd ; fd++) {
+ if (PHP_SAFE_FD_ISSET(fd, &poller->active.rfds)) {
+ if (SUCCESS != callback(opaque, fd, POLLIN)) {
+ retval = FAILURE;
+ }
+ }
+ if (PHP_SAFE_FD_ISSET(fd, &poller->active.wfds)) {
+ if (SUCCESS != callback(opaque, fd, POLLOUT)) {
+ retval = FAILURE;
+ }
+ }
+ }
+#endif
+ return retval;
+} /* }}} */
+
+static size_t php_cli_server_chunk_size(const php_cli_server_chunk *chunk) /* {{{ */
+{
+ switch (chunk->type) {
+ case PHP_CLI_SERVER_CHUNK_HEAP:
+ return chunk->data.heap.len;
+ case PHP_CLI_SERVER_CHUNK_IMMORTAL:
+ return chunk->data.immortal.len;
+ }
+ return 0;
+} /* }}} */
+
+static void php_cli_server_chunk_dtor(php_cli_server_chunk *chunk) /* {{{ */
+{
+ switch (chunk->type) {
+ case PHP_CLI_SERVER_CHUNK_HEAP:
+ if (chunk->data.heap.block != chunk) {
+ pefree(chunk->data.heap.block, 1);
+ }
+ break;
+ case PHP_CLI_SERVER_CHUNK_IMMORTAL:
+ break;
+ }
+} /* }}} */
+
+static void php_cli_server_buffer_dtor(php_cli_server_buffer *buffer) /* {{{ */
+{
+ php_cli_server_chunk *chunk, *next;
+ for (chunk = buffer->first; chunk; chunk = next) {
+ next = chunk->next;
+ php_cli_server_chunk_dtor(chunk);
+ pefree(chunk, 1);
+ }
+} /* }}} */
+
+static void php_cli_server_buffer_ctor(php_cli_server_buffer *buffer) /* {{{ */
+{
+ buffer->first = NULL;
+ buffer->last = NULL;
+} /* }}} */
+
+static void php_cli_server_buffer_append(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk) /* {{{ */
+{
+ php_cli_server_chunk *last;
+ for (last = chunk; last->next; last = last->next);
+ if (!buffer->last) {
+ buffer->first = chunk;
+ } else {
+ buffer->last->next = chunk;
+ }
+ buffer->last = last;
+} /* }}} */
+
+static void php_cli_server_buffer_prepend(php_cli_server_buffer *buffer, php_cli_server_chunk *chunk) /* {{{ */
+{
+ php_cli_server_chunk *last;
+ for (last = chunk; last->next; last = last->next);
+ last->next = buffer->first;
+ if (!buffer->last) {
+ buffer->last = last;
+ }
+ buffer->first = chunk;
+} /* }}} */
+
+static size_t php_cli_server_buffer_size(const php_cli_server_buffer *buffer) /* {{{ */
+{
+ php_cli_server_chunk *chunk;
+ size_t retval = 0;
+ for (chunk = buffer->first; chunk; chunk = chunk->next) {
+ retval += php_cli_server_chunk_size(chunk);
+ }
+ return retval;
+} /* }}} */
+
+static php_cli_server_chunk *php_cli_server_chunk_immortal_new(const char *buf, size_t len) /* {{{ */
+{
+ php_cli_server_chunk *chunk = pemalloc(sizeof(php_cli_server_chunk), 1);
+ if (!chunk) {
+ return NULL;
+ }
+
+ chunk->type = PHP_CLI_SERVER_CHUNK_IMMORTAL;
+ chunk->next = NULL;
+ chunk->data.immortal.p = buf;
+ chunk->data.immortal.len = len;
+ return chunk;
+} /* }}} */
+
+static php_cli_server_chunk *php_cli_server_chunk_heap_new(char *block, char *buf, size_t len) /* {{{ */
+{
+ php_cli_server_chunk *chunk = pemalloc(sizeof(php_cli_server_chunk), 1);
+ if (!chunk) {
+ return NULL;
+ }
+
+ chunk->type = PHP_CLI_SERVER_CHUNK_HEAP;
+ chunk->next = NULL;
+ chunk->data.heap.block = block;
+ chunk->data.heap.p = buf;
+ chunk->data.heap.len = len;
+ return chunk;
+} /* }}} */
+
+static php_cli_server_chunk *php_cli_server_chunk_heap_new_self_contained(size_t len) /* {{{ */
+{
+ php_cli_server_chunk *chunk = pemalloc(sizeof(php_cli_server_chunk) + len, 1);
+ if (!chunk) {
+ return NULL;
+ }
+
+ chunk->type = PHP_CLI_SERVER_CHUNK_HEAP;
+ chunk->next = NULL;
+ chunk->data.heap.block = chunk;
+ chunk->data.heap.p = (char *)(chunk + 1);
+ chunk->data.heap.len = len;
+ return chunk;
+} /* }}} */
+
+static void php_cli_server_content_sender_dtor(php_cli_server_content_sender *sender) /* {{{ */
+{
+ php_cli_server_buffer_dtor(&sender->buffer);
+} /* }}} */
+
+static void php_cli_server_content_sender_ctor(php_cli_server_content_sender *sender) /* {{{ */
+{
+ php_cli_server_buffer_ctor(&sender->buffer);
+} /* }}} */
+
+static int php_cli_server_content_sender_send(php_cli_server_content_sender *sender, php_socket_t fd, size_t *nbytes_sent_total) /* {{{ */
+{
+ php_cli_server_chunk *chunk, *next;
+ size_t _nbytes_sent_total = 0;
+
+ for (chunk = sender->buffer.first; chunk; chunk = next) {
+ ssize_t nbytes_sent;
+ next = chunk->next;
+
+ switch (chunk->type) {
+ case PHP_CLI_SERVER_CHUNK_HEAP:
+ nbytes_sent = send(fd, chunk->data.heap.p, chunk->data.heap.len, 0);
+ if (nbytes_sent < 0) {
+ *nbytes_sent_total = _nbytes_sent_total;
+ return php_socket_errno();
+ } else if (nbytes_sent == chunk->data.heap.len) {
+ php_cli_server_chunk_dtor(chunk);
+ pefree(chunk, 1);
+ sender->buffer.first = next;
+ if (!next) {
+ sender->buffer.last = NULL;
+ }
+ } else {
+ chunk->data.heap.p += nbytes_sent;
+ chunk->data.heap.len -= nbytes_sent;
+ }
+ _nbytes_sent_total += nbytes_sent;
+ break;
+
+ case PHP_CLI_SERVER_CHUNK_IMMORTAL:
+ nbytes_sent = send(fd, chunk->data.immortal.p, chunk->data.immortal.len, 0);
+ if (nbytes_sent < 0) {
+ *nbytes_sent_total = _nbytes_sent_total;
+ return php_socket_errno();
+ } else if (nbytes_sent == chunk->data.immortal.len) {
+ php_cli_server_chunk_dtor(chunk);
+ pefree(chunk, 1);
+ sender->buffer.first = next;
+ if (!next) {
+ sender->buffer.last = NULL;
+ }
+ } else {
+ chunk->data.immortal.p += nbytes_sent;
+ chunk->data.immortal.len -= nbytes_sent;
+ }
+ _nbytes_sent_total += nbytes_sent;
+ break;
+ }
+ }
+ *nbytes_sent_total = _nbytes_sent_total;
+ return 0;
+} /* }}} */
+
+static int php_cli_server_content_sender_pull(php_cli_server_content_sender *sender, int fd, size_t *nbytes_read) /* {{{ */
+{
+ ssize_t _nbytes_read;
+ php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(131072);
+
+ _nbytes_read = read(fd, chunk->data.heap.p, chunk->data.heap.len);
+ if (_nbytes_read < 0) {
+ char *errstr = get_last_error();
+ TSRMLS_FETCH();
+ php_cli_server_logf("%s" TSRMLS_CC, errstr);
+ pefree(errstr, 1);
+ php_cli_server_chunk_dtor(chunk);
+ pefree(chunk, 1);
+ return 1;
+ }
+ chunk->data.heap.len = _nbytes_read;
+ php_cli_server_buffer_append(&sender->buffer, chunk);
+ *nbytes_read = _nbytes_read;
+ return 0;
+} /* }}} */
+
+#if HAVE_UNISTD_H
+static int php_cli_is_output_tty() /* {{{ */
+{
+ if (php_cli_output_is_tty == OUTPUT_NOT_CHECKED) {
+ php_cli_output_is_tty = isatty(STDOUT_FILENO);
+ }
+ return php_cli_output_is_tty;
+} /* }}} */
+#endif
+
+static void php_cli_server_log_response(php_cli_server_client *client, int status, const char *message TSRMLS_DC) /* {{{ */
+{
+ int color = 0, effective_status = status;
+ char *basic_buf, *message_buf = "", *error_buf = "";
+ zend_bool append_error_message = 0;
+
+ if (PG(last_error_message)) {
+ switch (PG(last_error_type)) {
+ case E_ERROR:
+ case E_CORE_ERROR:
+ case E_COMPILE_ERROR:
+ case E_USER_ERROR:
+ case E_PARSE:
+ if (status == 200) {
+ /* the status code isn't changed by a fatal error, so fake it */
+ effective_status = 500;
+ }
+
+ append_error_message = 1;
+ break;
+ }
+ }
+
+#if HAVE_UNISTD_H
+ if (CLI_SERVER_G(color) && php_cli_is_output_tty() == OUTPUT_IS_TTY) {
+ if (effective_status >= 500) {
+ /* server error: red */
+ color = 1;
+ } else if (effective_status >= 400) {
+ /* client error: yellow */
+ color = 3;
+ } else if (effective_status >= 200) {
+ /* success: green */
+ color = 2;
+ }
+ }
+#endif
+
+ /* basic */
+ spprintf(&basic_buf, 0, "%s [%d]: %s", client->addr_str, status, client->request.request_uri);
+ if (!basic_buf) {
+ return;
+ }
+
+ /* message */
+ if (message) {
+ spprintf(&message_buf, 0, " - %s", message);
+ if (!message_buf) {
+ efree(basic_buf);
+ return;
+ }
+ }
+
+ /* error */
+ if (append_error_message) {
+ spprintf(&error_buf, 0, " - %s in %s on line %d", PG(last_error_message), PG(last_error_file), PG(last_error_lineno));
+ if (!error_buf) {
+ efree(basic_buf);
+ if (message) {
+ efree(message_buf);
+ }
+ return;
+ }
+ }
+
+ if (color) {
+ php_cli_server_logf("\x1b[3%dm%s%s%s\x1b[0m" TSRMLS_CC, color, basic_buf, message_buf, error_buf);
+ } else {
+ php_cli_server_logf("%s%s%s" TSRMLS_CC, basic_buf, message_buf, error_buf);
+ }
+
+ efree(basic_buf);
+ if (message) {
+ efree(message_buf);
+ }
+ if (append_error_message) {
+ efree(error_buf);
+ }
+} /* }}} */
+
+static void php_cli_server_logf(const char *format TSRMLS_DC, ...) /* {{{ */
+{
+ char *buf = NULL;
+ va_list ap;
+#ifdef ZTS
+ va_start(ap, tsrm_ls);
+#else
+ va_start(ap, format);
+#endif
+ vspprintf(&buf, 0, format, ap);
+ va_end(ap);
+
+ if (!buf) {
+ return;
+ }
+
+ if (sapi_module.log_message) {
+ sapi_module.log_message(buf TSRMLS_CC);
+ }
+
+ efree(buf);
+} /* }}} */
+
+static int php_network_listen_socket(const char *host, int *port, int socktype, int *af, socklen_t *socklen, char **errstr TSRMLS_DC) /* {{{ */
+{
+ int retval = SOCK_ERR;
+ int err = 0;
+ struct sockaddr *sa = NULL, **p, **sal;
+
+ int num_addrs = php_network_getaddresses(host, socktype, &sal, errstr TSRMLS_CC);
+ if (num_addrs == 0) {
+ return -1;
+ }
+ for (p = sal; *p; p++) {
+ if (sa) {
+ pefree(sa, 1);
+ sa = NULL;
+ }
+
+ retval = socket((*p)->sa_family, socktype, 0);
+ if (retval == SOCK_ERR) {
+ continue;
+ }
+
+ switch ((*p)->sa_family) {
+#if HAVE_GETADDRINFO && HAVE_IPV6
+ case AF_INET6:
+ sa = pemalloc(sizeof(struct sockaddr_in6), 1);
+ if (!sa) {
+ closesocket(retval);
+ retval = SOCK_ERR;
+ *errstr = NULL;
+ goto out;
+ }
+ *(struct sockaddr_in6 *)sa = *(struct sockaddr_in6 *)*p;
+ ((struct sockaddr_in6 *)sa)->sin6_port = htons(*port);
+ *socklen = sizeof(struct sockaddr_in6);
+ break;
+#endif
+ case AF_INET:
+ sa = pemalloc(sizeof(struct sockaddr_in), 1);
+ if (!sa) {
+ closesocket(retval);
+ retval = SOCK_ERR;
+ *errstr = NULL;
+ goto out;
+ }
+ *(struct sockaddr_in *)sa = *(struct sockaddr_in *)*p;
+ ((struct sockaddr_in *)sa)->sin_port = htons(*port);
+ *socklen = sizeof(struct sockaddr_in);
+ break;
+ default:
+ /* Unknown family */
+ *socklen = 0;
+ closesocket(retval);
+ continue;
+ }
+
+#ifdef SO_REUSEADDR
+ {
+ int val = 1;
+ setsockopt(retval, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val));
+ }
+#endif
+
+ if (bind(retval, sa, *socklen) == SOCK_CONN_ERR) {
+ err = php_socket_errno();
+ if (err == SOCK_EINVAL || err == SOCK_EADDRINUSE) {
+ goto out;
+ }
+ closesocket(retval);
+ retval = SOCK_ERR;
+ continue;
+ }
+ err = 0;
+
+ *af = sa->sa_family;
+ if (*port == 0) {
+ if (getsockname(retval, sa, socklen)) {
+ err = php_socket_errno();
+ goto out;
+ }
+ switch (sa->sa_family) {
+#if HAVE_GETADDRINFO && HAVE_IPV6
+ case AF_INET6:
+ *port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
+ break;
+#endif
+ case AF_INET:
+ *port = ntohs(((struct sockaddr_in *)sa)->sin_port);
+ break;
+ }
+ }
+
+ break;
+ }
+
+ if (retval == SOCK_ERR) {
+ goto out;
+ }
+
+ if (listen(retval, SOMAXCONN)) {
+ err = php_socket_errno();
+ goto out;
+ }
+
+out:
+ if (sa) {
+ pefree(sa, 1);
+ }
+ if (sal) {
+ php_network_freeaddresses(sal);
+ }
+ if (err) {
+ if (retval >= 0) {
+ closesocket(retval);
+ }
+ if (errstr) {
+ *errstr = php_socket_strerror(err, NULL, 0);
+ }
+ return SOCK_ERR;
+ }
+ return retval;
+} /* }}} */
+
+static int php_cli_server_request_ctor(php_cli_server_request *req) /* {{{ */
+{
+ req->protocol_version = 0;
+ req->request_uri = NULL;
+ req->request_uri_len = 0;
+ req->vpath = NULL;
+ req->vpath_len = 0;
+ req->path_translated = NULL;
+ req->path_translated_len = 0;
+ req->path_info = NULL;
+ req->path_info_len = 0;
+ req->query_string = NULL;
+ req->query_string_len = 0;
+ zend_hash_init(&req->headers, 0, NULL, (void(*)(void*))char_ptr_dtor_p, 1);
+ req->content = NULL;
+ req->content_len = 0;
+ req->ext = NULL;
+ req->ext_len = 0;
+ return SUCCESS;
+} /* }}} */
+
+static void php_cli_server_request_dtor(php_cli_server_request *req) /* {{{ */
+{
+ if (req->request_uri) {
+ pefree(req->request_uri, 1);
+ }
+ if (req->vpath) {
+ pefree(req->vpath, 1);
+ }
+ if (req->path_translated) {
+ pefree(req->path_translated, 1);
+ }
+ if (req->path_info) {
+ pefree(req->path_info, 1);
+ }
+ if (req->query_string) {
+ pefree(req->query_string, 1);
+ }
+ zend_hash_destroy(&req->headers);
+ if (req->content) {
+ pefree(req->content, 1);
+ }
+} /* }}} */
+
+static void php_cli_server_request_translate_vpath(php_cli_server_request *request, const char *document_root, size_t document_root_len) /* {{{ */
+{
+ struct stat sb;
+ static const char *index_files[] = { "index.php", "index.html", NULL };
+ char *buf = safe_pemalloc(1, request->vpath_len, 1 + document_root_len + 1 + sizeof("index.html"), 1);
+ char *p = buf, *prev_path = NULL, *q, *vpath;
+ size_t prev_path_len;
+ int is_static_file = 0;
+
+ if (!buf) {
+ return;
+ }
+
+ memmove(p, document_root, document_root_len);
+ p += document_root_len;
+ vpath = p;
+ if (request->vpath_len > 0 && request->vpath[0] != '/') {
+ *p++ = DEFAULT_SLASH;
+ }
+ q = request->vpath + request->vpath_len;
+ while (q > request->vpath) {
+ if (*q-- == '.') {
+ is_static_file = 1;
+ break;
+ }
+ }
+ memmove(p, request->vpath, request->vpath_len);
+#ifdef PHP_WIN32
+ q = p + request->vpath_len;
+ do {
+ if (*q == '/') {
+ *q = '\\';
+ }
+ } while (q-- > p);
+#endif
+ p += request->vpath_len;
+ *p = '\0';
+ q = p;
+ while (q > buf) {
+ if (!stat(buf, &sb)) {
+ if (sb.st_mode & S_IFDIR) {
+ const char **file = index_files;
+ if (q[-1] != DEFAULT_SLASH) {
+ *q++ = DEFAULT_SLASH;
+ }
+ while (*file) {
+ size_t l = strlen(*file);
+ memmove(q, *file, l + 1);
+ if (!stat(buf, &sb) && (sb.st_mode & S_IFREG)) {
+ q += l;
+ break;
+ }
+ file++;
+ }
+ if (!*file || is_static_file) {
+ if (prev_path) {
+ pefree(prev_path, 1);
+ }
+ pefree(buf, 1);
+ return;
+ }
+ }
+ break; /* regular file */
+ }
+ if (prev_path) {
+ pefree(prev_path, 1);
+ *q = DEFAULT_SLASH;
+ }
+ while (q > buf && *(--q) != DEFAULT_SLASH);
+ prev_path_len = p - q;
+ prev_path = pestrndup(q, prev_path_len, 1);
+ *q = '\0';
+ }
+ if (prev_path) {
+ request->path_info_len = prev_path_len;
+#ifdef PHP_WIN32
+ while (prev_path_len--) {
+ if (prev_path[prev_path_len] == '\\') {
+ prev_path[prev_path_len] = '/';
+ }
+ }
+#endif
+ request->path_info = prev_path;
+ pefree(request->vpath, 1);
+ request->vpath = pestrndup(vpath, q - vpath, 1);
+ request->vpath_len = q - vpath;
+ request->path_translated = buf;
+ request->path_translated_len = q - buf;
+ } else {
+ pefree(request->vpath, 1);
+ request->vpath = pestrndup(vpath, q - vpath, 1);
+ request->vpath_len = q - vpath;
+ request->path_translated = buf;
+ request->path_translated_len = q - buf;
+ }
+#ifdef PHP_WIN32
+ {
+ uint i = 0;
+ for (;i<request->vpath_len;i++) {
+ if (request->vpath[i] == '\\') {
+ request->vpath[i] = '/';
+ }
+ }
+ }
+#endif
+ request->sb = sb;
+} /* }}} */
+
+static void normalize_vpath(char **retval, size_t *retval_len, const char *vpath, size_t vpath_len, int persistent) /* {{{ */
+{
+ char *decoded_vpath = NULL;
+ char *decoded_vpath_end;
+ char *p;
+
+ *retval = NULL;
+
+ decoded_vpath = pestrndup(vpath, vpath_len, persistent);
+ if (!decoded_vpath) {
+ return;
+ }
+
+ decoded_vpath_end = decoded_vpath + php_url_decode(decoded_vpath, vpath_len);
+
+ p = decoded_vpath;
+
+ if (p < decoded_vpath_end && *p == '/') {
+ char *n = p;
+ while (n < decoded_vpath_end && *n == '/') n++;
+ memmove(++p, n, decoded_vpath_end - n);
+ decoded_vpath_end -= n - p;
+ }
+
+ while (p < decoded_vpath_end) {
+ char *n = p;
+ while (n < decoded_vpath_end && *n != '/') n++;
+ if (n - p == 2 && p[0] == '.' && p[1] == '.') {
+ if (p > decoded_vpath) {
+ --p;
+ for (;;) {
+ if (p == decoded_vpath) {
+ if (*p == '/') {
+ p++;
+ }
+ break;
+ }
+ if (*(--p) == '/') {
+ p++;
+ break;
+ }
+ }
+ }
+ while (n < decoded_vpath_end && *n == '/') n++;
+ memmove(p, n, decoded_vpath_end - n);
+ decoded_vpath_end -= n - p;
+ } else if (n - p == 1 && p[0] == '.') {
+ while (n < decoded_vpath_end && *n == '/') n++;
+ memmove(p, n, decoded_vpath_end - n);
+ decoded_vpath_end -= n - p;
+ } else {
+ if (n < decoded_vpath_end) {
+ char *nn = n;
+ while (nn < decoded_vpath_end && *nn == '/') nn++;
+ p = n + 1;
+ memmove(p, nn, decoded_vpath_end - nn);
+ decoded_vpath_end -= nn - p;
+ } else {
+ p = n;
+ }
+ }
+ }
+
+ *decoded_vpath_end = '\0';
+ *retval = decoded_vpath;
+ *retval_len = decoded_vpath_end - decoded_vpath;
+} /* }}} */
+
+/* {{{ php_cli_server_client_read_request */
+static int php_cli_server_client_read_request_on_message_begin(php_http_parser *parser)
+{
+ return 0;
+}
+
+static int php_cli_server_client_read_request_on_path(php_http_parser *parser, const char *at, size_t length)
+{
+ php_cli_server_client *client = parser->data;
+ {
+ char *vpath;
+ size_t vpath_len;
+ normalize_vpath(&vpath, &vpath_len, at, length, 1);
+ client->request.vpath = vpath;
+ client->request.vpath_len = vpath_len;
+ }
+ return 0;
+}
+
+static int php_cli_server_client_read_request_on_query_string(php_http_parser *parser, const char *at, size_t length)
+{
+ php_cli_server_client *client = parser->data;
+ client->request.query_string = pestrndup(at, length, 1);
+ client->request.query_string_len = length;
+ return 0;
+}
+
+static int php_cli_server_client_read_request_on_url(php_http_parser *parser, const char *at, size_t length)
+{
+ php_cli_server_client *client = parser->data;
+ client->request.request_method = parser->method;
+ client->request.request_uri = pestrndup(at, length, 1);
+ client->request.request_uri_len = length;
+ return 0;
+}
+
+static int php_cli_server_client_read_request_on_fragment(php_http_parser *parser, const char *at, size_t length)
+{
+ return 0;
+}
+
+static int php_cli_server_client_read_request_on_header_field(php_http_parser *parser, const char *at, size_t length)
+{
+ php_cli_server_client *client = parser->data;
+ if (client->current_header_name_allocated) {
+ pefree(client->current_header_name, 1);
+ client->current_header_name_allocated = 0;
+ }
+ client->current_header_name = (char *)at;
+ client->current_header_name_len = length;
+ return 0;
+}
+
+static int php_cli_server_client_read_request_on_header_value(php_http_parser *parser, const char *at, size_t length)
+{
+ php_cli_server_client *client = parser->data;
+ char *value = pestrndup(at, length, 1);
+ if (!value) {
+ return 1;
+ }
+ {
+ char *header_name = client->current_header_name;
+ size_t header_name_len = client->current_header_name_len;
+ char c = header_name[header_name_len];
+ header_name[header_name_len] = '\0';
+ zend_hash_add(&client->request.headers, header_name, header_name_len + 1, &value, sizeof(char *), NULL);
+ header_name[header_name_len] = c;
+ }
+
+ if (client->current_header_name_allocated) {
+ pefree(client->current_header_name, 1);
+ client->current_header_name_allocated = 0;
+ }
+ return 0;
+}
+
+static int php_cli_server_client_read_request_on_headers_complete(php_http_parser *parser)
+{
+ php_cli_server_client *client = parser->data;
+ if (client->current_header_name_allocated) {
+ pefree(client->current_header_name, 1);
+ client->current_header_name_allocated = 0;
+ }
+ client->current_header_name = NULL;
+ return 0;
+}
+
+static int php_cli_server_client_read_request_on_body(php_http_parser *parser, const char *at, size_t length)
+{
+ php_cli_server_client *client = parser->data;
+ if (!client->request.content) {
+ client->request.content = pemalloc(parser->content_length, 1);
+ if (!client->request.content) {
+ return -1;
+ }
+ client->request.content_len = 0;
+ }
+ memmove(client->request.content + client->request.content_len, at, length);
+ client->request.content_len += length;
+ return 0;
+}
+
+static int php_cli_server_client_read_request_on_message_complete(php_http_parser *parser)
+{
+ php_cli_server_client *client = parser->data;
+ client->request.protocol_version = parser->http_major * 100 + parser->http_minor;
+ php_cli_server_request_translate_vpath(&client->request, client->server->document_root, client->server->document_root_len);
+ {
+ const char *vpath = client->request.vpath, *end = vpath + client->request.vpath_len, *p = end;
+ client->request.ext = end;
+ client->request.ext_len = 0;
+ while (p > vpath) {
+ --p;
+ if (*p == '.') {
+ ++p;
+ client->request.ext = p;
+ client->request.ext_len = end - p;
+ break;
+ }
+ }
+ }
+ client->request_read = 1;
+ return 0;
+}
+
+static int php_cli_server_client_read_request(php_cli_server_client *client, char **errstr TSRMLS_DC)
+{
+ char buf[16384];
+ static const php_http_parser_settings settings = {
+ php_cli_server_client_read_request_on_message_begin,
+ php_cli_server_client_read_request_on_path,
+ php_cli_server_client_read_request_on_query_string,
+ php_cli_server_client_read_request_on_url,
+ php_cli_server_client_read_request_on_fragment,
+ php_cli_server_client_read_request_on_header_field,
+ php_cli_server_client_read_request_on_header_value,
+ php_cli_server_client_read_request_on_headers_complete,
+ php_cli_server_client_read_request_on_body,
+ php_cli_server_client_read_request_on_message_complete
+ };
+ size_t nbytes_consumed;
+ int nbytes_read;
+ if (client->request_read) {
+ return 1;
+ }
+ nbytes_read = recv(client->sock, buf, sizeof(buf) - 1, 0);
+ if (nbytes_read < 0) {
+ int err = php_socket_errno();
+ if (err == SOCK_EAGAIN) {
+ return 0;
+ }
+ *errstr = php_socket_strerror(err, NULL, 0);
+ return -1;
+ } else if (nbytes_read == 0) {
+ *errstr = estrdup("Unexpected EOF");
+ return -1;
+ }
+ client->parser.data = client;
+ nbytes_consumed = php_http_parser_execute(&client->parser, &settings, buf, nbytes_read);
+ if (nbytes_consumed != nbytes_read) {
+ if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) {
+ *errstr = estrdup("Unsupported SSL request");
+ } else {
+ *errstr = estrdup("Malformed HTTP request");
+ }
+ return -1;
+ }
+ if (client->current_header_name) {
+ char *header_name = safe_pemalloc(client->current_header_name_len, 1, 1, 1);
+ if (!header_name) {
+ return -1;
+ }
+ memmove(header_name, client->current_header_name, client->current_header_name_len);
+ client->current_header_name = header_name;
+ client->current_header_name_allocated = 1;
+ }
+ return client->request_read ? 1: 0;
+}
+/* }}} */
+
+static size_t php_cli_server_client_send_through(php_cli_server_client *client, const char *str, size_t str_len) /* {{{ */
+{
+ struct timeval tv = { 10, 0 };
+ ssize_t nbytes_left = str_len;
+ do {
+ ssize_t nbytes_sent = send(client->sock, str + str_len - nbytes_left, nbytes_left, 0);
+ if (nbytes_sent < 0) {
+ int err = php_socket_errno();
+ if (err == SOCK_EAGAIN) {
+ int nfds = php_pollfd_for(client->sock, POLLOUT, &tv);
+ if (nfds > 0) {
+ continue;
+ } else if (nfds < 0) {
+ /* error */
+ php_handle_aborted_connection();
+ return nbytes_left;
+ } else {
+ /* timeout */
+ php_handle_aborted_connection();
+ return nbytes_left;
+ }
+ } else {
+ php_handle_aborted_connection();
+ return nbytes_left;
+ }
+ }
+ nbytes_left -= nbytes_sent;
+ } while (nbytes_left > 0);
+
+ return str_len;
+} /* }}} */
+
+static void php_cli_server_client_populate_request_info(const php_cli_server_client *client, sapi_request_info *request_info) /* {{{ */
+{
+ char **val;
+
+ request_info->request_method = php_http_method_str(client->request.request_method);
+ request_info->proto_num = client->request.protocol_version;
+ request_info->request_uri = client->request.request_uri;
+ request_info->path_translated = client->request.path_translated;
+ request_info->query_string = client->request.query_string;
+ request_info->post_data = client->request.content;
+ request_info->content_length = request_info->post_data_length = client->request.content_len;
+ request_info->auth_user = request_info->auth_password = request_info->auth_digest = NULL;
+ if (SUCCESS == zend_hash_find(&client->request.headers, "Content-Type", sizeof("Content-Type"), (void**)&val)) {
+ request_info->content_type = *val;
+ }
+} /* }}} */
+
+static void destroy_request_info(sapi_request_info *request_info) /* {{{ */
+{
+} /* }}} */
+
+static int php_cli_server_client_ctor(php_cli_server_client *client, php_cli_server *server, int client_sock, struct sockaddr *addr, socklen_t addr_len TSRMLS_DC) /* {{{ */
+{
+ client->server = server;
+ client->sock = client_sock;
+ client->addr = addr;
+ client->addr_len = addr_len;
+ {
+ char *addr_str = 0;
+ long addr_str_len = 0;
+ php_network_populate_name_from_sockaddr(addr, addr_len, &addr_str, &addr_str_len, NULL, 0 TSRMLS_CC);
+ client->addr_str = pestrndup(addr_str, addr_str_len, 1);
+ client->addr_str_len = addr_str_len;
+ efree(addr_str);
+ }
+ php_http_parser_init(&client->parser, PHP_HTTP_REQUEST);
+ client->request_read = 0;
+ client->current_header_name = NULL;
+ client->current_header_name_len = 0;
+ client->current_header_name_allocated = 0;
+ client->post_read_offset = 0;
+ if (FAILURE == php_cli_server_request_ctor(&client->request)) {
+ return FAILURE;
+ }
+ client->content_sender_initialized = 0;
+ client->file_fd = -1;
+ return SUCCESS;
+} /* }}} */
+
+static void php_cli_server_client_dtor(php_cli_server_client *client) /* {{{ */
+{
+ php_cli_server_request_dtor(&client->request);
+ if (client->file_fd >= 0) {
+ close(client->file_fd);
+ client->file_fd = -1;
+ }
+ pefree(client->addr, 1);
+ pefree(client->addr_str, 1);
+ if (client->content_sender_initialized) {
+ php_cli_server_content_sender_dtor(&client->content_sender);
+ }
+} /* }}} */
+
+static void php_cli_server_close_connection(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
+{
+#ifdef DEBUG
+ php_cli_server_logf("%s Closing" TSRMLS_CC, client->addr_str);
+#endif
+ zend_hash_index_del(&server->clients, client->sock);
+} /* }}} */
+
+static int php_cli_server_send_error_page(php_cli_server *server, php_cli_server_client *client, int status TSRMLS_DC) /* {{{ */
+{
+ char *escaped_request_uri = NULL;
+ size_t escaped_request_uri_len;
+ const char *status_string = get_status_string(status);
+ const char *content_template = get_template_string(status);
+ char *errstr = get_last_error();
+ assert(status_string && content_template);
+
+ php_cli_server_content_sender_ctor(&client->content_sender);
+ client->content_sender_initialized = 1;
+
+ escaped_request_uri = php_escape_html_entities_ex((unsigned char *)client->request.request_uri, client->request.request_uri_len, &escaped_request_uri_len, 0, ENT_QUOTES, NULL, 0 TSRMLS_CC);
+
+ {
+ static const char prologue_template[] = "<!doctype html><html><head><title>%d %s</title>";
+ php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(strlen(prologue_template) + 3 + strlen(status_string) + 1);
+ if (!chunk) {
+ goto fail;
+ }
+ snprintf(chunk->data.heap.p, chunk->data.heap.len, prologue_template, status, status_string, escaped_request_uri);
+ chunk->data.heap.len = strlen(chunk->data.heap.p);
+ php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
+ }
+ {
+ php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(php_cli_server_css, sizeof(php_cli_server_css) - 1);
+ if (!chunk) {
+ goto fail;
+ }
+ php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
+ }
+ {
+ static const char template[] = "</head><body>";
+ php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(template, sizeof(template) - 1);
+ if (!chunk) {
+ goto fail;
+ }
+ php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
+ }
+ {
+ php_cli_server_chunk *chunk = php_cli_server_chunk_heap_new_self_contained(strlen(content_template) + escaped_request_uri_len + 3 + strlen(status_string) + 1);
+ if (!chunk) {
+ goto fail;
+ }
+ snprintf(chunk->data.heap.p, chunk->data.heap.len, content_template, status_string, escaped_request_uri);
+ chunk->data.heap.len = strlen(chunk->data.heap.p);
+ php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
+ }
+ {
+ static const char epilogue_template[] = "</body></html>";
+ php_cli_server_chunk *chunk = php_cli_server_chunk_immortal_new(epilogue_template, sizeof(epilogue_template) - 1);
+ if (!chunk) {
+ goto fail;
+ }
+ php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
+ }
+
+ {
+ php_cli_server_chunk *chunk;
+ smart_str buffer = { 0 };
+ append_http_status_line(&buffer, client->request.protocol_version, status, 1);
+ if (!buffer.c) {
+ /* out of memory */
+ goto fail;
+ }
+ append_essential_headers(&buffer, client, 1);
+ smart_str_appends_ex(&buffer, "Content-Type: text/html; charset=UTF-8\r\n", 1);
+ smart_str_appends_ex(&buffer, "Content-Length: ", 1);
+ smart_str_append_generic_ex(&buffer, php_cli_server_buffer_size(&client->content_sender.buffer), 1, size_t, _unsigned);
+ smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
+ smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
+
+ chunk = php_cli_server_chunk_heap_new(buffer.c, buffer.c, buffer.len);
+ if (!chunk) {
+ smart_str_free_ex(&buffer, 1);
+ goto fail;
+ }
+ php_cli_server_buffer_prepend(&client->content_sender.buffer, chunk);
+ }
+
+ php_cli_server_log_response(client, status, errstr ? errstr : "?" TSRMLS_CC);
+ php_cli_server_poller_add(&server->poller, POLLOUT, client->sock);
+ if (errstr) {
+ pefree(errstr, 1);
+ }
+ efree(escaped_request_uri);
+ return SUCCESS;
+
+fail:
+ if (errstr) {
+ pefree(errstr, 1);
+ }
+ efree(escaped_request_uri);
+ return FAILURE;
+} /* }}} */
+
+static int php_cli_server_dispatch_script(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
+{
+ if (strlen(client->request.path_translated) != client->request.path_translated_len) {
+ /* can't handle paths that contain nul bytes */
+ return php_cli_server_send_error_page(server, client, 400 TSRMLS_CC);
+ }
+ {
+ zend_file_handle zfd;
+ zfd.type = ZEND_HANDLE_FILENAME;
+ zfd.filename = SG(request_info).path_translated;
+ zfd.handle.fp = NULL;
+ zfd.free_filename = 0;
+ zfd.opened_path = NULL;
+ zend_try {
+ php_execute_script(&zfd TSRMLS_CC);
+ } zend_end_try();
+ }
+
+ php_cli_server_log_response(client, SG(sapi_headers).http_response_code, NULL TSRMLS_CC);
+ return SUCCESS;
+} /* }}} */
+
+static int php_cli_server_begin_send_static(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
+{
+ int fd;
+ int status = 200;
+
+ if (client->request.path_translated && strlen(client->request.path_translated) != client->request.path_translated_len) {
+ /* can't handle paths that contain nul bytes */
+ return php_cli_server_send_error_page(server, client, 400 TSRMLS_CC);
+ }
+
+ fd = client->request.path_translated ? open(client->request.path_translated, O_RDONLY): -1;
+ if (fd < 0) {
+ return php_cli_server_send_error_page(server, client, 404 TSRMLS_CC);
+ }
+
+ php_cli_server_content_sender_ctor(&client->content_sender);
+ client->content_sender_initialized = 1;
+ client->file_fd = fd;
+
+ {
+ php_cli_server_chunk *chunk;
+ smart_str buffer = { 0 };
+ const char *mime_type = get_mime_type(client->request.ext, client->request.ext_len);
+ if (!mime_type) {
+ mime_type = "application/octet-stream";
+ }
+
+ append_http_status_line(&buffer, client->request.protocol_version, status, 1);
+ if (!buffer.c) {
+ /* out of memory */
+ php_cli_server_log_response(client, 500, NULL TSRMLS_CC);
+ return FAILURE;
+ }
+ append_essential_headers(&buffer, client, 1);
+ smart_str_appendl_ex(&buffer, "Content-Type: ", sizeof("Content-Type: ") - 1, 1);
+ smart_str_appends_ex(&buffer, mime_type, 1);
+ if (strncmp(mime_type, "text/", 5) == 0) {
+ smart_str_appends_ex(&buffer, "; charset=UTF-8", 1);
+ }
+ smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
+ smart_str_appends_ex(&buffer, "Content-Length: ", 1);
+ smart_str_append_generic_ex(&buffer, client->request.sb.st_size, 1, size_t, _unsigned);
+ smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
+ smart_str_appendl_ex(&buffer, "\r\n", 2, 1);
+ chunk = php_cli_server_chunk_heap_new(buffer.c, buffer.c, buffer.len);
+ if (!chunk) {
+ smart_str_free_ex(&buffer, 1);
+ php_cli_server_log_response(client, 500, NULL TSRMLS_CC);
+ return FAILURE;
+ }
+ php_cli_server_buffer_append(&client->content_sender.buffer, chunk);
+ }
+ php_cli_server_log_response(client, 200, NULL TSRMLS_CC);
+ php_cli_server_poller_add(&server->poller, POLLOUT, client->sock);
+ return SUCCESS;
+}
+/* }}} */
+
+static int php_cli_server_request_startup(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) { /* {{{ */
+ char **auth;
+ php_cli_server_client_populate_request_info(client, &SG(request_info));
+ if (SUCCESS == zend_hash_find(&client->request.headers, "Authorization", sizeof("Authorization"), (void**)&auth)) {
+ php_handle_auth_data(*auth TSRMLS_CC);
+ }
+ SG(sapi_headers).http_response_code = 200;
+ if (FAILURE == php_request_startup(TSRMLS_C)) {
+ /* should never be happen */
+ destroy_request_info(&SG(request_info));
+ return FAILURE;
+ }
+ PG(during_request_startup) = 0;
+
+ return SUCCESS;
+}
+/* }}} */
+
+static int php_cli_server_request_shutdown(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) { /* {{{ */
+ php_request_shutdown(0);
+ php_cli_server_close_connection(server, client TSRMLS_CC);
+ destroy_request_info(&SG(request_info));
+ SG(server_context) = NULL;
+ SG(rfc1867_uploaded_files) = NULL;
+ return SUCCESS;
+}
+/* }}} */
+
+static int php_cli_server_dispatch_router(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
+{
+ int decline = 0;
+ if (!php_handle_special_queries(TSRMLS_C)) {
+ zend_file_handle zfd;
+ char *old_cwd;
+
+ ALLOCA_FLAG(use_heap)
+ old_cwd = do_alloca(MAXPATHLEN, use_heap);
+ old_cwd[0] = '\0';
+ php_ignore_value(VCWD_GETCWD(old_cwd, MAXPATHLEN - 1));
+
+ zfd.type = ZEND_HANDLE_FILENAME;
+ zfd.filename = server->router;
+ zfd.handle.fp = NULL;
+ zfd.free_filename = 0;
+ zfd.opened_path = NULL;
+
+ zend_try {
+ zval *retval = NULL;
+ if (SUCCESS == zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, &retval, 1, &zfd)) {
+ if (retval) {
+ decline = Z_TYPE_P(retval) == IS_BOOL && !Z_LVAL_P(retval);
+ zval_ptr_dtor(&retval);
+ }
+ } else {
+ decline = 1;
+ }
+ } zend_end_try();
+
+ if (old_cwd[0] != '\0') {
+ php_ignore_value(VCWD_CHDIR(old_cwd));
+ }
+
+ free_alloca(old_cwd, use_heap);
+ }
+
+ return decline;
+}
+/* }}} */
+
+static int php_cli_server_dispatch(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
+{
+ int is_static_file = 0;
+
+ SG(server_context) = client;
+ if (client->request.ext_len != 3 || memcmp(client->request.ext, "php", 3) || !client->request.path_translated) {
+ is_static_file = 1;
+ }
+
+ if (server->router || !is_static_file) {
+ if (FAILURE == php_cli_server_request_startup(server, client TSRMLS_CC)) {
+ SG(server_context) = NULL;
+ php_cli_server_close_connection(server, client TSRMLS_CC);
+ destroy_request_info(&SG(request_info));
+ return SUCCESS;
+ }
+ }
+
+ if (server->router) {
+ if (!php_cli_server_dispatch_router(server, client TSRMLS_CC)) {
+ php_cli_server_request_shutdown(server, client TSRMLS_CC);
+ return SUCCESS;
+ }
+ }
+
+ if (!is_static_file) {
+ if (SUCCESS == php_cli_server_dispatch_script(server, client TSRMLS_CC)
+ || SUCCESS != php_cli_server_send_error_page(server, client, 500 TSRMLS_CC)) {
+ php_cli_server_request_shutdown(server, client TSRMLS_CC);
+ return SUCCESS;
+ }
+ } else {
+ if (server->router) {
+ static int (*send_header_func)(sapi_headers_struct * TSRMLS_DC);
+ send_header_func = sapi_module.send_headers;
+ /* do not generate default content type header */
+ SG(sapi_headers).send_default_content_type = 0;
+ /* we don't want headers to be sent */
+ sapi_module.send_headers = sapi_cli_server_discard_headers;
+ php_request_shutdown(0);
+ sapi_module.send_headers = send_header_func;
+ SG(sapi_headers).send_default_content_type = 1;
+ SG(rfc1867_uploaded_files) = NULL;
+ }
+ if (SUCCESS != php_cli_server_begin_send_static(server, client TSRMLS_CC)) {
+ php_cli_server_close_connection(server, client TSRMLS_CC);
+ }
+ SG(server_context) = NULL;
+ return SUCCESS;
+ }
+
+ SG(server_context) = NULL;
+ destroy_request_info(&SG(request_info));
+ return SUCCESS;
+}
+/* }}} */
+
+static void php_cli_server_dtor(php_cli_server *server TSRMLS_DC) /* {{{ */
+{
+ zend_hash_destroy(&server->clients);
+ if (server->server_sock >= 0) {
+ closesocket(server->server_sock);
+ }
+ if (server->host) {
+ pefree(server->host, 1);
+ }
+ if (server->document_root) {
+ pefree(server->document_root, 1);
+ }
+ if (server->router) {
+ pefree(server->router, 1);
+ }
+} /* }}} */
+
+static void php_cli_server_client_dtor_wrapper(php_cli_server_client **p) /* {{{ */
+{
+ closesocket((*p)->sock);
+ php_cli_server_poller_remove(&(*p)->server->poller, POLLIN | POLLOUT, (*p)->sock);
+ php_cli_server_client_dtor(*p);
+ pefree(*p, 1);
+} /* }}} */
+
+static int php_cli_server_ctor(php_cli_server *server, const char *addr, const char *document_root, const char *router TSRMLS_DC) /* {{{ */
+{
+ int retval = SUCCESS;
+ char *host = NULL;
+ char *errstr = NULL;
+ char *_document_root = NULL;
+ char *_router = NULL;
+ int err = 0;
+ int port = 3000;
+ php_socket_t server_sock = SOCK_ERR;
+ char *p = NULL;
+
+ if (addr[0] == '[') {
+ host = pestrdup(addr + 1, 1);
+ if (!host) {
+ return FAILURE;
+ }
+ p = strchr(host, ']');
+ if (p) {
+ *p++ = '\0';
+ if (*p == ':') {
+ port = strtol(p + 1, &p, 10);
+ if (port <= 0) {
+ p = NULL;
+ }
+ } else if (*p != '\0') {
+ p = NULL;
+ }
+ }
+ } else {
+ host = pestrdup(addr, 1);
+ if (!host) {
+ return FAILURE;
+ }
+ p = strchr(host, ':');
+ if (p) {
+ *p++ = '\0';
+ port = strtol(p, &p, 10);
+ if (port <= 0) {
+ p = NULL;
+ }
+ }
+ }
+ if (!p) {
+ fprintf(stderr, "Invalid address: %s\n", addr);
+ retval = FAILURE;
+ goto out;
+ }
+
+ server_sock = php_network_listen_socket(host, &port, SOCK_STREAM, &server->address_family, &server->socklen, &errstr TSRMLS_CC);
+ if (server_sock == SOCK_ERR) {
+ php_cli_server_logf("Failed to listen on %s:%d (reason: %s)" TSRMLS_CC, host, port, errstr ? errstr: "?");
+ efree(errstr);
+ retval = FAILURE;
+ goto out;
+ }
+ server->server_sock = server_sock;
+
+ err = php_cli_server_poller_ctor(&server->poller);
+ if (SUCCESS != err) {
+ goto out;
+ }
+
+ php_cli_server_poller_add(&server->poller, POLLIN, server_sock);
+
+ server->host = host;
+ server->port = port;
+
+ zend_hash_init(&server->clients, 0, NULL, (void(*)(void*))php_cli_server_client_dtor_wrapper, 1);
+
+ {
+ size_t document_root_len = strlen(document_root);
+ _document_root = pestrndup(document_root, document_root_len, 1);
+ if (!_document_root) {
+ retval = FAILURE;
+ goto out;
+ }
+ server->document_root = _document_root;
+ server->document_root_len = document_root_len;
+ }
+
+ if (router) {
+ size_t router_len = strlen(router);
+ _router = pestrndup(router, router_len, 1);
+ if (!_router) {
+ retval = FAILURE;
+ goto out;
+ }
+ server->router = _router;
+ server->router_len = router_len;
+ } else {
+ server->router = NULL;
+ server->router_len = 0;
+ }
+
+ server->is_running = 1;
+out:
+ if (retval != SUCCESS) {
+ if (host) {
+ pefree(host, 1);
+ }
+ if (_document_root) {
+ pefree(_document_root, 1);
+ }
+ if (_router) {
+ pefree(_router, 1);
+ }
+ if (server_sock >= -1) {
+ closesocket(server_sock);
+ }
+ }
+ return retval;
+} /* }}} */
+
+static int php_cli_server_recv_event_read_request(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
+{
+ char *errstr = NULL;
+ int status = php_cli_server_client_read_request(client, &errstr TSRMLS_CC);
+ if (status < 0) {
+ php_cli_server_logf("%s Invalid request (%s)" TSRMLS_CC, client->addr_str, errstr);
+ efree(errstr);
+ php_cli_server_close_connection(server, client TSRMLS_CC);
+ return FAILURE;
+ } else if (status == 1 && client->request.request_method == PHP_HTTP_NOT_IMPLEMENTED) {
+ return php_cli_server_send_error_page(server, client, 501 TSRMLS_CC);
+ } else if (status == 1) {
+ php_cli_server_poller_remove(&server->poller, POLLIN, client->sock);
+ php_cli_server_dispatch(server, client TSRMLS_CC);
+ } else {
+ php_cli_server_poller_add(&server->poller, POLLIN, client->sock);
+ }
+
+ return SUCCESS;
+} /* }}} */
+
+static int php_cli_server_send_event(php_cli_server *server, php_cli_server_client *client TSRMLS_DC) /* {{{ */
+{
+ if (client->content_sender_initialized) {
+ if (client->file_fd >= 0 && !client->content_sender.buffer.first) {
+ size_t nbytes_read;
+ if (php_cli_server_content_sender_pull(&client->content_sender, client->file_fd, &nbytes_read)) {
+ php_cli_server_close_connection(server, client TSRMLS_CC);
+ return FAILURE;
+ }
+ if (nbytes_read == 0) {
+ close(client->file_fd);
+ client->file_fd = -1;
+ }
+ }
+ {
+ size_t nbytes_sent;
+ int err = php_cli_server_content_sender_send(&client->content_sender, client->sock, &nbytes_sent);
+ if (err && err != SOCK_EAGAIN) {
+ php_cli_server_close_connection(server, client TSRMLS_CC);
+ return FAILURE;
+ }
+ }
+ if (!client->content_sender.buffer.first && client->file_fd < 0) {
+ php_cli_server_close_connection(server, client TSRMLS_CC);
+ }
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+typedef struct php_cli_server_do_event_for_each_fd_callback_params {
+#ifdef ZTS
+ void ***tsrm_ls;
+#endif
+ php_cli_server *server;
+ int(*rhandler)(php_cli_server*, php_cli_server_client* TSRMLS_DC);
+ int(*whandler)(php_cli_server*, php_cli_server_client* TSRMLS_DC);
+} php_cli_server_do_event_for_each_fd_callback_params;
+
+static int php_cli_server_do_event_for_each_fd_callback(void *_params, int fd, int event) /* {{{ */
+{
+ php_cli_server_do_event_for_each_fd_callback_params *params = _params;
+#ifdef ZTS
+ void ***tsrm_ls = params->tsrm_ls;
+#endif
+ php_cli_server *server = params->server;
+ if (server->server_sock == fd) {
+ php_cli_server_client *client = NULL;
+ php_socket_t client_sock;
+ socklen_t socklen = server->socklen;
+ struct sockaddr *sa = pemalloc(server->socklen, 1);
+ if (!sa) {
+ return FAILURE;
+ }
+ client_sock = accept(server->server_sock, sa, &socklen);
+ if (client_sock < 0) {
+ char *errstr;
+ errstr = php_socket_strerror(php_socket_errno(), NULL, 0);
+ php_cli_server_logf("Failed to accept a client (reason: %s)" TSRMLS_CC, errstr);
+ efree(errstr);
+ pefree(sa, 1);
+ return SUCCESS;
+ }
+ if (SUCCESS != php_set_sock_blocking(client_sock, 0 TSRMLS_CC)) {
+ pefree(sa, 1);
+ closesocket(client_sock);
+ return SUCCESS;
+ }
+ if (!(client = pemalloc(sizeof(php_cli_server_client), 1)) || FAILURE == php_cli_server_client_ctor(client, server, client_sock, sa, socklen TSRMLS_CC)) {
+ php_cli_server_logf("Failed to create a new request object" TSRMLS_CC);
+ pefree(sa, 1);
+ closesocket(client_sock);
+ return SUCCESS;
+ }
+#ifdef DEBUG
+ php_cli_server_logf("%s Accepted" TSRMLS_CC, client->addr_str);
+#endif
+ zend_hash_index_update(&server->clients, client_sock, &client, sizeof(client), NULL);
+ php_cli_server_recv_event_read_request(server, client TSRMLS_CC);
+ } else {
+ php_cli_server_client **client;
+ if (SUCCESS == zend_hash_index_find(&server->clients, fd, (void **)&client)) {
+ if (event & POLLIN) {
+ params->rhandler(server, *client TSRMLS_CC);
+ }
+ if (event & POLLOUT) {
+ params->whandler(server, *client TSRMLS_CC);
+ }
+ }
+ }
+ return SUCCESS;
+} /* }}} */
+
+static void php_cli_server_do_event_for_each_fd(php_cli_server *server, int(*rhandler)(php_cli_server*, php_cli_server_client* TSRMLS_DC), int(*whandler)(php_cli_server*, php_cli_server_client* TSRMLS_DC) TSRMLS_DC) /* {{{ */
+{
+ php_cli_server_do_event_for_each_fd_callback_params params = {
+#ifdef ZTS
+ tsrm_ls,
+#endif
+ server,
+ rhandler,
+ whandler
+ };
+
+ php_cli_server_poller_iter_on_active(&server->poller, &params, php_cli_server_do_event_for_each_fd_callback);
+} /* }}} */
+
+static int php_cli_server_do_event_loop(php_cli_server *server TSRMLS_DC) /* {{{ */
+{
+ int retval = SUCCESS;
+ while (server->is_running) {
+ static const struct timeval tv = { 1, 0 };
+ int n = php_cli_server_poller_poll(&server->poller, &tv);
+ if (n > 0) {
+ php_cli_server_do_event_for_each_fd(server,
+ php_cli_server_recv_event_read_request,
+ php_cli_server_send_event TSRMLS_CC);
+ } else if (n == 0) {
+ /* do nothing */
+ } else {
+ int err = php_socket_errno();
+ if (err != SOCK_EINTR) {
+ char *errstr = php_socket_strerror(err, NULL, 0);
+ php_cli_server_logf("%s" TSRMLS_CC, errstr);
+ efree(errstr);
+ retval = FAILURE;
+ goto out;
+ }
+ }
+ }
+out:
+ return retval;
+} /* }}} */
+
+static php_cli_server server;
+
+static void php_cli_server_sigint_handler(int sig) /* {{{ */
+{
+ server.is_running = 0;
+}
+/* }}} */
+
+int do_cli_server(int argc, char **argv TSRMLS_DC) /* {{{ */
+{
+ char *php_optarg = NULL;
+ int php_optind = 1;
+ int c;
+ const char *server_bind_address = NULL;
+ extern const opt_struct OPTIONS[];
+ const char *document_root = NULL;
+ const char *router = NULL;
+ char document_root_buf[MAXPATHLEN];
+
+ while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2))!=-1) {
+ switch (c) {
+ case 'S':
+ server_bind_address = php_optarg;
+ break;
+ case 't':
+ document_root = php_optarg;
+ break;
+ }
+ }
+
+ if (document_root) {
+ struct stat sb;
+
+ if (stat(document_root, &sb)) {
+ fprintf(stderr, "Directory %s does not exist.\n", document_root);
+ return 1;
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ fprintf(stderr, "%s is not a directory.\n", document_root);
+ return 1;
+ }
+ if (VCWD_REALPATH(document_root, document_root_buf)) {
+ document_root = document_root_buf;
+ }
+ } else {
+ char *ret = NULL;
+
+#if HAVE_GETCWD
+ ret = VCWD_GETCWD(document_root_buf, MAXPATHLEN);
+#elif HAVE_GETWD
+ ret = VCWD_GETWD(document_root_buf);
+#endif
+ document_root = ret ? document_root_buf: ".";
+ }
+
+ if (argc > php_optind) {
+ router = argv[php_optind];
+ }
+
+ if (FAILURE == php_cli_server_ctor(&server, server_bind_address, document_root, router TSRMLS_CC)) {
+ return 1;
+ }
+ sapi_module.phpinfo_as_text = 0;
+
+ {
+ struct timeval tv;
+ struct tm tm;
+ char buf[52];
+ gettimeofday(&tv, NULL);
+ php_localtime_r(&tv.tv_sec, &tm);
+ php_asctime_r(&tm, buf);
+ printf("PHP %s Development Server started at %s"
+ "Listening on http://%s\n"
+ "Document root is %s\n"
+ "Press Ctrl-C to quit.\n",
+ PHP_VERSION, buf, server_bind_address, document_root);
+ }
+
+#if defined(HAVE_SIGNAL_H) && defined(SIGINT)
+ signal(SIGINT, php_cli_server_sigint_handler);
+#endif
+ php_cli_server_do_event_loop(&server TSRMLS_CC);
+ php_cli_server_dtor(&server TSRMLS_CC);
+ return 0;
+} /* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/sapi/cli/php_cli_server.h b/sapi/cli/php_cli_server.h
new file mode 100644
index 0000000..ed716f9
--- /dev/null
+++ b/sapi/cli/php_cli_server.h
@@ -0,0 +1,48 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Moriyoshi Koizumi <moriyoshi@php.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: php_cli.c 306938 2011-01-01 02:17:06Z felipe $ */
+
+#ifndef PHP_CLI_SERVER_H
+#define PHP_CLI_SERVER_H
+
+#include "SAPI.h"
+
+extern sapi_module_struct cli_server_sapi_module;
+extern int do_cli_server(int argc, char **argv TSRMLS_DC);
+
+ZEND_BEGIN_MODULE_GLOBALS(cli_server)
+ short color;
+ZEND_END_MODULE_GLOBALS(cli_server)
+
+#ifdef ZTS
+#define CLI_SERVER_G(v) TSRMG(cli_server_globals_id, zend_cli_server_globals *, v)
+#else
+#define CLI_SERVER_G(v) (cli_server_globals.v)
+#endif
+
+#endif /* PHP_CLI_SERVER_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
diff --git a/sapi/cli/php_http_parser.c b/sapi/cli/php_http_parser.c
new file mode 100644
index 0000000..d3bc496
--- /dev/null
+++ b/sapi/cli/php_http_parser.c
@@ -0,0 +1,1608 @@
+/* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include <assert.h>
+#include <stddef.h>
+#include "php_http_parser.h"
+
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+
+#define CALLBACK2(FOR) \
+do { \
+ if (settings->on_##FOR) { \
+ if (0 != settings->on_##FOR(parser)) return (p - data); \
+ } \
+} while (0)
+
+
+#define MARK(FOR) \
+do { \
+ FOR##_mark = p; \
+} while (0)
+
+#define CALLBACK_NOCLEAR(FOR) \
+do { \
+ if (FOR##_mark) { \
+ if (settings->on_##FOR) { \
+ if (0 != settings->on_##FOR(parser, \
+ FOR##_mark, \
+ p - FOR##_mark)) \
+ { \
+ return (p - data); \
+ } \
+ } \
+ } \
+} while (0)
+
+#ifdef PHP_WIN32
+# undef CALLBACK
+#endif
+#define CALLBACK(FOR) \
+do { \
+ CALLBACK_NOCLEAR(FOR); \
+ FOR##_mark = NULL; \
+} while (0)
+
+
+#define PROXY_CONNECTION "proxy-connection"
+#define CONNECTION "connection"
+#define CONTENT_LENGTH "content-length"
+#define TRANSFER_ENCODING "transfer-encoding"
+#define UPGRADE "upgrade"
+#define CHUNKED "chunked"
+#define KEEP_ALIVE "keep-alive"
+#define CLOSE "close"
+
+
+static const char *method_strings[] =
+ { "DELETE"
+ , "GET"
+ , "HEAD"
+ , "POST"
+ , "PUT"
+ , "PATCH"
+ , "CONNECT"
+ , "OPTIONS"
+ , "TRACE"
+ , "COPY"
+ , "LOCK"
+ , "MKCOL"
+ , "MOVE"
+ , "PROPFIND"
+ , "PROPPATCH"
+ , "UNLOCK"
+ , "REPORT"
+ , "MKACTIVITY"
+ , "CHECKOUT"
+ , "MERGE"
+ , "M-SEARCH"
+ , "NOTIFY"
+ , "SUBSCRIBE"
+ , "UNSUBSCRIBE"
+ , "NOTIMPLEMENTED"
+ };
+
+
+/* Tokens as defined by rfc 2616. Also lowercases them.
+ * token = 1*<any CHAR except CTLs or separators>
+ * separators = "(" | ")" | "<" | ">" | "@"
+ * | "," | ";" | ":" | "\" | <">
+ * | "/" | "[" | "]" | "?" | "="
+ * | "{" | "}" | SP | HT
+ */
+static const char tokens[256] = {
+/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
+ ' ', '!', '"', '#', '$', '%', '&', '\'',
+/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
+ 0, 0, '*', '+', 0, '-', '.', '/',
+/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
+ '0', '1', '2', '3', '4', '5', '6', '7',
+/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
+ '8', '9', 0, 0, 0, 0, 0, 0,
+/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
+ 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
+ 'x', 'y', 'z', 0, 0, 0, '^', '_',
+/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
+ 'x', 'y', 'z', 0, '|', '}', '~', 0 };
+
+
+static const int8_t unhex[256] =
+ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
+ ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ };
+
+
+static const uint8_t normal_url_char[256] = {
+/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
+ 0, 1, 1, 0, 1, 1, 1, 1,
+/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
+ 1, 1, 1, 1, 1, 1, 1, 0,
+/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
+ 1, 1, 1, 1, 1, 1, 1, 1,
+/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
+ 1, 1, 1, 1, 1, 1, 1, 0 };
+
+
+enum state
+ { s_dead = 1 /* important that this is > 0 */
+
+ , s_start_req_or_res
+ , s_res_or_resp_H
+ , s_start_res
+ , s_res_H
+ , s_res_HT
+ , s_res_HTT
+ , s_res_HTTP
+ , s_res_first_http_major
+ , s_res_http_major
+ , s_res_first_http_minor
+ , s_res_http_minor
+ , s_res_first_status_code
+ , s_res_status_code
+ , s_res_status
+ , s_res_line_almost_done
+
+ , s_start_req
+
+ , s_req_method
+ , s_req_spaces_before_url
+ , s_req_schema
+ , s_req_schema_slash
+ , s_req_schema_slash_slash
+ , s_req_host
+ , s_req_port
+ , s_req_path
+ , s_req_query_string_start
+ , s_req_query_string
+ , s_req_fragment_start
+ , s_req_fragment
+ , s_req_http_start
+ , s_req_http_H
+ , s_req_http_HT
+ , s_req_http_HTT
+ , s_req_http_HTTP
+ , s_req_first_http_major
+ , s_req_http_major
+ , s_req_first_http_minor
+ , s_req_http_minor
+ , s_req_line_almost_done
+
+ , s_header_field_start
+ , s_header_field
+ , s_header_value_start
+ , s_header_value
+
+ , s_header_almost_done
+
+ , s_headers_almost_done
+ /* Important: 's_headers_almost_done' must be the last 'header' state. All
+ * states beyond this must be 'body' states. It is used for overflow
+ * checking. See the PARSING_HEADER() macro.
+ */
+ , s_chunk_size_start
+ , s_chunk_size
+ , s_chunk_size_almost_done
+ , s_chunk_parameters
+ , s_chunk_data
+ , s_chunk_data_almost_done
+ , s_chunk_data_done
+
+ , s_body_identity
+ , s_body_identity_eof
+ };
+
+
+#define PARSING_HEADER(state) (state <= s_headers_almost_done && 0 == (parser->flags & F_TRAILING))
+
+
+enum header_states
+ { h_general = 0
+ , h_C
+ , h_CO
+ , h_CON
+
+ , h_matching_connection
+ , h_matching_proxy_connection
+ , h_matching_content_length
+ , h_matching_transfer_encoding
+ , h_matching_upgrade
+
+ , h_connection
+ , h_content_length
+ , h_transfer_encoding
+ , h_upgrade
+
+ , h_matching_transfer_encoding_chunked
+ , h_matching_connection_keep_alive
+ , h_matching_connection_close
+
+ , h_transfer_encoding_chunked
+ , h_connection_keep_alive
+ , h_connection_close
+ };
+
+
+enum flags
+ { F_CHUNKED = 1 << 0
+ , F_CONNECTION_KEEP_ALIVE = 1 << 1
+ , F_CONNECTION_CLOSE = 1 << 2
+ , F_TRAILING = 1 << 3
+ , F_UPGRADE = 1 << 4
+ , F_SKIPBODY = 1 << 5
+ };
+
+
+#define CR '\r'
+#define LF '\n'
+#define LOWER(c) (unsigned char)(c | 0x20)
+#define TOKEN(c) tokens[(unsigned char)c]
+
+
+#define start_state (parser->type == PHP_HTTP_REQUEST ? s_start_req : s_start_res)
+
+
+#if HTTP_PARSER_STRICT
+# define STRICT_CHECK(cond) if (cond) goto error
+# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
+#else
+# define STRICT_CHECK(cond)
+# define NEW_MESSAGE() start_state
+#endif
+
+
+size_t php_http_parser_execute (php_http_parser *parser,
+ const php_http_parser_settings *settings,
+ const char *data,
+ size_t len)
+{
+ char c, ch;
+ const char *p = data, *pe;
+ size_t to_read;
+
+ enum state state = (enum state) parser->state;
+ enum header_states header_state = (enum header_states) parser->header_state;
+ uint32_t index = parser->index;
+ uint32_t nread = parser->nread;
+
+ /* technically we could combine all of these (except for url_mark) into one
+ variable, saving stack space, but it seems more clear to have them
+ separated. */
+ const char *header_field_mark = 0;
+ const char *header_value_mark = 0;
+ const char *fragment_mark = 0;
+ const char *query_string_mark = 0;
+ const char *path_mark = 0;
+ const char *url_mark = 0;
+
+ if (len == 0) {
+ if (state == s_body_identity_eof) {
+ CALLBACK2(message_complete);
+ }
+ return 0;
+ }
+
+ if (state == s_header_field)
+ header_field_mark = data;
+ if (state == s_header_value)
+ header_value_mark = data;
+ if (state == s_req_fragment)
+ fragment_mark = data;
+ if (state == s_req_query_string)
+ query_string_mark = data;
+ if (state == s_req_path)
+ path_mark = data;
+ if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash
+ || state == s_req_schema_slash_slash || state == s_req_port
+ || state == s_req_query_string_start || state == s_req_query_string
+ || state == s_req_host
+ || state == s_req_fragment_start || state == s_req_fragment)
+ url_mark = data;
+
+ for (p=data, pe=data+len; p != pe; p++) {
+ ch = *p;
+
+ if (PARSING_HEADER(state)) {
+ ++nread;
+ /* Buffer overflow attack */
+ if (nread > PHP_HTTP_MAX_HEADER_SIZE) goto error;
+ }
+
+ switch (state) {
+
+ case s_dead:
+ /* this state is used after a 'Connection: close' message
+ * the parser will error out if it reads another message
+ */
+ goto error;
+
+ case s_start_req_or_res:
+ {
+ if (ch == CR || ch == LF)
+ break;
+ parser->flags = 0;
+ parser->content_length = -1;
+
+ CALLBACK2(message_begin);
+
+ if (ch == 'H')
+ state = s_res_or_resp_H;
+ else {
+ parser->type = PHP_HTTP_REQUEST;
+ goto start_req_method_assign;
+ }
+ break;
+ }
+
+ case s_res_or_resp_H:
+ if (ch == 'T') {
+ parser->type = PHP_HTTP_RESPONSE;
+ state = s_res_HT;
+ } else {
+ if (ch != 'E') goto error;
+ parser->type = PHP_HTTP_REQUEST;
+ parser->method = PHP_HTTP_HEAD;
+ index = 2;
+ state = s_req_method;
+ }
+ break;
+
+ case s_start_res:
+ {
+ parser->flags = 0;
+ parser->content_length = -1;
+
+ CALLBACK2(message_begin);
+
+ switch (ch) {
+ case 'H':
+ state = s_res_H;
+ break;
+
+ case CR:
+ case LF:
+ break;
+
+ default:
+ goto error;
+ }
+ break;
+ }
+
+ case s_res_H:
+ STRICT_CHECK(ch != 'T');
+ state = s_res_HT;
+ break;
+
+ case s_res_HT:
+ STRICT_CHECK(ch != 'T');
+ state = s_res_HTT;
+ break;
+
+ case s_res_HTT:
+ STRICT_CHECK(ch != 'P');
+ state = s_res_HTTP;
+ break;
+
+ case s_res_HTTP:
+ STRICT_CHECK(ch != '/');
+ state = s_res_first_http_major;
+ break;
+
+ case s_res_first_http_major:
+ if (ch < '1' || ch > '9') goto error;
+ parser->http_major = ch - '0';
+ state = s_res_http_major;
+ break;
+
+ /* major HTTP version or dot */
+ case s_res_http_major:
+ {
+ if (ch == '.') {
+ state = s_res_first_http_minor;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') goto error;
+
+ parser->http_major *= 10;
+ parser->http_major += ch - '0';
+
+ if (parser->http_major > 999) goto error;
+ break;
+ }
+
+ /* first digit of minor HTTP version */
+ case s_res_first_http_minor:
+ if (ch < '0' || ch > '9') goto error;
+ parser->http_minor = ch - '0';
+ state = s_res_http_minor;
+ break;
+
+ /* minor HTTP version or end of request line */
+ case s_res_http_minor:
+ {
+ if (ch == ' ') {
+ state = s_res_first_status_code;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') goto error;
+
+ parser->http_minor *= 10;
+ parser->http_minor += ch - '0';
+
+ if (parser->http_minor > 999) goto error;
+ break;
+ }
+
+ case s_res_first_status_code:
+ {
+ if (ch < '0' || ch > '9') {
+ if (ch == ' ') {
+ break;
+ }
+ goto error;
+ }
+ parser->status_code = ch - '0';
+ state = s_res_status_code;
+ break;
+ }
+
+ case s_res_status_code:
+ {
+ if (ch < '0' || ch > '9') {
+ switch (ch) {
+ case ' ':
+ state = s_res_status;
+ break;
+ case CR:
+ state = s_res_line_almost_done;
+ break;
+ case LF:
+ state = s_header_field_start;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ }
+
+ parser->status_code *= 10;
+ parser->status_code += ch - '0';
+
+ if (parser->status_code > 999) goto error;
+ break;
+ }
+
+ case s_res_status:
+ /* the human readable status. e.g. "NOT FOUND"
+ * we are not humans so just ignore this */
+ if (ch == CR) {
+ state = s_res_line_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ state = s_header_field_start;
+ break;
+ }
+ break;
+
+ case s_res_line_almost_done:
+ STRICT_CHECK(ch != LF);
+ state = s_header_field_start;
+ break;
+
+ case s_start_req:
+ {
+ if (ch == CR || ch == LF)
+ break;
+ parser->flags = 0;
+ parser->content_length = -1;
+
+ CALLBACK2(message_begin);
+
+ if (ch < 'A' || 'Z' < ch) goto error;
+
+ start_req_method_assign:
+ parser->method = (enum php_http_method) 0;
+ index = 1;
+ switch (ch) {
+ case 'C': parser->method = PHP_HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
+ case 'D': parser->method = PHP_HTTP_DELETE; break;
+ case 'G': parser->method = PHP_HTTP_GET; break;
+ case 'H': parser->method = PHP_HTTP_HEAD; break;
+ case 'L': parser->method = PHP_HTTP_LOCK; break;
+ case 'M': parser->method = PHP_HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
+ case 'N': parser->method = PHP_HTTP_NOTIFY; break;
+ case 'O': parser->method = PHP_HTTP_OPTIONS; break;
+ case 'P': parser->method = PHP_HTTP_POST; /* or PROPFIND or PROPPATCH or PUT */ break;
+ case 'R': parser->method = PHP_HTTP_REPORT; break;
+ case 'S': parser->method = PHP_HTTP_SUBSCRIBE; break;
+ case 'T': parser->method = PHP_HTTP_TRACE; break;
+ case 'U': parser->method = PHP_HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
+ default: parser->method = PHP_HTTP_NOT_IMPLEMENTED; break;
+ }
+ state = s_req_method;
+ break;
+ }
+
+ case s_req_method:
+ {
+ const char *matcher;
+ if (ch == '\0')
+ goto error;
+
+ matcher = method_strings[parser->method];
+ if (ch == ' ' && (matcher[index] == '\0' || parser->method == PHP_HTTP_NOT_IMPLEMENTED)) {
+ state = s_req_spaces_before_url;
+ } else if (ch == matcher[index]) {
+ ; /* nada */
+ } else if (parser->method == PHP_HTTP_CONNECT) {
+ if (index == 1 && ch == 'H') {
+ parser->method = PHP_HTTP_CHECKOUT;
+ } else if (index == 2 && ch == 'P') {
+ parser->method = PHP_HTTP_COPY;
+ }
+ } else if (parser->method == PHP_HTTP_MKCOL) {
+ if (index == 1 && ch == 'O') {
+ parser->method = PHP_HTTP_MOVE;
+ } else if (index == 1 && ch == 'E') {
+ parser->method = PHP_HTTP_MERGE;
+ } else if (index == 1 && ch == '-') {
+ parser->method = PHP_HTTP_MSEARCH;
+ } else if (index == 2 && ch == 'A') {
+ parser->method = PHP_HTTP_MKACTIVITY;
+ }
+ } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'R') {
+ parser->method = PHP_HTTP_PROPFIND; /* or HTTP_PROPPATCH */
+ } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'U') {
+ parser->method = PHP_HTTP_PUT;
+ } else if (index == 1 && parser->method == PHP_HTTP_POST && ch == 'A') {
+ parser->method = PHP_HTTP_PATCH;
+ } else if (index == 2 && parser->method == PHP_HTTP_UNLOCK && ch == 'S') {
+ parser->method = PHP_HTTP_UNSUBSCRIBE;
+ } else if (index == 4 && parser->method == PHP_HTTP_PROPFIND && ch == 'P') {
+ parser->method = PHP_HTTP_PROPPATCH;
+ } else {
+ parser->method = PHP_HTTP_NOT_IMPLEMENTED;
+ }
+
+ ++index;
+ break;
+ }
+ case s_req_spaces_before_url:
+ {
+ if (ch == ' ') break;
+
+ if (ch == '/' || ch == '*') {
+ MARK(url);
+ MARK(path);
+ state = s_req_path;
+ break;
+ }
+
+ c = LOWER(ch);
+
+ if (c >= 'a' && c <= 'z') {
+ MARK(url);
+ state = s_req_schema;
+ break;
+ }
+
+ goto error;
+ }
+
+ case s_req_schema:
+ {
+ c = LOWER(ch);
+
+ if (c >= 'a' && c <= 'z') break;
+
+ if (ch == ':') {
+ state = s_req_schema_slash;
+ break;
+ } else if (ch == '.') {
+ state = s_req_host;
+ break;
+ } else if ('0' <= ch && ch <= '9') {
+ state = s_req_host;
+ break;
+ }
+
+ goto error;
+ }
+
+ case s_req_schema_slash:
+ STRICT_CHECK(ch != '/');
+ state = s_req_schema_slash_slash;
+ break;
+
+ case s_req_schema_slash_slash:
+ STRICT_CHECK(ch != '/');
+ state = s_req_host;
+ break;
+
+ case s_req_host:
+ {
+ c = LOWER(ch);
+ if (c >= 'a' && c <= 'z') break;
+ if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') break;
+ switch (ch) {
+ case ':':
+ state = s_req_port;
+ break;
+ case '/':
+ MARK(path);
+ state = s_req_path;
+ break;
+ case ' ':
+ /* The request line looks like:
+ * "GET http://foo.bar.com HTTP/1.1"
+ * That is, there is no path.
+ */
+ CALLBACK(url);
+ state = s_req_http_start;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ }
+
+ case s_req_port:
+ {
+ if (ch >= '0' && ch <= '9') break;
+ switch (ch) {
+ case '/':
+ MARK(path);
+ state = s_req_path;
+ break;
+ case ' ':
+ /* The request line looks like:
+ * "GET http://foo.bar.com:1234 HTTP/1.1"
+ * That is, there is no path.
+ */
+ CALLBACK(url);
+ state = s_req_http_start;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ }
+
+ case s_req_path:
+ {
+ if (normal_url_char[(unsigned char)ch]) break;
+
+ switch (ch) {
+ case ' ':
+ CALLBACK(url);
+ CALLBACK(path);
+ state = s_req_http_start;
+ break;
+ case CR:
+ CALLBACK(url);
+ CALLBACK(path);
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ state = s_req_line_almost_done;
+ break;
+ case LF:
+ CALLBACK(url);
+ CALLBACK(path);
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ state = s_header_field_start;
+ break;
+ case '?':
+ CALLBACK(path);
+ state = s_req_query_string_start;
+ break;
+ case '#':
+ CALLBACK(path);
+ state = s_req_fragment_start;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ }
+
+ case s_req_query_string_start:
+ {
+ if (normal_url_char[(unsigned char)ch]) {
+ MARK(query_string);
+ state = s_req_query_string;
+ break;
+ }
+
+ switch (ch) {
+ case '?':
+ break; /* XXX ignore extra '?' ... is this right? */
+ case ' ':
+ CALLBACK(url);
+ state = s_req_http_start;
+ break;
+ case CR:
+ CALLBACK(url);
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ state = s_req_line_almost_done;
+ break;
+ case LF:
+ CALLBACK(url);
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ state = s_header_field_start;
+ break;
+ case '#':
+ state = s_req_fragment_start;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ }
+
+ case s_req_query_string:
+ {
+ if (normal_url_char[(unsigned char)ch]) break;
+
+ switch (ch) {
+ case '?':
+ /* allow extra '?' in query string */
+ break;
+ case ' ':
+ CALLBACK(url);
+ CALLBACK(query_string);
+ state = s_req_http_start;
+ break;
+ case CR:
+ CALLBACK(url);
+ CALLBACK(query_string);
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ state = s_req_line_almost_done;
+ break;
+ case LF:
+ CALLBACK(url);
+ CALLBACK(query_string);
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ state = s_header_field_start;
+ break;
+ case '#':
+ CALLBACK(query_string);
+ state = s_req_fragment_start;
+ break;
+ default:
+ goto error;
+ }
+ break;
+ }
+
+ case s_req_fragment_start:
+ {
+ if (normal_url_char[(unsigned char)ch]) {
+ MARK(fragment);
+ state = s_req_fragment;
+ break;
+ }
+
+ switch (ch) {
+ case ' ':
+ CALLBACK(url);
+ state = s_req_http_start;
+ break;
+ case CR:
+ CALLBACK(url);
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ state = s_req_line_almost_done;
+ break;
+ case LF:
+ CALLBACK(url);
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ state = s_header_field_start;
+ break;
+ case '?':
+ MARK(fragment);
+ state = s_req_fragment;
+ break;
+ case '#':
+ break;
+ default:
+ goto error;
+ }
+ break;
+ }
+
+ case s_req_fragment:
+ {
+ if (normal_url_char[(unsigned char)ch]) break;
+
+ switch (ch) {
+ case ' ':
+ CALLBACK(url);
+ CALLBACK(fragment);
+ state = s_req_http_start;
+ break;
+ case CR:
+ CALLBACK(url);
+ CALLBACK(fragment);
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ state = s_req_line_almost_done;
+ break;
+ case LF:
+ CALLBACK(url);
+ CALLBACK(fragment);
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ state = s_header_field_start;
+ break;
+ case '?':
+ case '#':
+ break;
+ default:
+ goto error;
+ }
+ break;
+ }
+
+ case s_req_http_start:
+ switch (ch) {
+ case 'H':
+ state = s_req_http_H;
+ break;
+ case ' ':
+ break;
+ default:
+ goto error;
+ }
+ break;
+
+ case s_req_http_H:
+ STRICT_CHECK(ch != 'T');
+ state = s_req_http_HT;
+ break;
+
+ case s_req_http_HT:
+ STRICT_CHECK(ch != 'T');
+ state = s_req_http_HTT;
+ break;
+
+ case s_req_http_HTT:
+ STRICT_CHECK(ch != 'P');
+ state = s_req_http_HTTP;
+ break;
+
+ case s_req_http_HTTP:
+ STRICT_CHECK(ch != '/');
+ state = s_req_first_http_major;
+ break;
+
+ /* first digit of major HTTP version */
+ case s_req_first_http_major:
+ if (ch < '1' || ch > '9') goto error;
+ parser->http_major = ch - '0';
+ state = s_req_http_major;
+ break;
+
+ /* major HTTP version or dot */
+ case s_req_http_major:
+ {
+ if (ch == '.') {
+ state = s_req_first_http_minor;
+ break;
+ }
+
+ if (ch < '0' || ch > '9') goto error;
+
+ parser->http_major *= 10;
+ parser->http_major += ch - '0';
+
+ if (parser->http_major > 999) goto error;
+ break;
+ }
+
+ /* first digit of minor HTTP version */
+ case s_req_first_http_minor:
+ if (ch < '0' || ch > '9') goto error;
+ parser->http_minor = ch - '0';
+ state = s_req_http_minor;
+ break;
+
+ /* minor HTTP version or end of request line */
+ case s_req_http_minor:
+ {
+ if (ch == CR) {
+ state = s_req_line_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ state = s_header_field_start;
+ break;
+ }
+
+ /* XXX allow spaces after digit? */
+
+ if (ch < '0' || ch > '9') goto error;
+
+ parser->http_minor *= 10;
+ parser->http_minor += ch - '0';
+
+ if (parser->http_minor > 999) goto error;
+ break;
+ }
+
+ /* end of request line */
+ case s_req_line_almost_done:
+ {
+ if (ch != LF) goto error;
+ state = s_header_field_start;
+ break;
+ }
+
+ case s_header_field_start:
+ {
+ if (ch == CR) {
+ state = s_headers_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ /* they might be just sending \n instead of \r\n so this would be
+ * the second \n to denote the end of headers*/
+ state = s_headers_almost_done;
+ goto headers_almost_done;
+ }
+
+ c = TOKEN(ch);
+
+ if (!c) goto error;
+
+ MARK(header_field);
+
+ index = 0;
+ state = s_header_field;
+
+ switch (c) {
+ case 'c':
+ header_state = h_C;
+ break;
+
+ case 'p':
+ header_state = h_matching_proxy_connection;
+ break;
+
+ case 't':
+ header_state = h_matching_transfer_encoding;
+ break;
+
+ case 'u':
+ header_state = h_matching_upgrade;
+ break;
+
+ default:
+ header_state = h_general;
+ break;
+ }
+ break;
+ }
+
+ case s_header_field:
+ {
+ c = TOKEN(ch);
+
+ if (c) {
+ switch (header_state) {
+ case h_general:
+ break;
+
+ case h_C:
+ index++;
+ header_state = (c == 'o' ? h_CO : h_general);
+ break;
+
+ case h_CO:
+ index++;
+ header_state = (c == 'n' ? h_CON : h_general);
+ break;
+
+ case h_CON:
+ index++;
+ switch (c) {
+ case 'n':
+ header_state = h_matching_connection;
+ break;
+ case 't':
+ header_state = h_matching_content_length;
+ break;
+ default:
+ header_state = h_general;
+ break;
+ }
+ break;
+
+ /* connection */
+
+ case h_matching_connection:
+ index++;
+ if (index > sizeof(CONNECTION)-1
+ || c != CONNECTION[index]) {
+ header_state = h_general;
+ } else if (index == sizeof(CONNECTION)-2) {
+ header_state = h_connection;
+ }
+ break;
+
+ /* proxy-connection */
+
+ case h_matching_proxy_connection:
+ index++;
+ if (index > sizeof(PROXY_CONNECTION)-1
+ || c != PROXY_CONNECTION[index]) {
+ header_state = h_general;
+ } else if (index == sizeof(PROXY_CONNECTION)-2) {
+ header_state = h_connection;
+ }
+ break;
+
+ /* content-length */
+
+ case h_matching_content_length:
+ index++;
+ if (index > sizeof(CONTENT_LENGTH)-1
+ || c != CONTENT_LENGTH[index]) {
+ header_state = h_general;
+ } else if (index == sizeof(CONTENT_LENGTH)-2) {
+ header_state = h_content_length;
+ }
+ break;
+
+ /* transfer-encoding */
+
+ case h_matching_transfer_encoding:
+ index++;
+ if (index > sizeof(TRANSFER_ENCODING)-1
+ || c != TRANSFER_ENCODING[index]) {
+ header_state = h_general;
+ } else if (index == sizeof(TRANSFER_ENCODING)-2) {
+ header_state = h_transfer_encoding;
+ }
+ break;
+
+ /* upgrade */
+
+ case h_matching_upgrade:
+ index++;
+ if (index > sizeof(UPGRADE)-1
+ || c != UPGRADE[index]) {
+ header_state = h_general;
+ } else if (index == sizeof(UPGRADE)-2) {
+ header_state = h_upgrade;
+ }
+ break;
+
+ case h_connection:
+ case h_content_length:
+ case h_transfer_encoding:
+ case h_upgrade:
+ if (ch != ' ') header_state = h_general;
+ break;
+
+ default:
+ assert(0 && "Unknown header_state");
+ break;
+ }
+ break;
+ }
+
+ if (ch == ':') {
+ CALLBACK(header_field);
+ state = s_header_value_start;
+ break;
+ }
+
+ if (ch == CR) {
+ state = s_header_almost_done;
+ CALLBACK(header_field);
+ break;
+ }
+
+ if (ch == LF) {
+ CALLBACK(header_field);
+ state = s_header_field_start;
+ break;
+ }
+
+ goto error;
+ }
+
+ case s_header_value_start:
+ {
+ if (ch == ' ') break;
+
+ MARK(header_value);
+
+ state = s_header_value;
+ index = 0;
+
+ c = LOWER(ch);
+
+ if (ch == CR) {
+ CALLBACK(header_value);
+ header_state = h_general;
+ state = s_header_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ CALLBACK(header_value);
+ state = s_header_field_start;
+ break;
+ }
+
+ switch (header_state) {
+ case h_upgrade:
+ parser->flags |= F_UPGRADE;
+ header_state = h_general;
+ break;
+
+ case h_transfer_encoding:
+ /* looking for 'Transfer-Encoding: chunked' */
+ if ('c' == c) {
+ header_state = h_matching_transfer_encoding_chunked;
+ } else {
+ header_state = h_general;
+ }
+ break;
+
+ case h_content_length:
+ if (ch < '0' || ch > '9') goto error;
+ parser->content_length = ch - '0';
+ break;
+
+ case h_connection:
+ /* looking for 'Connection: keep-alive' */
+ if (c == 'k') {
+ header_state = h_matching_connection_keep_alive;
+ /* looking for 'Connection: close' */
+ } else if (c == 'c') {
+ header_state = h_matching_connection_close;
+ } else {
+ header_state = h_general;
+ }
+ break;
+
+ default:
+ header_state = h_general;
+ break;
+ }
+ break;
+ }
+
+ case s_header_value:
+ {
+ c = LOWER(ch);
+
+ if (ch == CR) {
+ CALLBACK(header_value);
+ state = s_header_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ CALLBACK(header_value);
+ goto header_almost_done;
+ }
+
+ switch (header_state) {
+ case h_general:
+ break;
+
+ case h_connection:
+ case h_transfer_encoding:
+ assert(0 && "Shouldn't get here.");
+ break;
+
+ case h_content_length:
+ if (ch == ' ') break;
+ if (ch < '0' || ch > '9') goto error;
+ parser->content_length *= 10;
+ parser->content_length += ch - '0';
+ break;
+
+ /* Transfer-Encoding: chunked */
+ case h_matching_transfer_encoding_chunked:
+ index++;
+ if (index > sizeof(CHUNKED)-1
+ || c != CHUNKED[index]) {
+ header_state = h_general;
+ } else if (index == sizeof(CHUNKED)-2) {
+ header_state = h_transfer_encoding_chunked;
+ }
+ break;
+
+ /* looking for 'Connection: keep-alive' */
+ case h_matching_connection_keep_alive:
+ index++;
+ if (index > sizeof(KEEP_ALIVE)-1
+ || c != KEEP_ALIVE[index]) {
+ header_state = h_general;
+ } else if (index == sizeof(KEEP_ALIVE)-2) {
+ header_state = h_connection_keep_alive;
+ }
+ break;
+
+ /* looking for 'Connection: close' */
+ case h_matching_connection_close:
+ index++;
+ if (index > sizeof(CLOSE)-1 || c != CLOSE[index]) {
+ header_state = h_general;
+ } else if (index == sizeof(CLOSE)-2) {
+ header_state = h_connection_close;
+ }
+ break;
+
+ case h_transfer_encoding_chunked:
+ case h_connection_keep_alive:
+ case h_connection_close:
+ if (ch != ' ') header_state = h_general;
+ break;
+
+ default:
+ state = s_header_value;
+ header_state = h_general;
+ break;
+ }
+ break;
+ }
+
+ case s_header_almost_done:
+ header_almost_done:
+ {
+ STRICT_CHECK(ch != LF);
+
+ state = s_header_field_start;
+
+ switch (header_state) {
+ case h_connection_keep_alive:
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ break;
+ case h_connection_close:
+ parser->flags |= F_CONNECTION_CLOSE;
+ break;
+ case h_transfer_encoding_chunked:
+ parser->flags |= F_CHUNKED;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case s_headers_almost_done:
+ headers_almost_done:
+ {
+ STRICT_CHECK(ch != LF);
+
+ if (parser->flags & F_TRAILING) {
+ /* End of a chunked request */
+ CALLBACK2(message_complete);
+ state = NEW_MESSAGE();
+ break;
+ }
+
+ nread = 0;
+
+ if (parser->flags & F_UPGRADE || parser->method == PHP_HTTP_CONNECT) {
+ parser->upgrade = 1;
+ }
+
+ /* Here we call the headers_complete callback. This is somewhat
+ * different than other callbacks because if the user returns 1, we
+ * will interpret that as saying that this message has no body. This
+ * is needed for the annoying case of recieving a response to a HEAD
+ * request.
+ */
+ if (settings->on_headers_complete) {
+ switch (settings->on_headers_complete(parser)) {
+ case 0:
+ break;
+
+ case 1:
+ parser->flags |= F_SKIPBODY;
+ break;
+
+ default:
+ return p - data; /* Error */
+ }
+ }
+
+ /* Exit, the rest of the connect is in a different protocol. */
+ if (parser->upgrade) {
+ CALLBACK2(message_complete);
+ return (p - data);
+ }
+
+ if (parser->flags & F_SKIPBODY) {
+ CALLBACK2(message_complete);
+ state = NEW_MESSAGE();
+ } else if (parser->flags & F_CHUNKED) {
+ /* chunked encoding - ignore Content-Length header */
+ state = s_chunk_size_start;
+ } else {
+ if (parser->content_length == 0) {
+ /* Content-Length header given but zero: Content-Length: 0\r\n */
+ CALLBACK2(message_complete);
+ state = NEW_MESSAGE();
+ } else if (parser->content_length > 0) {
+ /* Content-Length header given and non-zero */
+ state = s_body_identity;
+ } else {
+ if (parser->type == PHP_HTTP_REQUEST || php_http_should_keep_alive(parser)) {
+ /* Assume content-length 0 - read the next */
+ CALLBACK2(message_complete);
+ state = NEW_MESSAGE();
+ } else {
+ /* Read body until EOF */
+ state = s_body_identity_eof;
+ }
+ }
+ }
+
+ break;
+ }
+
+ case s_body_identity:
+ to_read = MIN(pe - p, (size_t)parser->content_length);
+ if (to_read > 0) {
+ if (settings->on_body) settings->on_body(parser, p, to_read);
+ p += to_read - 1;
+ parser->content_length -= to_read;
+ if (parser->content_length == 0) {
+ CALLBACK2(message_complete);
+ state = NEW_MESSAGE();
+ }
+ }
+ break;
+
+ /* read until EOF */
+ case s_body_identity_eof:
+ to_read = pe - p;
+ if (to_read > 0) {
+ if (settings->on_body) settings->on_body(parser, p, to_read);
+ p += to_read - 1;
+ }
+ break;
+
+ case s_chunk_size_start:
+ {
+ assert(parser->flags & F_CHUNKED);
+
+ c = unhex[(unsigned char)ch];
+ if (c == -1) goto error;
+ parser->content_length = c;
+ state = s_chunk_size;
+ break;
+ }
+
+ case s_chunk_size:
+ {
+ assert(parser->flags & F_CHUNKED);
+
+ if (ch == CR) {
+ state = s_chunk_size_almost_done;
+ break;
+ }
+
+ c = unhex[(unsigned char)ch];
+
+ if (c == -1) {
+ if (ch == ';' || ch == ' ') {
+ state = s_chunk_parameters;
+ break;
+ }
+ goto error;
+ }
+
+ parser->content_length *= 16;
+ parser->content_length += c;
+ break;
+ }
+
+ case s_chunk_parameters:
+ {
+ assert(parser->flags & F_CHUNKED);
+ /* just ignore this shit. TODO check for overflow */
+ if (ch == CR) {
+ state = s_chunk_size_almost_done;
+ break;
+ }
+ break;
+ }
+
+ case s_chunk_size_almost_done:
+ {
+ assert(parser->flags & F_CHUNKED);
+ STRICT_CHECK(ch != LF);
+
+ if (parser->content_length == 0) {
+ parser->flags |= F_TRAILING;
+ state = s_header_field_start;
+ } else {
+ state = s_chunk_data;
+ }
+ break;
+ }
+
+ case s_chunk_data:
+ {
+ assert(parser->flags & F_CHUNKED);
+
+ to_read = MIN(pe - p, (size_t)(parser->content_length));
+
+ if (to_read > 0) {
+ if (settings->on_body) settings->on_body(parser, p, to_read);
+ p += to_read - 1;
+ }
+
+ if (to_read == parser->content_length) {
+ state = s_chunk_data_almost_done;
+ }
+
+ parser->content_length -= to_read;
+ break;
+ }
+
+ case s_chunk_data_almost_done:
+ assert(parser->flags & F_CHUNKED);
+ STRICT_CHECK(ch != CR);
+ state = s_chunk_data_done;
+ break;
+
+ case s_chunk_data_done:
+ assert(parser->flags & F_CHUNKED);
+ STRICT_CHECK(ch != LF);
+ state = s_chunk_size_start;
+ break;
+
+ default:
+ assert(0 && "unhandled state");
+ goto error;
+ }
+ }
+
+ CALLBACK_NOCLEAR(header_field);
+ CALLBACK_NOCLEAR(header_value);
+ CALLBACK_NOCLEAR(fragment);
+ CALLBACK_NOCLEAR(query_string);
+ CALLBACK_NOCLEAR(path);
+ CALLBACK_NOCLEAR(url);
+
+ parser->state = state;
+ parser->header_state = header_state;
+ parser->index = index;
+ parser->nread = nread;
+
+ return len;
+
+error:
+ parser->state = s_dead;
+ return (p - data);
+}
+
+
+int
+php_http_should_keep_alive (php_http_parser *parser)
+{
+ if (parser->http_major > 0 && parser->http_minor > 0) {
+ /* HTTP/1.1 */
+ if (parser->flags & F_CONNECTION_CLOSE) {
+ return 0;
+ } else {
+ return 1;
+ }
+ } else {
+ /* HTTP/1.0 or earlier */
+ if (parser->flags & F_CONNECTION_KEEP_ALIVE) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+
+const char * php_http_method_str (enum php_http_method m)
+{
+ return method_strings[m];
+}
+
+
+void
+php_http_parser_init (php_http_parser *parser, enum php_http_parser_type t)
+{
+ parser->type = t;
+ parser->state = (t == PHP_HTTP_REQUEST ? s_start_req : (t == PHP_HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
+ parser->nread = 0;
+ parser->upgrade = 0;
+ parser->flags = 0;
+ parser->method = 0;
+}
diff --git a/sapi/cli/php_http_parser.h b/sapi/cli/php_http_parser.h
new file mode 100644
index 0000000..2bf2356
--- /dev/null
+++ b/sapi/cli/php_http_parser.h
@@ -0,0 +1,180 @@
+/* Copyright 2009,2010 Ryan Dahl <ry@tinyclouds.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+/* modified by Moriyoshi Koizumi <moriyoshi@php.net> to make it fit to PHP source tree. */
+#ifndef php_http_parser_h
+#define php_http_parser_h
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include <sys/types.h>
+#if defined(_WIN32) && !defined(__MINGW32__)
+# include <windows.h>
+# include "win32/php_stdint.h"
+# include "config.w32.h"
+#else
+# include "php_config.h"
+# ifdef HAVE_STDINT_H
+# include <stdint.h>
+# endif
+#endif
+
+/* Compile with -DPHP_HTTP_PARSER_STRICT=0 to make less checks, but run
+ * faster
+ */
+#ifndef PHP_HTTP_PARSER_STRICT
+# define PHP_HTTP_PARSER_STRICT 1
+#else
+# define PHP_HTTP_PARSER_STRICT 0
+#endif
+
+
+/* Maximium header size allowed */
+#define PHP_HTTP_MAX_HEADER_SIZE (80*1024)
+
+
+typedef struct php_http_parser php_http_parser;
+typedef struct php_http_parser_settings php_http_parser_settings;
+
+
+/* Callbacks should return non-zero to indicate an error. The parser will
+ * then halt execution.
+ *
+ * The one exception is on_headers_complete. In a PHP_HTTP_RESPONSE parser
+ * returning '1' from on_headers_complete will tell the parser that it
+ * should not expect a body. This is used when receiving a response to a
+ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
+ * chunked' headers that indicate the presence of a body.
+ *
+ * http_data_cb does not return data chunks. It will be call arbitrarally
+ * many times for each string. E.G. you might get 10 callbacks for "on_path"
+ * each providing just a few characters more data.
+ */
+typedef int (*php_http_data_cb) (php_http_parser*, const char *at, size_t length);
+typedef int (*php_http_cb) (php_http_parser*);
+
+
+/* Request Methods */
+enum php_http_method
+ { PHP_HTTP_DELETE = 0
+ , PHP_HTTP_GET
+ , PHP_HTTP_HEAD
+ , PHP_HTTP_POST
+ , PHP_HTTP_PUT
+ , PHP_HTTP_PATCH
+ /* pathological */
+ , PHP_HTTP_CONNECT
+ , PHP_HTTP_OPTIONS
+ , PHP_HTTP_TRACE
+ /* webdav */
+ , PHP_HTTP_COPY
+ , PHP_HTTP_LOCK
+ , PHP_HTTP_MKCOL
+ , PHP_HTTP_MOVE
+ , PHP_HTTP_PROPFIND
+ , PHP_HTTP_PROPPATCH
+ , PHP_HTTP_UNLOCK
+ /* subversion */
+ , PHP_HTTP_REPORT
+ , PHP_HTTP_MKACTIVITY
+ , PHP_HTTP_CHECKOUT
+ , PHP_HTTP_MERGE
+ /* upnp */
+ , PHP_HTTP_MSEARCH
+ , PHP_HTTP_NOTIFY
+ , PHP_HTTP_SUBSCRIBE
+ , PHP_HTTP_UNSUBSCRIBE
+ /* unknown, not implemented */
+ , PHP_HTTP_NOT_IMPLEMENTED
+ };
+
+
+enum php_http_parser_type { PHP_HTTP_REQUEST, PHP_HTTP_RESPONSE, PHP_HTTP_BOTH };
+
+
+struct php_http_parser {
+ /** PRIVATE **/
+ unsigned char type : 2;
+ unsigned char flags : 6;
+ unsigned char state;
+ unsigned char header_state;
+ unsigned char index;
+
+ uint32_t nread;
+ ssize_t content_length;
+
+ /** READ-ONLY **/
+ unsigned short http_major;
+ unsigned short http_minor;
+ unsigned short status_code; /* responses only */
+ unsigned char method; /* requests only */
+
+ /* 1 = Upgrade header was present and the parser has exited because of that.
+ * 0 = No upgrade header present.
+ * Should be checked when http_parser_execute() returns in addition to
+ * error checking.
+ */
+ char upgrade;
+
+ /** PUBLIC **/
+ void *data; /* A pointer to get hook to the "connection" or "socket" object */
+};
+
+
+struct php_http_parser_settings {
+ php_http_cb on_message_begin;
+ php_http_data_cb on_path;
+ php_http_data_cb on_query_string;
+ php_http_data_cb on_url;
+ php_http_data_cb on_fragment;
+ php_http_data_cb on_header_field;
+ php_http_data_cb on_header_value;
+ php_http_cb on_headers_complete;
+ php_http_data_cb on_body;
+ php_http_cb on_message_complete;
+};
+
+
+void php_http_parser_init(php_http_parser *parser, enum php_http_parser_type type);
+
+
+size_t php_http_parser_execute(php_http_parser *parser,
+ const php_http_parser_settings *settings,
+ const char *data,
+ size_t len);
+
+
+/* If php_http_should_keep_alive() in the on_headers_complete or
+ * on_message_complete callback returns true, then this will be should be
+ * the last message on the connection.
+ * If you are the server, respond with the "Connection: close" header.
+ * If you are the client, close the connection.
+ */
+int php_http_should_keep_alive(php_http_parser *parser);
+
+/* Returns a string version of the HTTP method. */
+const char *php_http_method_str(enum php_http_method);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/sapi/cli/tests/001.phpt b/sapi/cli/tests/001.phpt
new file mode 100644
index 0000000..6fbd608
--- /dev/null
+++ b/sapi/cli/tests/001.phpt
@@ -0,0 +1,19 @@
+--TEST--
+version string
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+var_dump(`$php -n -v`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "PHP %s (cli) (built: %s)%s
+Copyright (c) 1997-20%d The PHP Group
+Zend Engine v%s, Copyright (c) 1998-20%d Zend Technologies
+"
+Done
diff --git a/sapi/cli/tests/002-win32.phpt b/sapi/cli/tests/002-win32.phpt
new file mode 100644
index 0000000..5a00c67
--- /dev/null
+++ b/sapi/cli/tests/002-win32.phpt
@@ -0,0 +1,22 @@
+--TEST--
+running code with -r
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) != 'WIN') {
+ die ("skip only for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+var_dump(`$php -n -r "var_dump('hello');"`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(18) "string(5) "hello"
+"
+Done
diff --git a/sapi/cli/tests/002.phpt b/sapi/cli/tests/002.phpt
new file mode 100644
index 0000000..be2b633
--- /dev/null
+++ b/sapi/cli/tests/002.phpt
@@ -0,0 +1,22 @@
+--TEST--
+running code with -r
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+var_dump(`$php -n -r 'var_dump("hello");'`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(18) "string(5) "hello"
+"
+Done
diff --git a/sapi/cli/tests/003-2.phpt b/sapi/cli/tests/003-2.phpt
new file mode 100644
index 0000000..2ed9b07
--- /dev/null
+++ b/sapi/cli/tests/003-2.phpt
@@ -0,0 +1,25 @@
+--TEST--
+defining INI options with -d (as 2nd arg)
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+var_dump(`"$php" -nd max_execution_time=111 -r 'var_dump(ini_get("max_execution_time"));'`);
+var_dump(`"$php" -nd max_execution_time=500 -r 'var_dump(ini_get("max_execution_time"));'`);
+
+?>
+===DONE===
+--EXPECTF--
+string(16) "string(3) "111"
+"
+string(16) "string(3) "500"
+"
+===DONE===
diff --git a/sapi/cli/tests/003.phpt b/sapi/cli/tests/003.phpt
new file mode 100644
index 0000000..d62360e
--- /dev/null
+++ b/sapi/cli/tests/003.phpt
@@ -0,0 +1,32 @@
+--TEST--
+defining INI options with -d
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+var_dump(`$php -n -d max_execution_time=111 -r 'var_dump(ini_get("max_execution_time"));'`);
+var_dump(`$php -n -d max_execution_time=500 -r 'var_dump(ini_get("max_execution_time"));'`);
+var_dump(`$php -n -d max_execution_time=500 -d max_execution_time=555 -r 'var_dump(ini_get("max_execution_time"));'`);
+var_dump(`$php -n -d upload_tmp_dir=/test/path -d max_execution_time=555 -r 'var_dump(ini_get("max_execution_time")); var_dump(ini_get("upload_tmp_dir"));'`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(16) "string(3) "111"
+"
+string(16) "string(3) "500"
+"
+string(16) "string(3) "555"
+"
+string(40) "string(3) "555"
+string(10) "/test/path"
+"
+Done
diff --git a/sapi/cli/tests/004.phpt b/sapi/cli/tests/004.phpt
new file mode 100644
index 0000000..378515d
--- /dev/null
+++ b/sapi/cli/tests/004.phpt
@@ -0,0 +1,34 @@
+--TEST--
+show information about function
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (!extension_loaded("reflection")) {
+ die("skip reflection extension required");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+var_dump(`$php -n --rf unknown`);
+var_dump(`$php -n --rf echo`);
+var_dump(`$php -n --rf phpinfo`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(45) "Exception: Function unknown() does not exist
+"
+string(42) "Exception: Function echo() does not exist
+"
+string(119) "Function [ <internal:standard> function phpinfo ] {
+
+ - Parameters [1] {
+ Parameter #0 [ <optional> $what ]
+ }
+}
+
+"
+Done
diff --git a/sapi/cli/tests/005.phpt b/sapi/cli/tests/005.phpt
new file mode 100644
index 0000000..84b0f98
--- /dev/null
+++ b/sapi/cli/tests/005.phpt
@@ -0,0 +1,104 @@
+--TEST--
+show information about class
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (!extension_loaded("reflection")) {
+ die("skip reflection extension required");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+var_dump(`"$php" -n --rc unknown`);
+var_dump(`"$php" -n --rc stdclass`);
+var_dump(`"$php" -n --rc exception`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(40) "Exception: Class unknown does not exist
+"
+string(183) "Class [ <internal:Core> class stdClass ] {
+
+ - Constants [0] {
+ }
+
+ - Static properties [0] {
+ }
+
+ - Static methods [0] {
+ }
+
+ - Properties [0] {
+ }
+
+ - Methods [0] {
+ }
+}
+
+"
+string(1355) "Class [ <internal:Core> class Exception ] {
+
+ - Constants [0] {
+ }
+
+ - Static properties [0] {
+ }
+
+ - Static methods [0] {
+ }
+
+ - Properties [7] {
+ Property [ <default> protected $message ]
+ Property [ <default> private $string ]
+ Property [ <default> protected $code ]
+ Property [ <default> protected $file ]
+ Property [ <default> protected $line ]
+ Property [ <default> private $trace ]
+ Property [ <default> private $previous ]
+ }
+
+ - Methods [10] {
+ Method [ <internal:Core> final private method __clone ] {
+ }
+
+ Method [ <internal:Core, ctor> public method __construct ] {
+
+ - Parameters [3] {
+ Parameter #0 [ <optional> $message ]
+ Parameter #1 [ <optional> $code ]
+ Parameter #2 [ <optional> $previous ]
+ }
+ }
+
+ Method [ <internal:Core> final public method getMessage ] {
+ }
+
+ Method [ <internal:Core> final public method getCode ] {
+ }
+
+ Method [ <internal:Core> final public method getFile ] {
+ }
+
+ Method [ <internal:Core> final public method getLine ] {
+ }
+
+ Method [ <internal:Core> final public method getTrace ] {
+ }
+
+ Method [ <internal:Core> final public method getPrevious ] {
+ }
+
+ Method [ <internal:Core> final public method getTraceAsString ] {
+ }
+
+ Method [ <internal:Core> public method __toString ] {
+ }
+ }
+}
+
+"
+Done
diff --git a/sapi/cli/tests/006.phpt b/sapi/cli/tests/006.phpt
new file mode 100644
index 0000000..3d5c916
--- /dev/null
+++ b/sapi/cli/tests/006.phpt
@@ -0,0 +1,140 @@
+--TEST--
+show information about extension
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (!extension_loaded("reflection") || !extension_loaded("session")) {
+ die("skip reflection and session extensions required");
+}
+?>
+--INI--
+date.timezone=
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+var_dump(`$php -n --re unknown`);
+var_dump(`$php -n --re ""`);
+var_dump(`$php -n --re pcre`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(44) "Exception: Extension unknown does not exist
+"
+string(37) "Exception: Extension does not exist
+"
+string(%d) "Extension [ <persistent> extension #%d pcre version <no_version> ] {
+
+ - INI {
+ Entry [ pcre.backtrack_limit <ALL> ]
+ Current = '%d'
+ }
+ Entry [ pcre.recursion_limit <ALL> ]
+ Current = '%d'
+ }
+ }
+
+ - Constants [14] {
+ Constant [ integer PREG_PATTERN_ORDER ] { 1 }
+ Constant [ integer PREG_SET_ORDER ] { 2 }
+ Constant [ integer PREG_OFFSET_CAPTURE ] { 256 }
+ Constant [ integer PREG_SPLIT_NO_EMPTY ] { 1 }
+ Constant [ integer PREG_SPLIT_DELIM_CAPTURE ] { 2 }
+ Constant [ integer PREG_SPLIT_OFFSET_CAPTURE ] { 4 }
+ Constant [ integer PREG_GREP_INVERT ] { 1 }
+ Constant [ integer PREG_NO_ERROR ] { 0 }
+ Constant [ integer PREG_INTERNAL_ERROR ] { 1 }
+ Constant [ integer PREG_BACKTRACK_LIMIT_ERROR ] { 2 }
+ Constant [ integer PREG_RECURSION_LIMIT_ERROR ] { 3 }
+ Constant [ integer PREG_BAD_UTF8_ERROR ] { 4 }
+ Constant [ integer PREG_BAD_UTF8_OFFSET_ERROR ] { 5 }
+ Constant [ string PCRE_VERSION ] { %s }
+ }
+
+ - Functions {
+ Function [ <internal:pcre> function preg_match ] {
+
+ - Parameters [5] {
+ Parameter #0 [ <required> $pattern ]
+ Parameter #1 [ <required> $subject ]
+ Parameter #2 [ <optional> &$subpatterns ]
+ Parameter #3 [ <optional> $flags ]
+ Parameter #4 [ <optional> $offset ]
+ }
+ }
+ Function [ <internal:pcre> function preg_match_all ] {
+
+ - Parameters [5] {
+ Parameter #0 [ <required> $pattern ]
+ Parameter #1 [ <required> $subject ]
+ Parameter #2 [ <optional> &$subpatterns ]
+ Parameter #3 [ <optional> $flags ]
+ Parameter #4 [ <optional> $offset ]
+ }
+ }
+ Function [ <internal:pcre> function preg_replace ] {
+
+ - Parameters [5] {
+ Parameter #0 [ <required> $regex ]
+ Parameter #1 [ <required> $replace ]
+ Parameter #2 [ <required> $subject ]
+ Parameter #3 [ <optional> $limit ]
+ Parameter #4 [ <optional> &$count ]
+ }
+ }
+ Function [ <internal:pcre> function preg_replace_callback ] {
+
+ - Parameters [5] {
+ Parameter #0 [ <required> $regex ]
+ Parameter #1 [ <required> $callback ]
+ Parameter #2 [ <required> $subject ]
+ Parameter #3 [ <optional> $limit ]
+ Parameter #4 [ <optional> &$count ]
+ }
+ }
+ Function [ <internal:pcre> function preg_filter ] {
+
+ - Parameters [5] {
+ Parameter #0 [ <required> $regex ]
+ Parameter #1 [ <required> $replace ]
+ Parameter #2 [ <required> $subject ]
+ Parameter #3 [ <optional> $limit ]
+ Parameter #4 [ <optional> &$count ]
+ }
+ }
+ Function [ <internal:pcre> function preg_split ] {
+
+ - Parameters [4] {
+ Parameter #0 [ <required> $pattern ]
+ Parameter #1 [ <required> $subject ]
+ Parameter #2 [ <optional> $limit ]
+ Parameter #3 [ <optional> $flags ]
+ }
+ }
+ Function [ <internal:pcre> function preg_quote ] {
+
+ - Parameters [2] {
+ Parameter #0 [ <required> $str ]
+ Parameter #1 [ <optional> $delim_char ]
+ }
+ }
+ Function [ <internal:pcre> function preg_grep ] {
+
+ - Parameters [3] {
+ Parameter #0 [ <required> $regex ]
+ Parameter #1 [ <required> $input ]
+ Parameter #2 [ <optional> $flags ]
+ }
+ }
+ Function [ <internal:pcre> function preg_last_error ] {
+
+ - Parameters [0] {
+ }
+ }
+ }
+}
+
+"
+Done
diff --git a/sapi/cli/tests/007.phpt b/sapi/cli/tests/007.phpt
new file mode 100644
index 0000000..0bf4070
--- /dev/null
+++ b/sapi/cli/tests/007.phpt
@@ -0,0 +1,52 @@
+--TEST--
+strip comments and whitespace with -w
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+$filename = dirname(__FILE__).'/007.test.php';
+$code ='
+<?php
+/* some test script */
+
+class test { /* {{{ */
+ public $var = "test"; //test var
+#perl style comment
+ private $pri; /* private attr */
+
+ function foo(/* void */) {
+ }
+}
+/* }}} */
+
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`$php -n -w "$filename"`);
+var_dump(`$php -n -w "wrong"`);
+var_dump(`echo "<?php /* comment */ class test {\n // comment \n function foo() {} } ?>" | $php -n -w`);
+
+@unlink($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(81) "
+<?php
+ class test { public $var = "test"; private $pri; function foo() { } } ?>
+"
+string(33) "Could not open input file: wrong
+"
+string(43) "<?php class test { function foo() {} } ?>
+"
+Done
diff --git a/sapi/cli/tests/008.phpt b/sapi/cli/tests/008.phpt
new file mode 100644
index 0000000..e14338f
--- /dev/null
+++ b/sapi/cli/tests/008.phpt
@@ -0,0 +1,43 @@
+--TEST--
+execute a file with -f
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+$filename = dirname(__FILE__).'/008.test.php';
+$code ='
+<?php
+
+class test {
+ private $pri;
+}
+
+var_dump(test::$pri);
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`$php -n -f "$filename"`);
+var_dump(`$php -n -f "wrong"`);
+
+@unlink($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "
+
+Fatal error: Cannot access private property test::$pri in %s on line %d
+"
+string(33) "Could not open input file: wrong
+"
+Done
diff --git a/sapi/cli/tests/009.phpt b/sapi/cli/tests/009.phpt
new file mode 100644
index 0000000..33f859f
--- /dev/null
+++ b/sapi/cli/tests/009.phpt
@@ -0,0 +1,20 @@
+--TEST--
+using invalid combinations of cmdline options
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+var_dump(`$php -n -a -r "echo hello;"`);
+var_dump(`$php -n -r "echo hello;" -a`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(57) "Either execute direct code, process stdin or use a file.
+"
+string(57) "Either execute direct code, process stdin or use a file.
+"
+Done
diff --git a/sapi/cli/tests/010-2.phpt b/sapi/cli/tests/010-2.phpt
new file mode 100644
index 0000000..bd33d2c
--- /dev/null
+++ b/sapi/cli/tests/010-2.phpt
@@ -0,0 +1,35 @@
+--TEST--
+executing a code with -R
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+$filename_txt = dirname(__FILE__)."/010.test.txt";
+
+$txt = '
+test
+hello
+';
+
+file_put_contents($filename_txt, $txt);
+
+var_dump(`cat "$filename_txt" | "$php" -n -R "var_dump(1);"`);
+
+@unlink($filename_txt);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(21) "int(1)
+int(1)
+int(1)
+"
+Done
diff --git a/sapi/cli/tests/010.phpt b/sapi/cli/tests/010.phpt
new file mode 100644
index 0000000..77c76c1
--- /dev/null
+++ b/sapi/cli/tests/010.phpt
@@ -0,0 +1,46 @@
+--TEST--
+executing a file with -F
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+$filename = __DIR__."/010.test.php";
+$filename_txt = __DIR__."/010.test.txt";
+
+$code = '
+<?php
+var_dump(fread(STDIN, 10));
+?>
+';
+
+file_put_contents($filename, $code);
+
+$txt = '
+test
+hello';
+
+file_put_contents($filename_txt, $txt);
+
+var_dump(`cat "$filename_txt" | "$php" -n -F "$filename"`);
+
+?>
+===DONE===
+--CLEAN--
+<?php
+@unlink(__DIR__."/010.test.php");
+@unlink(__DIR__."/010.test.txt");
+?>
+--EXPECTF--
+string(25) "
+string(10) "test
+hello"
+"
+===DONE===
diff --git a/sapi/cli/tests/011.phpt b/sapi/cli/tests/011.phpt
new file mode 100644
index 0000000..6154693
--- /dev/null
+++ b/sapi/cli/tests/011.phpt
@@ -0,0 +1,58 @@
+--TEST--
+syntax check
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+$filename = dirname(__FILE__)."/011.test.php";
+
+$code = '
+<?php
+
+$test = "var";
+
+class test {
+ private $var;
+}
+
+echo test::$var;
+
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`"$php" -n -l $filename`);
+var_dump(`"$php" -n -l some.unknown`);
+
+$code = '
+<?php
+
+class test
+ private $var;
+}
+
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`"$php" -n -l $filename`);
+
+@unlink($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(%d) "No syntax errors detected in %s011.test.php
+"
+string(40) "Could not open input file: some.unknown
+"
+string(%d) "
+Parse error: %s expecting %s{%s in %s on line %d
+Errors parsing %s011.test.php
+"
+Done
diff --git a/sapi/cli/tests/012.phpt b/sapi/cli/tests/012.phpt
new file mode 100644
index 0000000..c1e4f6a
--- /dev/null
+++ b/sapi/cli/tests/012.phpt
@@ -0,0 +1,38 @@
+--TEST--
+invalid arguments and error messages
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+var_dump(`"$php" -n -F some.php -F some.php`);
+var_dump(`"$php" -n -F some.php -R some.php`);
+var_dump(`"$php" -n -R some.php -F some.php`);
+var_dump(`"$php" -n -R some.php -R some.php`);
+var_dump(`"$php" -n -f some.php -f some.php`);
+var_dump(`"$php" -n -B '' -B ''`);
+var_dump(`"$php" -n -E '' -E ''`);
+var_dump(`"$php" -n -r '' -r ''`);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(32) "You can use -R or -F only once.
+"
+string(32) "You can use -R or -F only once.
+"
+string(32) "You can use -R or -F only once.
+"
+string(32) "You can use -R or -F only once.
+"
+string(26) "You can use -f only once.
+"
+string(26) "You can use -B only once.
+"
+string(26) "You can use -E only once.
+"
+string(26) "You can use -r only once.
+"
+Done
diff --git a/sapi/cli/tests/013.phpt b/sapi/cli/tests/013.phpt
new file mode 100644
index 0000000..99bfe5e
--- /dev/null
+++ b/sapi/cli/tests/013.phpt
@@ -0,0 +1,34 @@
+--TEST--
+running PHP code before and after processing input lines with -B and -E
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+$filename_txt = dirname(__FILE__)."/013.test.txt";
+file_put_contents($filename_txt, "test\nfile\ncontents\n");
+
+var_dump(`cat "$filename_txt" | "$php" -n -B 'var_dump("start");'`);
+var_dump(`cat "$filename_txt" | "$php" -n -E 'var_dump("end");'`);
+var_dump(`cat "$filename_txt" | "$php" -n -B 'var_dump("start");' -E 'var_dump("end");'`);
+
+@unlink($filename_txt);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(18) "string(5) "start"
+"
+string(16) "string(3) "end"
+"
+string(34) "string(5) "start"
+string(3) "end"
+"
+Done
diff --git a/sapi/cli/tests/014.phpt b/sapi/cli/tests/014.phpt
new file mode 100644
index 0000000..e8c5203
--- /dev/null
+++ b/sapi/cli/tests/014.phpt
@@ -0,0 +1,44 @@
+--TEST--
+syntax highlighting
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+$filename = dirname(__FILE__)."/014.test.php";
+$code = '
+<?php
+$test = "var"; //var
+/* test class */
+class test {
+ private $var = array();
+
+ public static function foo(Test $arg) {
+ echo "hello";
+ var_dump($this);
+ }
+}
+
+$o = new test;
+?>
+';
+
+file_put_contents($filename, $code);
+
+var_dump(`"$php" -n -s $filename`);
+var_dump(`"$php" -n -s unknown`);
+
+@unlink($filename);
+
+echo "Done\n";
+?>
+--EXPECTF--
+string(1478) "<code><span style="color: #000000">
+<br /><span style="color: #0000BB">&lt;?php<br />$test&nbsp;</span><span style="color: #007700">=&nbsp;</span><span style="color: #DD0000">"var"</span><span style="color: #007700">;&nbsp;</span><span style="color: #FF8000">//var<br />/*&nbsp;test&nbsp;class&nbsp;*/<br /></span><span style="color: #007700">class&nbsp;</span><span style="color: #0000BB">test&nbsp;</span><span style="color: #007700">{<br />&nbsp;&nbsp;&nbsp;&nbsp;private&nbsp;</span><span style="color: #0000BB">$var&nbsp;</span><span style="color: #007700">=&nbsp;array();<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;function&nbsp;</span><span style="color: #0000BB">foo</span><span style="color: #007700">(</span><span style="color: #0000BB">Test&nbsp;$arg</span><span style="color: #007700">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;echo&nbsp;</span><span style="color: #DD0000">"hello"</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000BB">var_dump</span><span style="color: #007700">(</span><span style="color: #0000BB">$this</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />}<br /><br /></span><span style="color: #0000BB">$o&nbsp;</span><span style="color: #007700">=&nbsp;new&nbsp;</span><span style="color: #0000BB">test</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">?&gt;<br /></span>
+</span>
+</code>"
+string(35) "Could not open input file: unknown
+"
+Done
diff --git a/sapi/cli/tests/015.phpt b/sapi/cli/tests/015.phpt
new file mode 100644
index 0000000..ab5918b
--- /dev/null
+++ b/sapi/cli/tests/015.phpt
@@ -0,0 +1,35 @@
+--TEST--
+CLI long options
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+
+echo `"$php" -n --version | grep built:`;
+echo `echo "<?php print_r(\\\$argv);" | "$php" -n -- foo bar baz`, "\n";
+echo `"$php" -n --version foo bar baz | grep built:`;
+echo `"$php" -n --notexisting foo bar baz | grep Usage:`;
+
+echo "Done\n";
+?>
+--EXPECTF--
+PHP %d.%d.%d%s(cli) (built: %s)%s
+Array
+(
+ [0] => -
+ [1] => foo
+ [2] => bar
+ [3] => baz
+)
+
+PHP %d.%d.%d%s(cli) (built: %s)%s
+Usage: %s [options] [-f] <file> [--] [args...]
+Done
diff --git a/sapi/cli/tests/016.phpt b/sapi/cli/tests/016.phpt
new file mode 100644
index 0000000..31c1a40
--- /dev/null
+++ b/sapi/cli/tests/016.phpt
@@ -0,0 +1,130 @@
+--TEST--
+CLI -a and readline
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (!extension_loaded('readline') || readline_info('done') === NULL) {
+ die ("skip need readline support");
+}
+?>
+--FILE--
+<?php
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+// disallow console escape sequences that may break the output
+putenv('TERM=VT100');
+
+$codes = array();
+
+$codes[1] = <<<EOT
+echo 'Hello world';
+exit
+EOT;
+
+$codes[] = <<<EOT
+echo 'multine
+single
+quote';
+exit
+EOT;
+
+$codes[] = <<<EOT
+echo <<<HEREDOC
+Here
+comes
+the
+doc
+HEREDOC;
+EOT;
+
+$codes[] = <<<EOT
+if (0) {
+ echo "I'm not there";
+}
+echo "Done";
+EOT;
+
+$codes[] = <<<EOT
+function a_function_with_some_name() {
+ echo "I was called!";
+}
+a_function_w );
+EOT;
+
+foreach ($codes as $key => $code) {
+ echo "\n--------------\nSnippet no. $key:\n--------------\n";
+ $code = escapeshellarg($code);
+ echo `echo $code | "$php" -a`, "\n";
+}
+
+echo "\nDone\n";
+?>
+--XFAIL--
+https://bugs.php.net/bug.php?id=55496
+--EXPECTF--
+--------------
+Snippet no. 1:
+--------------
+Interactive shell
+
+php > echo 'Hello world';
+Hello world
+php > exit
+
+
+--------------
+Snippet no. 2:
+--------------
+Interactive shell
+
+php > echo 'multine
+php ' single
+php ' quote';
+multine
+single
+quote
+php > exit
+
+
+--------------
+Snippet no. 3:
+--------------
+Interactive shell
+
+php > echo <<<HEREDOC
+<<< > Here
+<<< > comes
+<<< > the
+<<< > doc
+<<< > HEREDOC;
+Here
+comes
+the
+doc
+php >
+
+--------------
+Snippet no. 4:
+--------------
+Interactive shell
+
+php > if (0) {
+php { echo "I'm not there";
+php { }
+php > echo "Done";
+Done
+php >
+
+--------------
+Snippet no. 5:
+--------------
+Interactive shell
+
+php > function a_function_with_some_name() {
+php { echo "I was called!";
+php { }
+php > a_function_with_some_name();
+I was called!
+php >
+
+Done
diff --git a/sapi/cli/tests/017.phpt b/sapi/cli/tests/017.phpt
new file mode 100644
index 0000000..efaf977
--- /dev/null
+++ b/sapi/cli/tests/017.phpt
@@ -0,0 +1,106 @@
+--TEST--
+CLI -a and libedit
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (!extension_loaded('readline') || readline_info('done') !== NULL) {
+ die ("skip need readline support using libedit");
+}
+?>
+--FILE--
+<?php
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+$codes = array();
+
+$codes[1] = <<<EOT
+echo 'Hello world';
+exit
+EOT;
+
+$codes[] = <<<EOT
+echo 'multine
+single
+quote';
+exit
+EOT;
+
+$codes[] = <<<EOT
+echo <<<HEREDOC
+Here
+comes
+the
+doc
+HEREDOC;
+EOT;
+
+$codes[] = <<<EOT
+if (0) {
+ echo "I'm not there";
+}
+echo "Done";
+EOT;
+
+$codes[] = <<<EOT
+function a_function_with_some_name() {
+ echo "I was called!";
+}
+a_function_w );
+EOT;
+
+foreach ($codes as $key => $code) {
+ echo "\n--------------\nSnippet no. $key:\n--------------\n";
+ $code = escapeshellarg($code);
+ echo `echo $code | "$php" -a`, "\n";
+}
+
+echo "\nDone\n";
+?>
+--EXPECTF--
+--------------
+Snippet no. 1:
+--------------
+Interactive shell
+
+Hello world
+
+
+--------------
+Snippet no. 2:
+--------------
+Interactive shell
+
+multine
+single
+quote
+
+
+--------------
+Snippet no. 3:
+--------------
+Interactive shell
+
+Here
+comes
+the
+doc
+
+
+--------------
+Snippet no. 4:
+--------------
+Interactive shell
+
+Done
+
+
+--------------
+Snippet no. 5:
+--------------
+Interactive shell
+
+
+Parse error: syntax error, unexpected ')' in php shell code on line 1
+
+
+Done
diff --git a/sapi/cli/tests/018.phpt b/sapi/cli/tests/018.phpt
new file mode 100644
index 0000000..56921bd
--- /dev/null
+++ b/sapi/cli/tests/018.phpt
@@ -0,0 +1,27 @@
+--TEST--
+CLI php -m
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+
+echo `"$php" -n -m`;
+
+echo "Done\n";
+?>
+--EXPECTF--
+[PHP Modules]
+%a
+pcre
+%a
+
+[Zend Modules]
+%aDone
diff --git a/sapi/cli/tests/019.phpt b/sapi/cli/tests/019.phpt
new file mode 100644
index 0000000..c98155d
--- /dev/null
+++ b/sapi/cli/tests/019.phpt
@@ -0,0 +1,36 @@
+--TEST--
+CLI php -i
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+
+echo `"$php" -n -i`;
+
+echo "\nDone\n";
+?>
+--EXPECTF--
+phpinfo()
+PHP Version => %s
+%a
+PHP License
+This program is free software; you can redistribute it and/or modify
+it under the terms of the PHP License as published by the PHP Group
+and included in the distribution in the file: LICENSE
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+If you did not receive a copy of the PHP license, or have any
+questions about PHP licensing, please contact license@php.net.
+
+Done
diff --git a/sapi/cli/tests/020.phpt b/sapi/cli/tests/020.phpt
new file mode 100644
index 0000000..62be4ba
--- /dev/null
+++ b/sapi/cli/tests/020.phpt
@@ -0,0 +1,32 @@
+--TEST--
+CLI php --ri
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+
+echo `"$php" -n --ri this_extension_does_not_exist_568537753423`;
+echo `"$php" -n --ri standard`;
+
+echo "\nDone\n";
+?>
+--EXPECTF--
+Extension 'this_extension_does_not_exist_568537753423' not present.
+
+standard
+
+%a
+
+Directive => Local Value => Master Value
+%a
+
+Done
+
diff --git a/sapi/cli/tests/021.phpt b/sapi/cli/tests/021.phpt
new file mode 100644
index 0000000..2ddd688
--- /dev/null
+++ b/sapi/cli/tests/021.phpt
@@ -0,0 +1,43 @@
+--TEST--
+CLI shell shebang
+--SKIPIF--
+<?php
+include 'skipif.inc';
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ die ("skip not for Windows");
+}
+
+if (strlen("#!".getenv('TEST_PHP_EXECUTABLE')) > 127) {
+ die ("skip shebang is too long, see http://www.in-ulm.de/~mascheck/various/shebang/#results");
+}
+?>
+--FILE--
+<?php
+
+$php = getenv('TEST_PHP_EXECUTABLE');
+
+$filename = __DIR__.'/021.tmp.php';
+
+$script = "#!$php -n\n".
+ "ola\n".
+ "<?php echo 1+1,'\n';\n".
+ "?>\n".
+ "adeus\n";
+
+file_put_contents($filename, $script);
+chmod($filename, 0777);
+
+echo `$filename`;
+
+echo "\nDone\n";
+?>
+--CLEAN--
+<?php
+unlink(__DIR__.'/021.tmp.php');
+?>
+--EXPECTF--
+ola
+2
+adeus
+
+Done
diff --git a/sapi/cli/tests/022.inc b/sapi/cli/tests/022.inc
new file mode 100644
index 0000000..b77512f
--- /dev/null
+++ b/sapi/cli/tests/022.inc
@@ -0,0 +1,14 @@
+<?php
+
+ob_start();
+var_dump(STDIN);
+
+$fd = fopen("php://stdin","r");
+var_dump($fd);
+
+$client_socket = stream_socket_accept($fd);
+
+$data = ob_get_clean();
+fwrite($client_socket, $data);
+
+?>
diff --git a/sapi/cli/tests/022.phpt b/sapi/cli/tests/022.phpt
new file mode 100644
index 0000000..0110220
--- /dev/null
+++ b/sapi/cli/tests/022.phpt
@@ -0,0 +1,47 @@
+--TEST--
+STDIN/OUT/ERR stream type
+--SKIPIF--
+<?php
+if (!getenv("TEST_PHP_EXECUTABLE")) die("skip TEST_PHP_EXECUTABLE not set");
+if (substr(PHP_OS, 0, 3) == "WIN") die("skip non windows test");
+?>
+--FILE--
+<?php
+$php = getenv("TEST_PHP_EXECUTABLE");
+$socket_file = tempnam(sys_get_temp_dir(), pathinfo(__FILE__, PATHINFO_FILENAME) . '.sock');
+$test_file = dirname(__FILE__) . '/' . pathinfo(__FILE__, PATHINFO_FILENAME) . '.inc';
+if (file_exists($socket_file)) {
+ unlink($socket_file);
+}
+$socket = stream_socket_server('unix://' . $socket_file);
+var_dump($socket);
+if (!$socket) {
+ exit(1);
+}
+$desc = array(
+ 0 => $socket,
+ 1 => STDOUT,
+ 2 => STDERR,
+);
+$pipes = array();
+$proc = proc_open("$php -n " . escapeshellarg($test_file), $desc, $pipes);
+var_dump($proc);
+if (!$proc) {
+ exit(1);
+}
+
+$client_socket = stream_socket_client('unix://' . $socket_file);
+var_dump($client_socket);
+echo stream_get_contents($client_socket);
+fclose($client_socket);
+
+proc_terminate($proc);
+proc_close($proc);
+unlink($socket_file);
+?>
+--EXPECTF--
+resource(%d) of type (stream)
+resource(%d) of type (process)
+resource(%d) of type (stream)
+resource(%d) of type (stream)
+resource(%d) of type (stream)
diff --git a/sapi/cli/tests/bug43177.phpt b/sapi/cli/tests/bug43177.phpt
new file mode 100644
index 0000000..36b5504
--- /dev/null
+++ b/sapi/cli/tests/bug43177.phpt
@@ -0,0 +1,82 @@
+--TEST--
+Bug #61977 Test exit code for various errors
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start(<<<'SCRIPT'
+ ini_set('display_errors', 0);
+ switch($_SERVER["REQUEST_URI"]) {
+ case "/parse":
+ eval("this is a parse error");
+ echo "OK\n";
+ break;
+ case "/fatal":
+ eval("foo();");
+ echo "OK\n";
+ break;
+ case "/compile":
+ eval("class foo { final private final function bar() {} }");
+ echo "OK\n";
+ break;
+ case "/fatal2":
+ foo();
+ echo "OK\n";
+ break;
+ default:
+ return false;
+ }
+SCRIPT
+);
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+foreach(array("parse", "fatal", "fatal2", "compile") as $url) {
+ $fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+ if (!$fp) {
+ die("connect failed");
+ }
+
+ if(fwrite($fp, <<<HEADER
+GET /$url HTTP/1.1
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+ }
+}
+
+?>
+--EXPECTF--
+HTTP/1.1 200 OK
+Host: localhost
+Connection: close
+X-Powered-By: %s
+Content-type: text/html
+
+OK
+HTTP/1.0 500 Internal Server Error
+Host: localhost
+Connection: close
+X-Powered-By: %s
+Content-type: text/html
+
+HTTP/1.0 500 Internal Server Error
+Host: localhost
+Connection: close
+X-Powered-By: %s
+Content-type: text/html
+
+HTTP/1.0 500 Internal Server Error
+Host: localhost
+Connection: close
+X-Powered-By: %s
+Content-type: text/html
diff --git a/sapi/cli/tests/bug44564.phpt b/sapi/cli/tests/bug44564.phpt
new file mode 100644
index 0000000..7dca62a
--- /dev/null
+++ b/sapi/cli/tests/bug44564.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Bug #44564 (escapeshellarg removes UTF-8 multi-byte characters)
+--SKIPIF--
+<?php
+if (false == setlocale(LC_CTYPE, "UTF8", "en_US.UTF-8")) {
+ die("skip setlocale() failed\n");
+}
+?>
+--FILE--
+<?php
+setlocale(LC_CTYPE, "UTF8", "en_US.UTF-8");
+var_dump(escapeshellcmd('f{o}<€>'));
+var_dump(escapeshellarg('f~|;*Þ?'));
+var_dump(escapeshellcmd('?€®đæ?'));
+var_dump(escapeshellarg('aŊł€'));
+
+?>
+--EXPECT--
+string(13) "f\{o\}\<€\>"
+string(10) "'f~|;*Þ?'"
+string(13) "\?€®đæ\?"
+string(10) "'aŊł€'"
diff --git a/sapi/cli/tests/bug61546.phpt b/sapi/cli/tests/bug61546.phpt
new file mode 100644
index 0000000..071edb7
--- /dev/null
+++ b/sapi/cli/tests/bug61546.phpt
@@ -0,0 +1,31 @@
+--TEST--
+Bug #61546 (functions related to current script failed when chdir() in cli sapi)
+--FILE--
+<?php
+// reference doc for getmyinode() on php.net states that it returns an integer or FALSE on error
+// on Windows, getmyinode() returns 0 which normally casts to FALSE
+// however, the implementation of getmyinode() (in pageinfo.c) returns an explicit FALSE in the
+// event that the internal page_inode structure is less than 0, otherwise it returns the long value
+// of page_inode. therefore, an explicit 0 should be a passing value for this test.
+//
+// the ext/standard/tests/file/statpage.phpt test also tests getmyinode() returns an integer and will
+// pass even if that integer is 0. on Windows, the getmyinode() call in statpage.phpt returns 0 and
+// passes on Windows.
+$php = getenv("TEST_PHP_EXECUTABLE");
+$test_code = <<<PHP
+<?php
+chdir('..');
+var_dump(get_current_user() != "");
+chdir('..');
+var_dump(getmyinode() !== false);
+var_dump(getlastmod() != false);
+PHP;
+
+file_put_contents("bug61546_sub.php", $test_code);
+system($php . ' -n bug61546_sub.php');
+unlink("bug61546_sub.php");
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
diff --git a/sapi/cli/tests/bug61679.phpt b/sapi/cli/tests/bug61679.phpt
new file mode 100644
index 0000000..819ce2f
--- /dev/null
+++ b/sapi/cli/tests/bug61679.phpt
@@ -0,0 +1,43 @@
+--TEST--
+Bug #61679 (Error on non-standard HTTP methods)
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start(<<<'PHP'
+echo "This should never echo";
+PHP
+);
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+// Send a request with a fictitious request method,
+// I like smurfs, the smurf everything.
+if(fwrite($fp, <<<HEADER
+SMURF / HTTP/1.1
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ // Only echo the first line from the response,
+ // the rest is not interesting
+ break;
+ }
+}
+
+fclose($fp);
+?>
+--EXPECTF--
+HTTP/1.1 501 Not Implemented
diff --git a/sapi/cli/tests/bug61977.phpt b/sapi/cli/tests/bug61977.phpt
new file mode 100644
index 0000000..09a6ba6
--- /dev/null
+++ b/sapi/cli/tests/bug61977.phpt
@@ -0,0 +1,55 @@
+--TEST--
+Bug #61977 test CLI web-server support for Mime Type File extensions mapping
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('<?php ?>', true);
+
+/*
+ * If a Mime Type is added in php_cli_server.c, add it to this array and update
+ * the EXPECTF section accordingly
+ */
+$mimetypes = ['html', 'htm', 'svg', 'css', 'js', 'png', 'webm', 'ogv', 'ogg'];
+
+function test_mimetypes($mimetypes) {
+ foreach ($mimetypes as $mimetype) {
+ list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+ $port = intval($port) ? : 80;
+ $fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+ if (!$fp) die('Connect failed');
+ file_put_contents(__DIR__ . "/foo.{$mimetype}", '');
+ $header = <<<HEADER
+GET /foo.{$mimetype} HTTP/1.1
+Host: {$host}
+
+
+HEADER;
+ if (fwrite($fp, $header)) {
+ while (!feof($fp)) {
+ $text = fgets($fp);
+ if (strncasecmp("Content-type:", $text, 13) == 0) {
+ echo "foo.{$mimetype} => ", $text;
+ }
+ }
+ @unlink(__DIR__ . "/foo.{$mimetype}");
+ fclose($fp);
+ }
+ }
+}
+
+test_mimetypes($mimetypes);
+?>
+--EXPECTF--
+foo.html => Content-Type: text/html; charset=UTF-8
+foo.htm => Content-Type: text/html; charset=UTF-8
+foo.svg => Content-Type: image/svg+xml
+foo.css => Content-Type: text/css; charset=UTF-8
+foo.js => Content-Type: text/javascript; charset=UTF-8
+foo.png => Content-Type: image/png
+foo.webm => Content-Type: video/webm
+foo.ogv => Content-Type: video/ogg
+foo.ogg => Content-Type: audio/ogg
diff --git a/sapi/cli/tests/php_cli_server.inc b/sapi/cli/tests/php_cli_server.inc
new file mode 100644
index 0000000..40c5361
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server.inc
@@ -0,0 +1,62 @@
+<?php
+define ("PHP_CLI_SERVER_HOSTNAME", "localhost");
+define ("PHP_CLI_SERVER_PORT", 8964);
+define ("PHP_CLI_SERVER_ADDRESS", PHP_CLI_SERVER_HOSTNAME.":".PHP_CLI_SERVER_PORT);
+
+function php_cli_server_start($code = 'echo "Hello world";', $no_router = FALSE) {
+ $php_executable = getenv('TEST_PHP_EXECUTABLE');
+ $doc_root = __DIR__;
+ $router = "index.php";
+
+ if ($code) {
+ file_put_contents($doc_root . '/' . $router, '<?php ' . $code . ' ?>');
+ }
+
+ $descriptorspec = array(
+ 0 => STDIN,
+ 1 => STDOUT,
+ 2 => STDERR,
+ );
+
+ if (substr(PHP_OS, 0, 3) == 'WIN') {
+ $cmd = "{$php_executable} -t {$doc_root} -n -S " . PHP_CLI_SERVER_ADDRESS;
+ if (!$no_router) {
+ $cmd .= " {$router}";
+ }
+
+ $handle = proc_open(addslashes($cmd), $descriptorspec, $pipes, $doc_root, NULL, array("bypass_shell" => true, "suppress_errors" => true));
+ } else {
+ $cmd = "exec {$php_executable} -t {$doc_root} -n -S " . PHP_CLI_SERVER_ADDRESS;
+ if (!$no_router) {
+ $cmd .= " {$router}";
+ }
+ $cmd .= " 2>/dev/null";
+
+ $handle = proc_open($cmd, $descriptorspec, $pipes, $doc_root);
+ }
+
+ // note: even when server prints 'Listening on localhost:8964...Press Ctrl-C to quit.'
+ // it might not be listening yet...need to wait until fsockopen() call returns
+ $i = 0;
+ while (($i++ < 30) && !($fp = @fsockopen(PHP_CLI_SERVER_HOSTNAME, PHP_CLI_SERVER_PORT))) {
+ usleep(10000);
+ }
+
+ if ($fp) {
+ fclose($fp);
+ }
+
+ register_shutdown_function(
+ function($handle) use($router) {
+ proc_terminate($handle);
+ @unlink(__DIR__ . "/{$router}");
+ },
+ $handle
+ );
+ // don't bother sleeping, server is already up
+ // server can take a variable amount of time to be up, so just sleeping a guessed amount of time
+ // does not work. this is why tests sometimes pass and sometimes fail. to get a reliable pass
+ // sleeping doesn't work.
+}
+?>
+
diff --git a/sapi/cli/tests/php_cli_server_001.phpt b/sapi/cli/tests/php_cli_server_001.phpt
new file mode 100644
index 0000000..3f1083e
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_001.phpt
@@ -0,0 +1,16 @@
+--TEST--
+basic function
+--INI--
+allow_url_fopen=1
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start();
+var_dump(file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS));
+?>
+--EXPECT--
+string(11) "Hello world"
diff --git a/sapi/cli/tests/php_cli_server_002.phpt b/sapi/cli/tests/php_cli_server_002.phpt
new file mode 100644
index 0000000..93151c1
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_002.phpt
@@ -0,0 +1,20 @@
+--TEST--
+$_SERVER variable
+--INI--
+allow_url_fopen=1
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('var_dump($_SERVER["DOCUMENT_ROOT"], $_SERVER["SERVER_SOFTWARE"], $_SERVER["SERVER_NAME"], $_SERVER["SERVER_PORT"]);');
+var_dump(file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS));
+?>
+--EXPECTF--
+string(%d) "string(%d) "%stests"
+string(%d) "PHP %s Development Server"
+string(%d) "localhost"
+string(%d) "8964"
+"
diff --git a/sapi/cli/tests/php_cli_server_003.phpt b/sapi/cli/tests/php_cli_server_003.phpt
new file mode 100644
index 0000000..d1e95fe
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_003.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Bug #55726 (Changing the working directory makes router script inaccessible)
+--INI--
+allow_url_fopen=1
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('chdir(__DIR__); echo "okey";');
+var_dump(file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS));
+var_dump(file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS));
+?>
+--EXPECTF--
+string(4) "okey"
+string(4) "okey"
diff --git a/sapi/cli/tests/php_cli_server_004.phpt b/sapi/cli/tests/php_cli_server_004.phpt
new file mode 100644
index 0000000..b61f886
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_004.phpt
@@ -0,0 +1,48 @@
+--TEST--
+Bug #55747 (request headers missed in $_SERVER)
+--INI--
+allow_url_fopen=1
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('foreach($_SERVER as $k=>$v) { if (!strncmp($k, "HTTP", 4)) var_dump( $k . ":" . $v); }');
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+GET / HTTP/1.1
+Host:{$host}
+User-Agent:dummy
+Custom:foo
+Referer:http://www.php.net/
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+?>
+--EXPECTF--
+HTTP/1.1 200 OK
+Host: %s
+Connection: close
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+string(19) "HTTP_HOST:localhost"
+string(21) "HTTP_USER_AGENT:dummy"
+string(15) "HTTP_CUSTOM:foo"
+string(32) "HTTP_REFERER:http://www.php.net/"
diff --git a/sapi/cli/tests/php_cli_server_005.phpt b/sapi/cli/tests/php_cli_server_005.phpt
new file mode 100644
index 0000000..ccc0f8f
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_005.phpt
@@ -0,0 +1,71 @@
+--TEST--
+Post a file
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('var_dump($_FILES);');
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+$post_data = <<<POST
+-----------------------------114782935826962
+Content-Disposition: form-data; name="userfile"; filename="laruence.txt"
+Content-Type: text/plain
+
+I am not sure about this.
+
+-----------------------------114782935826962--
+
+
+POST;
+
+$post_len = strlen($post_data);
+
+if(fwrite($fp, <<<HEADER
+POST / HTTP/1.1
+Host: {$host}
+Content-Type: multipart/form-data; boundary=---------------------------114782935826962
+Content-Length: {$post_len}
+
+
+{$post_data}
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+?>
+--EXPECTF--
+HTTP/1.1 200 OK
+Host: %s
+Connection: close
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+array(1) {
+ ["userfile"]=>
+ array(5) {
+ ["name"]=>
+ string(12) "laruence.txt"
+ ["type"]=>
+ string(10) "text/plain"
+ ["tmp_name"]=>
+ string(%d) "%s"
+ ["error"]=>
+ int(0)
+ ["size"]=>
+ int(26)
+ }
+}
diff --git a/sapi/cli/tests/php_cli_server_006.phpt b/sapi/cli/tests/php_cli_server_006.phpt
new file mode 100644
index 0000000..09e7ab0
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_006.phpt
@@ -0,0 +1,42 @@
+--TEST--
+Bug #55755 (SegFault when outputting header WWW-Authenticate)
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('var_dump($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"]);');
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+GET / HTTP/1.1
+Host: {$host}
+Authorization: Basic Zm9vOmJhcg==
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+?>
+--EXPECTF--
+HTTP/1.1 200 OK
+Host: %s
+Connection: close
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+string(3) "foo"
+string(3) "bar"
diff --git a/sapi/cli/tests/php_cli_server_007.phpt b/sapi/cli/tests/php_cli_server_007.phpt
new file mode 100644
index 0000000..64d4df0
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_007.phpt
@@ -0,0 +1,40 @@
+--TEST--
+Bug #55758 (Digest Authenticate missed in 5.4)
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('header(\'WWW-Authenticate: Digest realm="foo",qop="auth",nonce="XXXXX",opaque="'.md5("foo").'"\');');
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+GET / HTTP/1.1
+Host: {$host}
+Authorization: Basic Zm9vOmJhcg==
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+?>
+--EXPECTF--
+HTTP/1.1 401 Unauthorized
+Host: %s
+Connection: close
+X-Powered-By: PHP/%s
+WWW-Authenticate: Digest realm="foo",qop="auth",nonce="XXXXX",opaque="acbd18db4cc2f85cedef654fccc4a4d8"
+Content-type: text/html
diff --git a/sapi/cli/tests/php_cli_server_008.phpt b/sapi/cli/tests/php_cli_server_008.phpt
new file mode 100644
index 0000000..2e68e24
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_008.phpt
@@ -0,0 +1,68 @@
+--TEST--
+SERVER_PROTOCOL header availability
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('var_dump($_SERVER["SERVER_PROTOCOL"]);');
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+GET / HTTP/1.1
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+fclose($fp);
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+
+if(fwrite($fp, <<<HEADER
+GET / HTTP/1.0
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+fclose($fp);
+?>
+--EXPECTF--
+HTTP/1.1 200 OK
+Host: %s
+Connection: close
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+string(8) "HTTP/1.1"
+HTTP/1.0 200 OK
+Host: %s
+Connection: close
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+string(8) "HTTP/1.0"
diff --git a/sapi/cli/tests/php_cli_server_009.phpt b/sapi/cli/tests/php_cli_server_009.phpt
new file mode 100644
index 0000000..2beaeed
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_009.phpt
@@ -0,0 +1,93 @@
+--TEST--
+PATH_INFO (relevant to #60112)
+--DESCRIPTION--
+After this fix(#60112), previously 404 request like "localhost/foo/bar"
+now could serve correctly with request_uri "index.php" and PATH_INFO "/foo/bar/"
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('var_dump($_SERVER["PATH_INFO"]);', TRUE);
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+GET /foo/bar HTTP/1.1
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+fclose($fp);
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+
+if(fwrite($fp, <<<HEADER
+GET /foo/bar/ HTTP/1.0
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+fclose($fp);
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+
+if(fwrite($fp, <<<HEADER
+GET /foo/bar.js HTTP/1.0
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ break;
+ }
+}
+
+fclose($fp);
+?>
+--EXPECTF--
+HTTP/1.1 200 OK
+Host: %s
+Connection: close
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+string(8) "/foo/bar"
+HTTP/1.0 200 OK
+Host: %s
+Connection: close
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+string(9) "/foo/bar/"
+HTTP/1.0 404 Not Found
diff --git a/sapi/cli/tests/php_cli_server_010.phpt b/sapi/cli/tests/php_cli_server_010.phpt
new file mode 100644
index 0000000..2ef018b
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_010.phpt
@@ -0,0 +1,75 @@
+--TEST--
+Bug #60180 ($_SERVER["PHP_SELF"] incorrect)
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('var_dump($_SERVER["PHP_SELF"], $_SERVER["SCRIPT_NAME"], $_SERVER["PATH_INFO"], $_SERVER["QUERY_STRING"]);', TRUE);
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+GET /foo/bar?foo=bar HTTP/1.1
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+fclose($fp);
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+
+if(fwrite($fp, <<<HEADER
+GET /index.php/foo/bar/?foo=bar HTTP/1.0
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+fclose($fp);
+
+?>
+--EXPECTF--
+HTTP/1.1 200 OK
+Host: %s
+Connection: close
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+string(18) "/index.php/foo/bar"
+string(10) "/index.php"
+string(8) "/foo/bar"
+string(7) "foo=bar"
+HTTP/1.0 200 OK
+Host: %s
+Connection: close
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+string(19) "/index.php/foo/bar/"
+string(10) "/index.php"
+string(9) "/foo/bar/"
+string(7) "foo=bar"
diff --git a/sapi/cli/tests/php_cli_server_011.phpt b/sapi/cli/tests/php_cli_server_011.phpt
new file mode 100644
index 0000000..a957a8e
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_011.phpt
@@ -0,0 +1,41 @@
+--TEST--
+Bug #60180 ($_SERVER["PHP_SELF"] incorrect)
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('sytanx error;', TRUE);
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+$logo_id = php_logo_guid();
+
+if(fwrite($fp, <<<HEADER
+GET /?={$logo_id} HTTP/1.1
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ if (("Content-Type: image/gif") == trim(fgets($fp))) {
+ echo "okey";
+ break;
+ }
+ }
+}
+
+fclose($fp);
+
+?>
+--EXPECTF--
+okey
diff --git a/sapi/cli/tests/php_cli_server_012.phpt b/sapi/cli/tests/php_cli_server_012.phpt
new file mode 100644
index 0000000..9a1e60c
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_012.phpt
@@ -0,0 +1,55 @@
+--TEST--
+Bug #60159 (Router returns false, but POST is not passed to requested resource)
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('print_r($_REQUEST); $_REQUEST["foo"] = "bar"; return FALSE;');
+$doc_root = __DIR__;
+file_put_contents($doc_root . '/request.php', '<?php print_r($_REQUEST); ?>');
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+POST /request.php HTTP/1.1
+Host: {$host}
+Content-Type: application/x-www-form-urlencoded
+Content-Length: 3
+
+a=b
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+fclose($fp);
+@unlink($doc_root . '/request.php');
+
+?>
+--EXPECTF--
+HTTP/1.1 200 OK
+Host: %s
+Connection: close
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+Array
+(
+ [a] => b
+)
+Array
+(
+ [a] => b
+ [foo] => bar
+)
diff --git a/sapi/cli/tests/php_cli_server_013.phpt b/sapi/cli/tests/php_cli_server_013.phpt
new file mode 100644
index 0000000..570798a
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_013.phpt
@@ -0,0 +1,108 @@
+--TEST--
+No router, no script
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start(NULL, TRUE);
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+$output = '';
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+
+if(fwrite($fp, <<<HEADER
+POST / HTTP/1.1
+Host: {$host}
+Content-Type: application/x-www-form-urlencoded
+Content-Length: 3
+
+a=b
+HEADER
+)) {
+ while (!feof($fp)) {
+ $output .= fgets($fp);
+ }
+}
+
+echo preg_replace("/<style>(.*?)<\/style>/s", "<style>AAA</style>", $output), "\n";
+fclose($fp);
+
+
+$output = '';
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+GET /main/style.css HTTP/1.1
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ $output .= fgets($fp);
+ }
+}
+
+echo preg_replace("/<style>(.*?)<\/style>/s", "<style>AAA</style>", $output), "\n";
+fclose($fp);
+
+$output = '';
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+HEAD /main/foo/bar HTTP/1.1
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ $output .= fgets($fp);
+ }
+}
+
+echo preg_replace("/<style>(.*?)<\/style>/s", "<style>AAA</style>", $output), "\n";
+fclose($fp);
+?>
+--EXPECTF--
+
+HTTP/1.1 404 Not Found
+Host: %s
+Connection: close
+Content-Type: text/html; charset=UTF-8
+Content-Length: %d
+
+<!doctype html><html><head><title>404 Not Found</title><style>AAA</style>
+</head><body><h1>Not Found</h1><p>The requested resource / was not found on this server.</p></body></html>
+HTTP/1.1 404 Not Found
+Host: %s
+Connection: close
+Content-Type: text/html; charset=UTF-8
+Content-Length: %d
+
+<!doctype html><html><head><title>404 Not Found</title><style>AAA</style>
+</head><body><h1>Not Found</h1><p>The requested resource /main/style.css was not found on this server.</p></body></html>
+HTTP/1.1 404 Not Found
+Host: %s
+Connection: close
+Content-Type: text/html; charset=UTF-8
+Content-Length: %d
+
+<!doctype html><html><head><title>404 Not Found</title><style>AAA</style>
+</head><body><h1>Not Found</h1><p>The requested resource /main/foo/bar was not found on this server.</p></body></html>
+
diff --git a/sapi/cli/tests/php_cli_server_014.phpt b/sapi/cli/tests/php_cli_server_014.phpt
new file mode 100644
index 0000000..f8a9905
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_014.phpt
@@ -0,0 +1,80 @@
+--TEST--
+Bug #60477: Segfault after two multipart/form-data POST requestes
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('echo done, "\n";', TRUE);
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+$output = '';
+
+// note: select() on Windows (& some other platforms) has historical issues with
+// timeouts less than 1000 millis(0.5). it may be better to increase these
+// timeouts to 1000 millis(1.0) (fsockopen eventually calls select()).
+// see articles like: http://support.microsoft.com/kb/257821
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+POST /index.php HTTP/1.1
+Host: {$host}
+Content-Type: multipart/form-data; boundary=---------123456789
+Content-Length: 70
+
+---------123456789
+Content-Type: application/x-www-form-urlencoded
+a=b
+HEADER
+)) {
+ while (!feof($fp)) {
+ $output .= fgets($fp);
+ }
+}
+
+fclose($fp);
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if(fwrite($fp, <<<HEADER
+POST /main/no-exists.php HTTP/1.1
+Host: {$host}
+Content-Type: multipart/form-data; boundary=---------123456789
+Content-Length: 70
+
+---------123456789
+Content-Type: application/x-www-form-urlencoded
+a=b
+HEADER
+)) {
+ while (!feof($fp)) {
+ $output .= fgets($fp);
+ }
+}
+
+echo preg_replace("/<style>(.*?)<\/style>/s", "<style>AAA</style>", $output), "\n";
+fclose($fp);
+
+?>
+--EXPECTF--
+
+HTTP/1.1 200 OK
+Host: %s
+Connection: close
+X-Powered-By: %s
+Content-type: %s
+
+done
+HTTP/1.1 404 Not Found
+Host: %s
+Connection: close
+Content-Type: %s
+Content-Length: %d
+
+<!doctype html><html><head><title>404 Not Found</title><style>AAA</style>
+</head><body><h1>Not Found</h1><p>The requested resource /main/no-exists.php was not found on this server.</p></body></html>
diff --git a/sapi/cli/tests/php_cli_server_015.phpt b/sapi/cli/tests/php_cli_server_015.phpt
new file mode 100644
index 0000000..6fb0169
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_015.phpt
@@ -0,0 +1,49 @@
+--TEST--
+Bug #60523 (PHP Errors are not reported in browsers using built-in SAPI)
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--INI--
+display_errors=1
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start('require("syntax_error.php");');
+$dir = realpath(dirname(__FILE__));
+
+file_put_contents($dir . "/syntax_error.php", "<?php non_exists_function(); ?>");
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+$output = '';
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+GET /index.php HTTP/1.1
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ $output .= fgets($fp);
+ }
+}
+echo $output;
+@unlink($dir . "/syntax_error.php");
+fclose($fp);
+?>
+--EXPECTF--
+HTTP/1.1 200 OK
+Host: %s
+Connection: close
+X-Powered-By: PHP/%s
+Content-type: text/html
+
+<br />
+<b>Fatal error</b>: Call to undefined function non_exists_function() in <b>%ssyntax_error.php</b> on line <b>%s</b><br />
diff --git a/sapi/cli/tests/php_cli_server_016.phpt b/sapi/cli/tests/php_cli_server_016.phpt
new file mode 100644
index 0000000..f15aff1
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_016.phpt
@@ -0,0 +1,46 @@
+--TEST--
+Bug #60591 (Memory leak when access a non-exists file)
+--DESCRIPTION--
+this is an indirect test for bug 60591, since mem leak is reproted in the server side
+and require php compiled with --enable-debug
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start(<<<'PHP'
+if (preg_match('/\.(?:png|jpg|jpeg|gif)$/', $_SERVER["REQUEST_URI"]))
+ return false; // serve the requested resource as-is.
+else {
+ echo "here";
+}
+PHP
+);
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+POST /no-exists.jpg HTTP/1.1
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ break;
+ }
+}
+
+fclose($fp);
+?>
+--EXPECTF--
+HTTP/1.1 404 Not Found
diff --git a/sapi/cli/tests/php_cli_server_017.phpt b/sapi/cli/tests/php_cli_server_017.phpt
new file mode 100644
index 0000000..73530af
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_017.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Implement Req #60850 (Built in web server does not set $_SERVER['SCRIPT_FILENAME'] when using router)
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start(<<<'PHP'
+var_dump($_SERVER['SCRIPT_FILENAME']);
+PHP
+);
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+POST / HTTP/1.1
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+fclose($fp);
+?>
+--EXPECTF--
+HTTP/1.1 200 OK
+Host: %s
+Connection: close
+X-Powered-By: %s
+Content-type: text/html
+
+string(%d) "%sindex.php"
diff --git a/sapi/cli/tests/php_cli_server_018.phpt b/sapi/cli/tests/php_cli_server_018.phpt
new file mode 100644
index 0000000..deb9348
--- /dev/null
+++ b/sapi/cli/tests/php_cli_server_018.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Implement Req #61679 (Support HTTP PATCH method)
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+include "php_cli_server.inc";
+php_cli_server_start(<<<'PHP'
+var_dump($_SERVER['REQUEST_METHOD']);
+PHP
+);
+
+list($host, $port) = explode(':', PHP_CLI_SERVER_ADDRESS);
+$port = intval($port)?:80;
+
+$fp = fsockopen($host, $port, $errno, $errstr, 0.5);
+if (!$fp) {
+ die("connect failed");
+}
+
+if(fwrite($fp, <<<HEADER
+PATCH / HTTP/1.1
+Host: {$host}
+
+
+HEADER
+)) {
+ while (!feof($fp)) {
+ echo fgets($fp);
+ }
+}
+
+fclose($fp);
+?>
+--EXPECTF--
+HTTP/1.1 200 OK
+Host: %s
+Connection: close
+X-Powered-By: %s
+Content-type: text/html
+
+string(5) "PATCH"
diff --git a/sapi/cli/tests/skipif.inc b/sapi/cli/tests/skipif.inc
new file mode 100644
index 0000000..79e6c91
--- /dev/null
+++ b/sapi/cli/tests/skipif.inc
@@ -0,0 +1,7 @@
+<?php
+
+if (php_sapi_name() != "cli") {
+ die("skip CLI only");
+}
+
+?>
diff --git a/sapi/continuity/CREDITS b/sapi/continuity/CREDITS
new file mode 100644
index 0000000..35335e9
--- /dev/null
+++ b/sapi/continuity/CREDITS
@@ -0,0 +1,2 @@
+Continuity
+Alex Leigh (based on nsapi code)
diff --git a/sapi/continuity/capi.c b/sapi/continuity/capi.c
new file mode 100644
index 0000000..c635b52
--- /dev/null
+++ b/sapi/continuity/capi.c
@@ -0,0 +1,508 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Alex Leigh <php (at) postfin (dot) com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* For more information on Continuity: http://www.ashpool.com/ */
+
+/*
+ * This code is based on the PHP5 SAPI module for NSAPI by Jayakumar
+ * Muthukumarasamy
+ */
+
+/* PHP includes */
+#define CONTINUITY 1
+#define CAPI_DEBUG
+
+/* Define for CDP specific extensions */
+#undef CONTINUITY_CDPEXT
+
+#include "php.h"
+#include "php_variables.h"
+#include "ext/standard/info.h"
+#include "php_ini.h"
+#include "php_globals.h"
+#include "SAPI.h"
+#include "php_main.h"
+#include "php_version.h"
+#include "TSRM.h"
+#include "ext/standard/php_standard.h"
+
+/*
+ * CAPI includes
+ */
+#include <continuity.h>
+#include <http.h>
+
+#define NSLS_D struct capi_request_context *request_context
+#define NSLS_DC , NSLS_D
+#define NSLS_C request_context
+#define NSLS_CC , NSLS_C
+#define NSG(v) (request_context->v)
+
+/*
+ * ZTS needs to be defined for CAPI to work
+ */
+#if !defined(ZTS)
+#error "CAPI module needs ZTS to be defined"
+#endif
+
+/*
+ * Structure to encapsulate the CAPI request in SAPI
+ */
+typedef struct capi_request_context {
+ httpTtrans *t;
+ int read_post_bytes;
+} capi_request_context;
+
+/**************/
+
+PHP_MINIT_FUNCTION(continuity);
+PHP_MSHUTDOWN_FUNCTION(continuity);
+PHP_RINIT_FUNCTION(continuity);
+PHP_RSHUTDOWN_FUNCTION(continuity);
+PHP_MINFO_FUNCTION(continuity);
+
+PHP_FUNCTION(continuity_virtual);
+PHP_FUNCTION(continuity_request_headers);
+PHP_FUNCTION(continuity_response_headers);
+
+const zend_function_entry continuity_functions[] = {
+ {NULL, NULL, NULL}
+};
+
+zend_module_entry continuity_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "continuity",
+ continuity_functions,
+ PHP_MINIT(continuity),
+ PHP_MSHUTDOWN(continuity),
+ NULL,
+ NULL,
+ PHP_MINFO(continuity),
+ NO_VERSION_YET,
+ STANDARD_MODULE_PROPERTIES
+};
+
+PHP_MINIT_FUNCTION(continuity)
+{
+ return SUCCESS;
+}
+
+PHP_MSHUTDOWN_FUNCTION(continuity)
+{
+ return SUCCESS;
+}
+
+PHP_MINFO_FUNCTION(continuity)
+{
+ php_info_print_table_start();
+ php_info_print_table_row(2, "Continuity Module Revision", "$Id: 26762a5a963c1eb2e5bf91efbccadce7a8b2e40f $");
+ php_info_print_table_row(2, "Server Version", conFget_build());
+#ifdef CONTINUITY_CDPEXT
+ php_info_print_table_row(2,"CDP Extensions", "enabled");
+#else
+ php_info_print_table_row(2,"CDP Extensions", "disabled");
+#endif
+ php_info_print_table_end();
+
+/* DISPLAY_INI_ENTRIES(); */
+}
+
+/**************/
+
+/*
+ * sapi_capi_ub_write: Write len bytes to the connection output.
+ */
+static int sapi_capi_ub_write(const char *str, unsigned int str_length TSRMLS_DC)
+{
+ int retval;
+ capi_request_context *rc;
+
+ rc = (capi_request_context *) SG(server_context);
+ retval = httpFwrite(rc->t, (char *) str, str_length);
+ if (retval == -1 || retval == 0)
+ php_handle_aborted_connection();
+ return retval;
+}
+
+/*
+ * sapi_capi_header_handler: Add/update response headers with those provided
+ * by the PHP engine.
+ */
+static int sapi_capi_header_handler(sapi_header_struct * sapi_header, sapi_headers_struct * sapi_headers TSRMLS_DC)
+{
+ char *header_name, *header_content, *p;
+ capi_request_context *rc = (capi_request_context *) SG(server_context);
+
+ lstFset_delete_key(rc->t->res_hdrs, "Content-Type");
+
+ header_name = sapi_header->header;
+ header_content = p = strchr(header_name, ':');
+ if (p == NULL) {
+ return 0;
+ }
+ *p = 0;
+ do {
+ header_content++;
+ } while (*header_content == ' ');
+
+ lstFset_add(rc->t->res_hdrs, header_name, header_content);
+
+ *p = ':'; /* restore '*p' */
+
+ efree(sapi_header->header);
+
+ return 0; /* don't use the default SAPI mechanism, CAPI
+ * duplicates this functionality */
+}
+
+/*
+ * sapi_capi_send_headers: Transmit the headers to the client. This has the
+ * effect of starting the response under Continuity.
+ */
+static int sapi_capi_send_headers(sapi_headers_struct * sapi_headers TSRMLS_DC)
+{
+ int retval;
+ capi_request_context *rc = (capi_request_context *) SG(server_context);
+
+ /*
+ * We could probably just do this in the header_handler. But, I don't know
+ * what the implication of doing it there is.
+ */
+
+ if (SG(sapi_headers).send_default_content_type) {
+ /* lstFset_delete_key(rc->t->res_hdrs, "Content-Type"); */
+ lstFset_update(rc->t->res_hdrs, "Content-Type", "text/html");
+ }
+ httpFset_status(rc->t, SG(sapi_headers).http_response_code, NULL);
+ httpFstart_response(rc->t);
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+
+}
+
+static int sapi_capi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ unsigned int max_read, total_read = 0;
+ capi_request_context *rc = (capi_request_context *) SG(server_context);
+
+ if (rc->read_post_bytes == -1) {
+ max_read = MIN(count_bytes, SG(request_info).content_length);
+ } else {
+ if (rc->read_post_bytes == 0)
+ return 0;
+ max_read = MIN(count_bytes, (SG(request_info).content_length - rc->read_post_bytes));
+ }
+
+ total_read = httpFread(rc->t, buffer, max_read);
+
+ if (total_read < 0)
+ total_read = -1;
+ else
+ rc->read_post_bytes = total_read;
+
+ return total_read;
+}
+
+/*
+ * sapi_capi_read_cookies: Return cookie information into PHP.
+ */
+static char *sapi_capi_read_cookies(TSRMLS_D)
+{
+ char *cookie_string;
+ capi_request_context *rc = (capi_request_context *) SG(server_context);
+
+ cookie_string = lstFset_get(rc->t->req_hdrs, "cookie");
+ return cookie_string;
+}
+
+static void sapi_capi_register_server_variables(zval * track_vars_array TSRMLS_DC)
+{
+ capi_request_context *rc = (capi_request_context *) SG(server_context);
+ size_t i;
+ char *value;
+ char buf[128];
+
+ /* PHP_SELF and REQUEST_URI */
+ value = lstFset_get(rc->t->vars, "uri");
+ if (value != NULL) {
+ php_register_variable("PHP_SELF", value, track_vars_array TSRMLS_CC);
+ php_register_variable("REQUEST_URI", value, track_vars_array TSRMLS_CC);
+ }
+
+ /* COUNTRY CODE */
+ value = lstFset_get(rc->t->vars, "ccode");
+ if(value!=NULL)
+ php_register_variable("COUNTRY_CODE", value, track_vars_array TSRMLS_CC);
+
+ /* argv */
+ value = lstFset_get(rc->t->vars, "query");
+ if (value != NULL)
+ php_register_variable("argv", value, track_vars_array TSRMLS_CC);
+
+ /* GATEWAY_INTERFACE */
+ php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC);
+
+ /* SERVER_NAME and HTTP_HOST */
+ value = lstFset_get(rc->t->req_hdrs, "host");
+ if (value != NULL) {
+ php_register_variable("HTTP_HOST", value, track_vars_array TSRMLS_CC);
+ /* TODO: This should probably scrub the port value if one is present. */
+ php_register_variable("SERVER_NAME", value, track_vars_array TSRMLS_CC);
+ }
+ /* SERVER_SOFTWARE */
+ value = lstFset_get(rc->t->res_hdrs, "Server");
+ if (value != NULL)
+ php_register_variable("SERVER_SOFTWARE", value, track_vars_array TSRMLS_CC);
+
+ /* SERVER_PROTOCOL */
+ value = lstFset_get(rc->t->vars, "protocol");
+ if (value != NULL)
+ php_register_variable("SERVER_PROTOCOL", value, track_vars_array TSRMLS_CC);
+
+ /* REQUEST_METHOD */
+ value = lstFset_get(rc->t->vars, "method");
+ if (value != NULL)
+ php_register_variable("REQUEST_METHOD", value, track_vars_array TSRMLS_CC);
+
+ /* QUERY_STRING */
+ value = lstFset_get(rc->t->vars, "query");
+ if (value != NULL)
+ php_register_variable("QUERY_STRING", value, track_vars_array TSRMLS_CC);
+
+ /* DOCUMENT_ROOT */
+ value = lstFset_get(rc->t->vars, "docroot");
+ if (value != NULL)
+ php_register_variable("DOCUMENT_ROOT", value, track_vars_array TSRMLS_CC);
+
+ /* HTTP_ACCEPT */
+ value = lstFset_get(rc->t->req_hdrs, "accept");
+ if (value != NULL)
+ php_register_variable("HTTP_ACCEPT", value, track_vars_array TSRMLS_CC);
+
+ /* HTTP_ACCEPT_CHARSET */
+ value = lstFset_get(rc->t->req_hdrs, "accept-charset");
+ if (value != NULL)
+ php_register_variable("HTTP_ACCEPT_CHARSET", value, track_vars_array TSRMLS_CC);
+
+ /* HTTP_ACCEPT_ENCODING */
+ value = lstFset_get(rc->t->req_hdrs, "accept-encoding");
+ if (value != NULL)
+ php_register_variable("HTTP_ACCEPT_ENCODING", value, track_vars_array TSRMLS_CC);
+
+ /* HTTP_ACCEPT_LANGUAGE */
+ value = lstFset_get(rc->t->req_hdrs, "accept-language");
+ if (value != NULL)
+ php_register_variable("HTTP_ACCEPT_LANGUAGE", value, track_vars_array TSRMLS_CC);
+
+ /* HTTP_CONNECTION */
+ value = lstFset_get(rc->t->req_hdrs, "connection");
+ if (value != NULL)
+ php_register_variable("HTTP_CONNECTION", value, track_vars_array TSRMLS_CC);
+
+ /* HTTP_REFERER */
+ value = lstFset_get(rc->t->req_hdrs, "referer");
+ if (value != NULL)
+ php_register_variable("HTTP_REFERER", value, track_vars_array TSRMLS_CC);
+
+ /* HTTP_USER_AGENT */
+ value = lstFset_get(rc->t->req_hdrs, "user-agent");
+ if (value != NULL)
+ php_register_variable("HTTP_USER_AGENT", value, track_vars_array TSRMLS_CC);
+
+ /* REMOTE_ADDR */
+ utlFip_to_str(rc->t->cli_ipv4_addr, buf, sizeof(buf));
+ php_register_variable("REMOTE_ADDR", buf, track_vars_array TSRMLS_CC);
+
+ /* REMOTE_PORT */
+
+ /* SCRIPT_FILENAME and PATH_TRANSLATED */
+ value = lstFset_get(rc->t->vars, "path");
+ if (value != NULL) {
+ php_register_variable("SCRIPT_FILENAME", value, track_vars_array TSRMLS_CC);
+ php_register_variable("PATH_TRANSLATED", value, track_vars_array TSRMLS_CC);
+ }
+ /* SERVER_ADMIN */
+ /* Not applicable */
+
+ /* SERVER_PORT */
+
+}
+
+static void capi_log_message(char *message TSRMLS_DC)
+{
+ capi_request_context *rc = (capi_request_context *) SG(server_context);
+ logFmsg(0, "mod/php: %s", message);
+}
+
+static int php_capi_startup(sapi_module_struct *sapi_module);
+
+sapi_module_struct capi_sapi_module = {
+ "Continuity", /* name */
+ "Continuity Server Enterprise Edition", /* pretty name */
+
+ php_capi_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ sapi_capi_ub_write, /* unbuffered write */
+ NULL, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ sapi_capi_header_handler, /* header handler */
+ sapi_capi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ sapi_capi_read_post, /* read POST data */
+ sapi_capi_read_cookies, /* read Cookies */
+
+ sapi_capi_register_server_variables, /* register server variables */
+ capi_log_message, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ NULL, /* Block interruptions */
+ NULL, /* Unblock interruptions */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+static int php_capi_startup(sapi_module_struct *sapi_module) {
+ if(php_module_startup(sapi_module,&continuity_module_entry,1)==FAILURE) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+
+static char *
+ capi_strdup(char *str)
+{
+ if (str != NULL)
+ return strFcopy(str);
+ return NULL;
+}
+
+static void capi_free(void *addr)
+{
+ if (addr != NULL)
+ free(addr);
+}
+
+static void capi_request_ctor(NSLS_D TSRMLS_DC)
+{
+ char *query_string = lstFset_get(NSG(t->vars), "query");
+ char *uri = lstFset_get(NSG(t->vars), "uri");
+ char *path_info = lstFset_get(NSG(t->vars), "path-info");
+ char *path_translated = lstFset_get(NSG(t->vars), "path");
+ char *request_method = lstFset_get(NSG(t->vars), "method");
+ char *content_type = lstFset_get(NSG(t->req_hdrs), "content-type");
+ char *content_length = lstFset_get(NSG(t->req_hdrs), "content-length");
+
+ SG(request_info).query_string = capi_strdup(query_string);
+ SG(request_info).request_uri = capi_strdup(uri);
+ SG(request_info).request_method = capi_strdup(request_method);
+ SG(request_info).path_translated = capi_strdup(path_translated);
+ SG(request_info).content_type = capi_strdup(content_type);
+ SG(request_info).content_length = (content_length == NULL) ? 0 : strtoul(content_length, 0, 0);
+ SG(sapi_headers).http_response_code = 200;
+}
+
+static void capi_request_dtor(NSLS_D TSRMLS_DC)
+{
+ capi_free(SG(request_info).query_string);
+ capi_free(SG(request_info).request_uri);
+ capi_free(SG(request_info).request_method);
+ capi_free(SG(request_info).path_translated);
+ capi_free(SG(request_info).content_type);
+}
+
+int capi_module_main(NSLS_D TSRMLS_DC)
+{
+ zend_file_handle file_handle;
+
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ return FAILURE;
+ }
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.free_filename = 0;
+ file_handle.opened_path = NULL;
+
+ php_execute_script(&file_handle TSRMLS_CC);
+ php_request_shutdown(NULL);
+
+ return SUCCESS;
+}
+
+int phpFinit(lstTset * opt)
+{
+ php_core_globals *core_globals;
+
+ tsrm_startup(128, 1, 0, NULL);
+ core_globals = ts_resource(core_globals_id);
+
+ logFmsg(0, "mod/php: PHP Interface v3 (module)");
+ logFmsg(0, "mod/php: Copyright (c) 1999-2005 The PHP Group. All rights reserved.");
+
+ sapi_startup(&capi_sapi_module);
+ capi_sapi_module.startup(&capi_sapi_module);
+
+ return STATUS_PROCEED;
+}
+
+int phpFservice(httpTtrans * t, lstTset * opts)
+{
+ int retval;
+ capi_request_context *request_context;
+
+ TSRMLS_FETCH();
+
+ request_context = (capi_request_context *) malloc(sizeof(capi_request_context));
+ request_context->t = t;
+ request_context->read_post_bytes = -1;
+
+ SG(server_context) = request_context;
+
+ capi_request_ctor(NSLS_C TSRMLS_CC);
+ retval = capi_module_main(NSLS_C TSRMLS_CC);
+ capi_request_dtor(NSLS_C TSRMLS_CC);
+
+ free(request_context);
+
+ /*
+ * This call is ostensibly provided to free the memory from PHP/TSRM when
+ * the thread terminated, but, it leaks a structure in some hash list
+ * according to the developers. Not calling this will leak the entire
+ * interpreter, around 100k, but calling it and then terminating the
+ * thread will leak the struct (around a k). The only answer with the
+ * current TSRM implementation is to reuse the threads that allocate TSRM
+ * resources.
+ */
+ /* ts_free_thread(); */
+
+ if (retval == SUCCESS) {
+ return STATUS_EXIT;
+ } else {
+ return STATUS_ERROR;
+ }
+}
diff --git a/sapi/continuity/config.m4 b/sapi/continuity/config.m4
new file mode 100644
index 0000000..8d27419
--- /dev/null
+++ b/sapi/continuity/config.m4
@@ -0,0 +1,28 @@
+dnl ## $Id$ -*- sh -*-
+
+PHP_ARG_WITH(continuity, for Continuity support,
+[ --with-continuity=DIR Build PHP as Continuity Server module.
+ DIR is path to the installed Continuity Server root], no, no)
+
+if test "$PHP_CONTINUITY" != "no"; then
+ if test ! -d $PHP_CONTINUITY; then
+ AC_MSG_ERROR([Please specify the path to the root of your Continuity server using --with-continuity=DIR])
+ fi
+ AC_MSG_CHECKING([for Continuity include files])
+ if test -d $PHP_CONTINUITY/include ; then
+ CAPI_INCLUDE=$PHP_CONTINUITY/include
+ AC_MSG_RESULT([Continuity Binary Distribution])
+ else
+ AC_MSG_ERROR([Cannot find your CAPI include files in either DIR/src or DIR/include])
+ fi
+
+ PHP_SELECT_SAPI(continuity, shared, capi.c)
+ PHP_ADD_INCLUDE($CAPI_INCLUDE)
+ PHP_BUILD_THREAD_SAFE
+ AC_DEFINE(HAVE_CONTINUITY, 1, [Whether you have a Continuity Server])
+ INSTALL_IT="\$(INSTALL) -m 0755 $SAPI_SHARED \$(INSTALL_ROOT)$PHP_CONTINUITY/lib/"
+fi
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/embed/CREDITS b/sapi/embed/CREDITS
new file mode 100644
index 0000000..a5227b4
--- /dev/null
+++ b/sapi/embed/CREDITS
@@ -0,0 +1,2 @@
+Embed
+Edin Kadribasic
diff --git a/sapi/embed/EXPERIMENTAL b/sapi/embed/EXPERIMENTAL
new file mode 100644
index 0000000..293159a
--- /dev/null
+++ b/sapi/embed/EXPERIMENTAL
@@ -0,0 +1,5 @@
+this module is experimental,
+its functions may change their names
+or move to extension all together
+so do not rely to much on them
+you have been warned!
diff --git a/sapi/embed/config.m4 b/sapi/embed/config.m4
new file mode 100644
index 0000000..3a61b45
--- /dev/null
+++ b/sapi/embed/config.m4
@@ -0,0 +1,33 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_ENABLE(embed,,
+[ --enable-embed[=TYPE] EXPERIMENTAL: Enable building of embedded SAPI library
+ TYPE is either 'shared' or 'static'. [TYPE=shared]], no, no)
+
+AC_MSG_CHECKING([for embedded SAPI library support])
+
+if test "$PHP_EMBED" != "no"; then
+ case "$PHP_EMBED" in
+ yes|shared)
+ PHP_EMBED_TYPE=shared
+ INSTALL_IT="\$(mkinstalldirs) \$(INSTALL_ROOT)\$(prefix)/lib; \$(INSTALL) -m 0755 $SAPI_SHARED \$(INSTALL_ROOT)\$(prefix)/lib"
+ ;;
+ static)
+ PHP_EMBED_TYPE=static
+ INSTALL_IT="\$(mkinstalldirs) \$(INSTALL_ROOT)\$(prefix)/lib; \$(INSTALL) -m 0644 $SAPI_STATIC \$(INSTALL_ROOT)\$(prefix)/lib"
+ ;;
+ *)
+ PHP_EMBED_TYPE=no
+ ;;
+ esac
+ if test "$PHP_EMBED_TYPE" != "no"; then
+ PHP_SELECT_SAPI(embed, $PHP_EMBED_TYPE, php_embed.c)
+ PHP_INSTALL_HEADERS([sapi/embed/php_embed.h])
+ fi
+ AC_MSG_RESULT([$PHP_EMBED_TYPE])
+else
+ AC_MSG_RESULT(no)
+fi
+
diff --git a/sapi/embed/config.w32 b/sapi/embed/config.w32
new file mode 100644
index 0000000..f3cc60d
--- /dev/null
+++ b/sapi/embed/config.w32
@@ -0,0 +1,9 @@
+// vim:ft=javascript
+// $Id$
+
+ARG_ENABLE('embed', 'Embedded SAPI library', 'no');
+
+if (PHP_EMBED != "no") {
+ SAPI('embed', 'php_embed.c', 'php' + PHP_VERSION + 'embed.lib');
+ PHP_INSTALL_HEADERS("sapi/embed", "php_embed.h");
+}
diff --git a/sapi/embed/php5embed.dsp b/sapi/embed/php5embed.dsp
new file mode 100644
index 0000000..8564b11
--- /dev/null
+++ b/sapi/embed/php5embed.dsp
@@ -0,0 +1,100 @@
+# Microsoft Developer Studio Project File - Name="php5embed" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=php5embed - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "php5embed.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "php5embed.mak" CFG="php5embed - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "php5embed - Win32 Debug_TS" (based on "Win32 (x86) Static Library")
+!MESSAGE "php5embed - Win32 Release_TS" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "php5embed - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\TSRM" /D "_DEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D ZEND_DEBUG=1 /YX /FD /GZ /c
+# ADD BASE RSC /l 0x406 /d "_DEBUG"
+# ADD RSC /l 0x406 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"..\..\Debug_TS\php5embed.lib"
+
+!ELSEIF "$(CFG)" == "php5embed - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\.." /I "..\..\main" /I "..\..\Zend" /I "..\..\TSRM" /D "NDEBUG" /D "WIN32" /D "_MBCS" /D "_LIB" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D ZEND_DEBUG=0 /YX /FD /c
+# ADD BASE RSC /l 0x406 /d "NDEBUG"
+# ADD RSC /l 0x406 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo /out:"..\..\Release_TS\php5embed.lib"
+
+!ENDIF
+
+# Begin Target
+
+# Name "php5embed - Win32 Debug_TS"
+# Name "php5embed - Win32 Release_TS"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=php_embed.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=php_embed.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/sapi/embed/php_embed.c b/sapi/embed/php_embed.c
new file mode 100644
index 0000000..414b4db
--- /dev/null
+++ b/sapi/embed/php_embed.c
@@ -0,0 +1,241 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Edin Kadribasic <edink@php.net> |
+ +----------------------------------------------------------------------+
+*/
+/* $Id$ */
+
+#include "php_embed.h"
+#include "ext/standard/php_standard.h"
+
+#ifdef PHP_WIN32
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+const char HARDCODED_INI[] =
+ "html_errors=0\n"
+ "register_argc_argv=1\n"
+ "implicit_flush=1\n"
+ "output_buffering=0\n"
+ "max_execution_time=0\n"
+ "max_input_time=-1\n\0";
+
+static char* php_embed_read_cookies(TSRMLS_D)
+{
+ return NULL;
+}
+
+static int php_embed_deactivate(TSRMLS_D)
+{
+ fflush(stdout);
+ return SUCCESS;
+}
+
+static inline size_t php_embed_single_write(const char *str, uint str_length)
+{
+#ifdef PHP_WRITE_STDOUT
+ long ret;
+
+ ret = write(STDOUT_FILENO, str, str_length);
+ if (ret <= 0) return 0;
+ return ret;
+#else
+ size_t ret;
+
+ ret = fwrite(str, 1, MIN(str_length, 16384), stdout);
+ return ret;
+#endif
+}
+
+
+static int php_embed_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ const char *ptr = str;
+ uint remaining = str_length;
+ size_t ret;
+
+ while (remaining > 0) {
+ ret = php_embed_single_write(ptr, remaining);
+ if (!ret) {
+ php_handle_aborted_connection();
+ }
+ ptr += ret;
+ remaining -= ret;
+ }
+
+ return str_length;
+}
+
+static void php_embed_flush(void *server_context)
+{
+ if (fflush(stdout)==EOF) {
+ php_handle_aborted_connection();
+ }
+}
+
+static void php_embed_send_header(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC)
+{
+}
+
+static void php_embed_log_message(char *message TSRMLS_DC)
+{
+ fprintf (stderr, "%s\n", message);
+}
+
+static void php_embed_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ php_import_environment_variables(track_vars_array TSRMLS_CC);
+}
+
+static int php_embed_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, NULL, 0)==FAILURE) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+extern EMBED_SAPI_API sapi_module_struct php_embed_module = {
+ "embed", /* name */
+ "PHP Embedded Library", /* pretty name */
+
+ php_embed_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ php_embed_deactivate, /* deactivate */
+
+ php_embed_ub_write, /* unbuffered write */
+ php_embed_flush, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ NULL, /* header handler */
+ NULL, /* send headers handler */
+ php_embed_send_header, /* send header handler */
+
+ NULL, /* read POST data */
+ php_embed_read_cookies, /* read Cookies */
+
+ php_embed_register_variables, /* register server variables */
+ php_embed_log_message, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+/* }}} */
+
+/* {{{ arginfo ext/standard/dl.c */
+ZEND_BEGIN_ARG_INFO(arginfo_dl, 0)
+ ZEND_ARG_INFO(0, extension_filename)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+static const zend_function_entry additional_functions[] = {
+ ZEND_FE(dl, arginfo_dl)
+ {NULL, NULL, NULL}
+};
+
+EMBED_SAPI_API int php_embed_init(int argc, char **argv PTSRMLS_DC)
+{
+ zend_llist global_vars;
+#ifdef ZTS
+ void ***tsrm_ls = NULL;
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#if defined(SIGPIPE) && defined(SIG_IGN)
+ signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
+ that sockets created via fsockopen()
+ don't kill PHP if the remote site
+ closes it. in apache|apxs mode apache
+ does that for us! thies@thieso.net
+ 20000419 */
+#endif
+#endif
+
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+ tsrm_ls = ts_resource(0);
+ *ptsrm_ls = tsrm_ls;
+#endif
+
+ sapi_startup(&php_embed_module);
+
+#ifdef PHP_WIN32
+ _fmode = _O_BINARY; /*sets default for file streams to binary */
+ setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
+#endif
+
+ php_embed_module.ini_entries = malloc(sizeof(HARDCODED_INI));
+ memcpy(php_embed_module.ini_entries, HARDCODED_INI, sizeof(HARDCODED_INI));
+
+ php_embed_module.additional_functions = additional_functions;
+
+ if (argv) {
+ php_embed_module.executable_location = argv[0];
+ }
+
+ if (php_embed_module.startup(&php_embed_module)==FAILURE) {
+ return FAILURE;
+ }
+
+ zend_llist_init(&global_vars, sizeof(char *), NULL, 0);
+
+ /* Set some Embedded PHP defaults */
+ SG(options) |= SAPI_OPTION_NO_CHDIR;
+ SG(request_info).argc=argc;
+ SG(request_info).argv=argv;
+
+ if (php_request_startup(TSRMLS_C)==FAILURE) {
+ php_module_shutdown(TSRMLS_C);
+ return FAILURE;
+ }
+
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+ php_register_variable("PHP_SELF", "-", NULL TSRMLS_CC);
+
+ return SUCCESS;
+}
+
+EMBED_SAPI_API void php_embed_shutdown(TSRMLS_D)
+{
+ php_request_shutdown((void *) 0);
+ php_module_shutdown(TSRMLS_C);
+ sapi_shutdown();
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ if (php_embed_module.ini_entries) {
+ free(php_embed_module.ini_entries);
+ php_embed_module.ini_entries = NULL;
+ }
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/embed/php_embed.h b/sapi/embed/php_embed.h
new file mode 100644
index 0000000..ad5f4bc
--- /dev/null
+++ b/sapi/embed/php_embed.h
@@ -0,0 +1,73 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Edin Kadribasic <edink@php.net> |
+ +----------------------------------------------------------------------+
+*/
+/* $Id$ */
+
+#ifndef _PHP_EMBED_H_
+#define _PHP_EMBED_H_
+
+#include <main/php.h>
+#include <main/SAPI.h>
+#include <main/php_main.h>
+#include <main/php_variables.h>
+#include <main/php_ini.h>
+#include <zend_ini.h>
+
+#ifdef ZTS
+#define PTSRMLS_D void ****ptsrm_ls
+#define PTSRMLS_DC , PTSRMLS_D
+#define PTSRMLS_C &tsrm_ls
+#define PTSRMLS_CC , PTSRMLS_C
+
+#define PHP_EMBED_START_BLOCK(x,y) { \
+ void ***tsrm_ls; \
+ php_embed_init(x, y PTSRMLS_CC); \
+ zend_first_try {
+
+#else
+#define PTSRMLS_D
+#define PTSRMLS_DC
+#define PTSRMLS_C
+#define PTSRMLS_CC
+
+#define PHP_EMBED_START_BLOCK(x,y) { \
+ php_embed_init(x, y); \
+ zend_first_try {
+
+#endif
+
+#define PHP_EMBED_END_BLOCK() \
+ } zend_catch { \
+ /* int exit_status = EG(exit_status); */ \
+ } zend_end_try(); \
+ php_embed_shutdown(TSRMLS_C); \
+}
+
+#ifndef PHP_WIN32
+ #define EMBED_SAPI_API SAPI_API
+#else
+ #define EMBED_SAPI_API
+#endif
+
+BEGIN_EXTERN_C()
+EMBED_SAPI_API int php_embed_init(int argc, char **argv PTSRMLS_DC);
+EMBED_SAPI_API void php_embed_shutdown(TSRMLS_D);
+extern EMBED_SAPI_API sapi_module_struct php_embed_module;
+END_EXTERN_C()
+
+
+#endif /* _PHP_EMBED_H_ */
diff --git a/sapi/fpm/CREDITS b/sapi/fpm/CREDITS
new file mode 100644
index 0000000..cd87daa
--- /dev/null
+++ b/sapi/fpm/CREDITS
@@ -0,0 +1,2 @@
+FastCGI Process Manager
+Andrei Nigmatulin, dreamcat4, Antony Dovgal, Jerome Loyet
diff --git a/sapi/fpm/LICENSE b/sapi/fpm/LICENSE
new file mode 100644
index 0000000..4e1dd99
--- /dev/null
+++ b/sapi/fpm/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2007-2009, Andrei Nigmatulin
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/sapi/fpm/Makefile.frag b/sapi/fpm/Makefile.frag
new file mode 100644
index 0000000..6ed9e4a
--- /dev/null
+++ b/sapi/fpm/Makefile.frag
@@ -0,0 +1,23 @@
+fpm: $(SAPI_FPM_PATH)
+
+$(SAPI_FPM_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_FPM_OBJS)
+ $(BUILD_FPM)
+
+install-fpm: $(SAPI_FPM_PATH)
+ @echo "Installing PHP FPM binary: $(INSTALL_ROOT)$(sbindir)/"
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(sbindir)
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/log
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(localstatedir)/run
+ @$(INSTALL) -m 0755 $(SAPI_FPM_PATH) $(INSTALL_ROOT)$(sbindir)/$(program_prefix)php-fpm$(program_suffix)$(EXEEXT)
+
+ @echo "Installing PHP FPM config: $(INSTALL_ROOT)$(sysconfdir)/" && \
+ $(mkinstalldirs) $(INSTALL_ROOT)$(sysconfdir) || :
+ @$(INSTALL_DATA) sapi/fpm/php-fpm.conf $(INSTALL_ROOT)$(sysconfdir)/php-fpm.conf.default || :
+
+ @echo "Installing PHP FPM man page: $(INSTALL_ROOT)$(mandir)/man8/"
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(mandir)/man8
+ @$(INSTALL_DATA) sapi/fpm/php-fpm.8 $(INSTALL_ROOT)$(mandir)/man8/php-fpm$(program_suffix).8
+
+ @echo "Installing PHP FPM status page: $(INSTALL_ROOT)$(datadir)/fpm/"
+ @$(mkinstalldirs) $(INSTALL_ROOT)$(datadir)/fpm
+ @$(INSTALL_DATA) sapi/fpm/status.html $(INSTALL_ROOT)$(datadir)/fpm/status.html
diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4
new file mode 100644
index 0000000..6191c32
--- /dev/null
+++ b/sapi/fpm/config.m4
@@ -0,0 +1,657 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_ENABLE(fpm,,
+[ --enable-fpm Enable building of the fpm SAPI executable], no, no)
+
+dnl configure checks {{{
+AC_DEFUN([AC_FPM_STDLIBS],
+[
+ AC_CHECK_FUNCS(setenv clearenv setproctitle)
+
+ AC_SEARCH_LIBS(socket, socket)
+ AC_SEARCH_LIBS(inet_addr, nsl)
+
+ AC_CHECK_HEADERS([errno.h fcntl.h stdio.h stdlib.h unistd.h sys/uio.h])
+ AC_CHECK_HEADERS([sys/select.h sys/socket.h sys/time.h])
+ AC_CHECK_HEADERS([arpa/inet.h netinet/in.h])
+ AC_CHECK_HEADERS([sysexits.h])
+])
+
+AC_DEFUN([AC_FPM_PRCTL],
+[
+ AC_MSG_CHECKING([for prctl])
+
+ AC_TRY_COMPILE([ #include <sys/prctl.h> ], [prctl(0, 0, 0, 0, 0);], [
+ AC_DEFINE([HAVE_PRCTL], 1, [do we have prctl?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+
+AC_DEFUN([AC_FPM_CLOCK],
+[
+ have_clock_gettime=no
+
+ AC_MSG_CHECKING([for clock_gettime])
+
+ AC_TRY_LINK([ #include <time.h> ], [struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);], [
+ have_clock_gettime=yes
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+
+ if test "$have_clock_gettime" = "no"; then
+ AC_MSG_CHECKING([for clock_gettime in -lrt])
+
+ SAVED_LIBS="$LIBS"
+ LIBS="$LIBS -lrt"
+
+ AC_TRY_LINK([ #include <time.h> ], [struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);], [
+ have_clock_gettime=yes
+ AC_MSG_RESULT([yes])
+ ], [
+ LIBS="$SAVED_LIBS"
+ AC_MSG_RESULT([no])
+ ])
+ fi
+
+ if test "$have_clock_gettime" = "yes"; then
+ AC_DEFINE([HAVE_CLOCK_GETTIME], 1, [do we have clock_gettime?])
+ fi
+
+ have_clock_get_time=no
+
+ if test "$have_clock_gettime" = "no"; then
+ AC_MSG_CHECKING([for clock_get_time])
+
+ AC_TRY_RUN([ #include <mach/mach.h>
+ #include <mach/clock.h>
+ #include <mach/mach_error.h>
+
+ int main()
+ {
+ kern_return_t ret; clock_serv_t aClock; mach_timespec_t aTime;
+ ret = host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &aClock);
+
+ if (ret != KERN_SUCCESS) {
+ return 1;
+ }
+
+ ret = clock_get_time(aClock, &aTime);
+ if (ret != KERN_SUCCESS) {
+ return 2;
+ }
+
+ return 0;
+ }
+ ], [
+ have_clock_get_time=yes
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+ fi
+
+ if test "$have_clock_get_time" = "yes"; then
+ AC_DEFINE([HAVE_CLOCK_GET_TIME], 1, [do we have clock_get_time?])
+ fi
+])
+
+AC_DEFUN([AC_FPM_TRACE],
+[
+ have_ptrace=no
+ have_broken_ptrace=no
+
+ AC_MSG_CHECKING([for ptrace])
+
+ AC_TRY_COMPILE([
+ #include <sys/types.h>
+ #include <sys/ptrace.h> ], [ptrace(0, 0, (void *) 0, 0);], [
+ have_ptrace=yes
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+
+ if test "$have_ptrace" = "yes"; then
+ AC_MSG_CHECKING([whether ptrace works])
+
+ AC_TRY_RUN([
+ #include <unistd.h>
+ #include <signal.h>
+ #include <sys/wait.h>
+ #include <sys/types.h>
+ #include <sys/ptrace.h>
+ #include <errno.h>
+
+ #if !defined(PTRACE_ATTACH) && defined(PT_ATTACH)
+ #define PTRACE_ATTACH PT_ATTACH
+ #endif
+
+ #if !defined(PTRACE_DETACH) && defined(PT_DETACH)
+ #define PTRACE_DETACH PT_DETACH
+ #endif
+
+ #if !defined(PTRACE_PEEKDATA) && defined(PT_READ_D)
+ #define PTRACE_PEEKDATA PT_READ_D
+ #endif
+
+ int main()
+ {
+ long v1 = (unsigned int) -1; /* copy will fail if sizeof(long) == 8 and we've got "int ptrace()" */
+ long v2;
+ pid_t child;
+ int status;
+
+ if ( (child = fork()) ) { /* parent */
+ int ret = 0;
+
+ if (0 > ptrace(PTRACE_ATTACH, child, 0, 0)) {
+ return 2;
+ }
+
+ waitpid(child, &status, 0);
+
+ #ifdef PT_IO
+ struct ptrace_io_desc ptio = {
+ .piod_op = PIOD_READ_D,
+ .piod_offs = &v1,
+ .piod_addr = &v2,
+ .piod_len = sizeof(v1)
+ };
+
+ if (0 > ptrace(PT_IO, child, (void *) &ptio, 0)) {
+ ret = 3;
+ }
+ #else
+ errno = 0;
+
+ v2 = ptrace(PTRACE_PEEKDATA, child, (void *) &v1, 0);
+
+ if (errno) {
+ ret = 4;
+ }
+ #endif
+ ptrace(PTRACE_DETACH, child, (void *) 1, 0);
+
+ kill(child, SIGKILL);
+
+ return ret ? ret : (v1 != v2);
+ }
+ else { /* child */
+ sleep(10);
+ return 0;
+ }
+ }
+ ], [
+ AC_MSG_RESULT([yes])
+ ], [
+ have_ptrace=no
+ have_broken_ptrace=yes
+ AC_MSG_RESULT([no])
+ ], [
+ AC_MSG_RESULT([skipped (cross compiling)])
+ ])
+ fi
+
+ if test "$have_ptrace" = "yes"; then
+ AC_DEFINE([HAVE_PTRACE], 1, [do we have ptrace?])
+ fi
+
+ have_mach_vm_read=no
+
+ if test "$have_broken_ptrace" = "yes"; then
+ AC_MSG_CHECKING([for mach_vm_read])
+
+ AC_TRY_COMPILE([ #include <mach/mach.h>
+ #include <mach/mach_vm.h>
+ ], [
+ mach_vm_read((vm_map_t)0, (mach_vm_address_t)0, (mach_vm_size_t)0, (vm_offset_t *)0, (mach_msg_type_number_t*)0);
+ ], [
+ have_mach_vm_read=yes
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+ fi
+
+ if test "$have_mach_vm_read" = "yes"; then
+ AC_DEFINE([HAVE_MACH_VM_READ], 1, [do we have mach_vm_read?])
+ fi
+
+ proc_mem_file=""
+
+ if test -r /proc/$$/mem ; then
+ proc_mem_file="mem"
+ else
+ if test -r /proc/$$/as ; then
+ proc_mem_file="as"
+ fi
+ fi
+
+ if test -n "$proc_mem_file" ; then
+ AC_MSG_CHECKING([for proc mem file])
+
+ AC_TRY_RUN([
+ #define _GNU_SOURCE
+ #define _FILE_OFFSET_BITS 64
+ #include <stdint.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <stdio.h>
+ int main()
+ {
+ long v1 = (unsigned int) -1, v2 = 0;
+ char buf[128];
+ int fd;
+ sprintf(buf, "/proc/%d/$proc_mem_file", getpid());
+ fd = open(buf, O_RDONLY);
+ if (0 > fd) {
+ return 1;
+ }
+ if (sizeof(long) != pread(fd, &v2, sizeof(long), (uintptr_t) &v1)) {
+ close(fd);
+ return 1;
+ }
+ close(fd);
+ return v1 != v2;
+ }
+ ], [
+ AC_MSG_RESULT([$proc_mem_file])
+ ], [
+ proc_mem_file=""
+ AC_MSG_RESULT([no])
+ ], [
+ AC_MSG_RESULT([skipped (cross compiling)])
+ ])
+ fi
+
+ if test -n "$proc_mem_file"; then
+ AC_DEFINE_UNQUOTED([PROC_MEM_FILE], "$proc_mem_file", [/proc/pid/mem interface])
+ fi
+
+ fpm_trace_type=""
+
+ if test "$have_ptrace" = "yes"; then
+ fpm_trace_type=ptrace
+
+ elif test -n "$proc_mem_file"; then
+ fpm_trace_type=pread
+
+ elif test "$have_mach_vm_read" = "yes" ; then
+ fpm_trace_type=mach
+
+ else
+ AC_MSG_WARN([FPM Trace - ptrace, pread, or mach: could not be found])
+ fi
+
+])
+
+AC_DEFUN([AC_FPM_BUILTIN_ATOMIC],
+[
+ AC_MSG_CHECKING([if gcc supports __sync_bool_compare_and_swap])
+ AC_TRY_LINK(,
+ [
+ int variable = 1;
+ return (__sync_bool_compare_and_swap(&variable, 1, 2)
+ && __sync_add_and_fetch(&variable, 1)) ? 1 : 0;
+ ],
+ [
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(HAVE_BUILTIN_ATOMIC, 1, [Define to 1 if gcc supports __sync_bool_compare_and_swap() a.o.])
+ ],
+ [
+ AC_MSG_RESULT([no])
+ ])
+])
+
+AC_DEFUN([AC_FPM_LQ],
+[
+ have_lq=no
+
+ AC_MSG_CHECKING([for TCP_INFO])
+
+ AC_TRY_COMPILE([ #include <netinet/tcp.h> ], [struct tcp_info ti; int x = TCP_INFO;], [
+ have_lq=tcp_info
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+
+ if test "$have_lq" = "tcp_info"; then
+ AC_DEFINE([HAVE_LQ_TCP_INFO], 1, [do we have TCP_INFO?])
+ fi
+
+ if test "$have_lq" = "no" ; then
+ AC_MSG_CHECKING([for SO_LISTENQLEN])
+
+ AC_TRY_COMPILE([ #include <sys/socket.h> ], [int x = SO_LISTENQLIMIT; int y = SO_LISTENQLEN;], [
+ have_lq=so_listenq
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+
+ if test "$have_lq" = "tcp_info"; then
+ AC_DEFINE([HAVE_LQ_SO_LISTENQ], 1, [do we have SO_LISTENQxxx?])
+ fi
+ fi
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_SYSCONF],
+[
+ AC_MSG_CHECKING([for sysconf])
+
+ AC_TRY_COMPILE([ #include <unistd.h> ], [sysconf(_SC_CLK_TCK);], [
+ AC_DEFINE([HAVE_SYSCONF], 1, [do we have sysconf?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_TIMES],
+[
+ AC_MSG_CHECKING([for times])
+
+ AC_TRY_COMPILE([ #include <sys/times.h> ], [struct tms t; times(&t);], [
+ AC_DEFINE([HAVE_TIMES], 1, [do we have times?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_KQUEUE],
+[
+ AC_MSG_CHECKING([for kqueue])
+
+ AC_TRY_COMPILE(
+ [
+ #include <sys/types.h>
+ #include <sys/event.h>
+ #include <sys/time.h>
+ ], [
+ int kfd;
+ struct kevent k;
+ kfd = kqueue();
+ /* 0 -> STDIN_FILENO */
+ EV_SET(&k, 0, EVFILT_READ , EV_ADD | EV_CLEAR, 0, 0, NULL);
+ ], [
+ AC_DEFINE([HAVE_KQUEUE], 1, [do we have kqueue?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_PORT],
+[
+ AC_MSG_CHECKING([for port framework])
+
+ AC_TRY_COMPILE(
+ [
+ #include <port.h>
+ ], [
+ int port;
+
+ port = port_create();
+ if (port < 0) {
+ return 1;
+ }
+ ], [
+ AC_DEFINE([HAVE_PORT], 1, [do we have port framework?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_DEVPOLL],
+[
+ AC_MSG_CHECKING([for /dev/poll])
+
+ AC_TRY_COMPILE(
+ [
+ #include <stdio.h>
+ #include <sys/devpoll.h>
+ ], [
+ int n, dp;
+ struct dvpoll dvp;
+ dp = 0;
+ dvp.dp_fds = NULL;
+ dvp.dp_nfds = 0;
+ dvp.dp_timeout = 0;
+ n = ioctl(dp, DP_POLL, &dvp)
+ ], [
+ AC_DEFINE([HAVE_DEVPOLL], 1, [do we have /dev/poll?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_EPOLL],
+[
+ AC_MSG_CHECKING([for epoll])
+
+ AC_TRY_COMPILE(
+ [
+ #include <sys/epoll.h>
+ ], [
+ int epollfd;
+ struct epoll_event e;
+
+ epollfd = epoll_create(1);
+ if (epollfd < 0) {
+ return 1;
+ }
+
+ e.events = EPOLLIN | EPOLLET;
+ e.data.fd = 0;
+
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, 0, &e) == -1) {
+ return 1;
+ }
+
+ e.events = 0;
+ if (epoll_wait(epollfd, &e, 1, 1) < 0) {
+ return 1;
+ }
+ ], [
+ AC_DEFINE([HAVE_EPOLL], 1, [do we have epoll?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_POLL],
+[
+ AC_MSG_CHECKING([for poll])
+
+ AC_TRY_COMPILE(
+ [
+ #include <poll.h>
+ ], [
+ struct pollfd fds[2];
+
+ fds[0].fd = 0;
+ fds[0].events = POLLIN;
+
+ fds[1].fd = 0;
+ fds[1].events = POLLIN;
+
+ poll(fds, 2, 1);
+ ], [
+ AC_DEFINE([HAVE_POLL], 1, [do we have poll?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+AC_DEFUN([AC_FPM_SELECT],
+[
+ AC_MSG_CHECKING([for select])
+
+ AC_TRY_COMPILE(
+ [
+ /* According to POSIX.1-2001 */
+ #include <sys/select.h>
+
+ /* According to earlier standards */
+ #include <sys/time.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+ ], [
+ fd_set fds;
+ struct timeval t;
+ t.tv_sec = 0;
+ t.tv_usec = 42;
+ FD_ZERO(&fds);
+ /* 0 -> STDIN_FILENO */
+ FD_SET(0, &fds);
+ select(FD_SETSIZE, &fds, NULL, NULL, &t);
+ ], [
+ AC_DEFINE([HAVE_SELECT], 1, [do we have select?])
+ AC_MSG_RESULT([yes])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+])
+dnl }}}
+
+
+AC_MSG_CHECKING(for FPM build)
+if test "$PHP_FPM" != "no"; then
+ AC_MSG_RESULT($PHP_FPM)
+
+ AC_FPM_STDLIBS
+ AC_FPM_PRCTL
+ AC_FPM_CLOCK
+ AC_FPM_TRACE
+ AC_FPM_BUILTIN_ATOMIC
+ AC_FPM_LQ
+ AC_FPM_SYSCONF
+ AC_FPM_TIMES
+ AC_FPM_KQUEUE
+ AC_FPM_PORT
+ AC_FPM_DEVPOLL
+ AC_FPM_EPOLL
+ AC_FPM_POLL
+ AC_FPM_SELECT
+
+ PHP_ARG_WITH(fpm-user,,
+ [ --with-fpm-user[=USER] Set the user for php-fpm to run as. (default: nobody)], nobody, no)
+
+ PHP_ARG_WITH(fpm-group,,
+ [ --with-fpm-group[=GRP] Set the group for php-fpm to run as. For a system user, this
+ should usually be set to match the fpm username (default: nobody)], nobody, no)
+
+ if test -z "$PHP_FPM_USER" -o "$PHP_FPM_USER" = "yes" -o "$PHP_FPM_USER" = "no"; then
+ php_fpm_user="nobody"
+ else
+ php_fpm_user="$PHP_FPM_USER"
+ fi
+
+ if test -z "$PHP_FPM_GROUP" -o "$PHP_FPM_GROUP" = "yes" -o "$PHP_FPM_GROUP" = "no"; then
+ php_fpm_group="nobody"
+ else
+ php_fpm_group="$PHP_FPM_GROUP"
+ fi
+
+ PHP_SUBST_OLD(php_fpm_user)
+ PHP_SUBST_OLD(php_fpm_group)
+ php_fpm_sysconfdir=`eval echo $sysconfdir`
+ PHP_SUBST_OLD(php_fpm_sysconfdir)
+ php_fpm_localstatedir=`eval echo $localstatedir`
+ PHP_SUBST_OLD(php_fpm_localstatedir)
+ php_fpm_prefix=`eval echo $prefix`
+ PHP_SUBST_OLD(php_fpm_prefix)
+
+ AC_DEFINE_UNQUOTED(PHP_FPM_USER, "$php_fpm_user", [fpm user name])
+ AC_DEFINE_UNQUOTED(PHP_FPM_GROUP, "$php_fpm_group", [fpm group name])
+
+ AC_DEFINE_UNQUOTED(PHP_FPM_USER, "$php_fpm_user", [fpm user name])
+ AC_DEFINE_UNQUOTED(PHP_FPM_GROUP, "$php_fpm_group", [fpm group name])
+
+ PHP_ADD_BUILD_DIR(sapi/fpm/fpm)
+ PHP_ADD_BUILD_DIR(sapi/fpm/fpm/events)
+ PHP_OUTPUT(sapi/fpm/php-fpm.conf sapi/fpm/init.d.php-fpm sapi/fpm/php-fpm.service sapi/fpm/php-fpm.8 sapi/fpm/status.html)
+ PHP_ADD_MAKEFILE_FRAGMENT([$abs_srcdir/sapi/fpm/Makefile.frag])
+
+ SAPI_FPM_PATH=sapi/fpm/php-fpm
+
+
+ if test "$fpm_trace_type" && test -f "$abs_srcdir/sapi/fpm/fpm/fpm_trace_$fpm_trace_type.c"; then
+ PHP_FPM_TRACE_FILES="fpm/fpm_trace.c fpm/fpm_trace_$fpm_trace_type.c"
+ fi
+
+ PHP_FPM_CFLAGS="-I$abs_srcdir/sapi/fpm"
+
+ PHP_FPM_FILES="fpm/fastcgi.c \
+ fpm/fpm.c \
+ fpm/fpm_children.c \
+ fpm/fpm_cleanup.c \
+ fpm/fpm_clock.c \
+ fpm/fpm_conf.c \
+ fpm/fpm_env.c \
+ fpm/fpm_events.c \
+ fpm/fpm_log.c \
+ fpm/fpm_main.c \
+ fpm/fpm_php.c \
+ fpm/fpm_php_trace.c \
+ fpm/fpm_process_ctl.c \
+ fpm/fpm_request.c \
+ fpm/fpm_shm.c \
+ fpm/fpm_scoreboard.c \
+ fpm/fpm_signals.c \
+ fpm/fpm_sockets.c \
+ fpm/fpm_status.c \
+ fpm/fpm_stdio.c \
+ fpm/fpm_unix.c \
+ fpm/fpm_worker_pool.c \
+ fpm/zlog.c \
+ fpm/events/select.c \
+ fpm/events/poll.c \
+ fpm/events/epoll.c \
+ fpm/events/kqueue.c \
+ fpm/events/devpoll.c \
+ fpm/events/port.c \
+ "
+
+ PHP_SELECT_SAPI(fpm, program, $PHP_FPM_FILES $PHP_FPM_TRACE_FILES, $PHP_FPM_CFLAGS, '$(SAPI_FPM_PATH)')
+
+ case $host_alias in
+ *aix*)
+ BUILD_FPM="echo '\#! .' > php.sym && echo >>php.sym && nm -BCpg \`echo \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FPM_OBJS) | sed 's/\([A-Za-z0-9_]*\)\.lo/\1.o/g'\` | \$(AWK) '{ if (((\$\$2 == \"T\") || (\$\$2 == \"D\") || (\$\$2 == \"B\")) && (substr(\$\$3,1,1) != \".\")) { print \$\$3 } }' | sort -u >> php.sym && \$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) -Wl,-brtl -Wl,-bE:php.sym \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)"
+ ;;
+ *darwin*)
+ BUILD_FPM="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_FPM_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)"
+ ;;
+ *)
+ BUILD_FPM="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_FPM_OBJS) \$(EXTRA_LIBS) \$(FPM_EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_FPM_PATH)"
+ ;;
+ esac
+
+ PHP_SUBST(SAPI_FPM_PATH)
+ PHP_SUBST(BUILD_FPM)
+
+else
+ AC_MSG_RESULT(no)
+fi
diff --git a/sapi/fpm/fpm/events/devpoll.c b/sapi/fpm/fpm/events/devpoll.c
new file mode 100644
index 0000000..223d072
--- /dev/null
+++ b/sapi/fpm/fpm/events/devpoll.c
@@ -0,0 +1,248 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_DEVPOLL
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/devpoll.h>
+#include <errno.h>
+
+static int fpm_event_devpoll_init(int max);
+static int fpm_event_devpoll_clean();
+static int fpm_event_devpoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
+static int fpm_event_devpoll_add(struct fpm_event_s *ev);
+static int fpm_event_devpoll_remove(struct fpm_event_s *ev);
+
+static struct fpm_event_module_s devpoll_module = {
+ .name = "/dev/poll",
+ .support_edge_trigger = 0,
+ .init = fpm_event_devpoll_init,
+ .clean = fpm_event_devpoll_clean,
+ .wait = fpm_event_devpoll_wait,
+ .add = fpm_event_devpoll_add,
+ .remove = fpm_event_devpoll_remove,
+};
+
+int dpfd = -1;
+static struct pollfd *pollfds = NULL;
+static struct pollfd *active_pollfds = NULL;
+static int npollfds = 0;
+
+#endif /* HAVE_DEVPOLL */
+
+struct fpm_event_module_s *fpm_event_devpoll_module() /* {{{ */
+{
+#if HAVE_DEVPOLL
+ return &devpoll_module;
+#else
+ return NULL;
+#endif /* HAVE_DEVPOLL */
+}
+/* }}} */
+
+#if HAVE_DEVPOLL
+
+/*
+ * Init module
+ */
+static int fpm_event_devpoll_init(int max) /* {{{ */
+{
+ int i;
+
+ /* open /dev/poll for future usages */
+ dpfd = open("/dev/poll", O_RDWR);
+ if (dpfd < 0) {
+ zlog(ZLOG_ERROR, "Unable to open /dev/poll");
+ return -1;
+ }
+
+ if (max < 1) {
+ return 0;
+ }
+
+ /* alloc and clear pollfds */
+ pollfds = malloc(sizeof(struct pollfd) * max);
+ if (!pollfds) {
+ zlog(ZLOG_ERROR, "poll: unable to allocate %d events", max);
+ return -1;
+ }
+ memset(pollfds, 0, sizeof(struct pollfd) * max);
+
+ /* set all fd to -1 in order to ensure it's not set */
+ for (i = 0; i < max; i++) {
+ pollfds[i].fd = -1;
+ }
+
+ /* alloc and clear active_pollfds */
+ active_pollfds = malloc(sizeof(struct pollfd) * max);
+ if (!active_pollfds) {
+ free(pollfds);
+ zlog(ZLOG_ERROR, "poll: unable to allocate %d events", max);
+ return -1;
+ }
+ memset(active_pollfds, 0, sizeof(struct pollfd) * max);
+
+ /* save max */
+ npollfds = max;
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * Clean the module
+ */
+static int fpm_event_devpoll_clean() /* {{{ */
+{
+ /* close /dev/poll if open */
+ if (dpfd > -1) {
+ close(dpfd);
+ dpfd = -1;
+ }
+
+ /* free pollfds */
+ if (pollfds) {
+ free(pollfds);
+ pollfds = NULL;
+ }
+
+ /* free active_pollfds */
+ if (active_pollfds) {
+ free(active_pollfds);
+ active_pollfds = NULL;
+ }
+
+ npollfds = 0;
+ return 0;
+}
+/* }}} */
+
+/*
+ * wait for events or timeout
+ */
+static int fpm_event_devpoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
+{
+ int ret, i;
+ struct fpm_event_queue_s *q;
+ struct dvpoll dopoll;
+
+ /* setup /dev/poll */
+ dopoll.dp_fds = active_pollfds;
+ dopoll.dp_nfds = npollfds;
+ dopoll.dp_timeout = (int)timeout;
+
+ /* wait for inconming event or timeout */
+ ret = ioctl(dpfd, DP_POLL, &dopoll);
+
+ if (ret < 0) {
+
+ /* trigger error unless signal interrupt */
+ if (errno != EINTR) {
+ zlog(ZLOG_WARNING, "/dev/poll: ioctl() returns %d", errno);
+ return -1;
+ }
+ }
+
+ /* iterate throught triggered events */
+ for (i = 0; i < ret; i++) {
+
+ /* find the corresponding event */
+ q = queue;
+ while (q) {
+
+ /* found */
+ if (q->ev && q->ev->fd == active_pollfds[i].fd) {
+
+ /* fire the event */
+ fpm_event_fire(q->ev);
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return -2;
+ }
+ break; /* next triggered event */
+ }
+ q = q->next; /* iterate */
+ }
+ }
+
+ return ret;
+}
+/* }}} */
+
+/*
+ * Add a FD from the fd set
+ */
+static int fpm_event_devpoll_add(struct fpm_event_s *ev) /* {{{ */
+{
+ struct pollfd pollfd;
+
+ /* fill pollfd with event informations */
+ pollfd.fd = ev->fd;
+ pollfd.events = POLLIN;
+ pollfd.revents = 0;
+
+ /* add the event to the internal queue */
+ if (write(dpfd, &pollfd, sizeof(struct pollfd)) != sizeof(struct pollfd)) {
+ zlog(ZLOG_ERROR, "/dev/poll: Unable to add the event in the internal queue");
+ return -1;
+ }
+
+ /* mark the event as registered */
+ ev->index = ev->fd;
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * Remove a FD from the fd set
+ */
+static int fpm_event_devpoll_remove(struct fpm_event_s *ev) /* {{{ */
+{
+ struct pollfd pollfd;
+
+ /* fill pollfd with the same informations as fpm_event_devpoll_add */
+ pollfd.fd = ev->fd;
+ pollfd.events = POLLIN | POLLREMOVE;
+ pollfd.revents = 0;
+
+ /* add the event to the internal queue */
+ if (write(dpfd, &pollfd, sizeof(struct pollfd)) != sizeof(struct pollfd)) {
+ zlog(ZLOG_ERROR, "/dev/poll: Unable to remove the event in the internal queue");
+ return -1;
+ }
+
+ /* mark the event as registered */
+ ev->index = -1;
+
+ return 0;
+}
+/* }}} */
+
+#endif /* HAVE_DEVPOLL */
diff --git a/sapi/fpm/fpm/events/devpoll.h b/sapi/fpm/fpm/events/devpoll.h
new file mode 100644
index 0000000..f9bc4af
--- /dev/null
+++ b/sapi/fpm/fpm/events/devpoll.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef FPM_EVENTS_DEVPOLL_H
+#define FPM_EVENTS_DEVPOLL_H
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+
+struct fpm_event_module_s *fpm_event_devpoll_module();
+
+#endif /* FPM_EVENTS_DEVPOLL_H */
diff --git a/sapi/fpm/fpm/events/epoll.c b/sapi/fpm/fpm/events/epoll.c
new file mode 100644
index 0000000..c9c7f1f
--- /dev/null
+++ b/sapi/fpm/fpm/events/epoll.c
@@ -0,0 +1,211 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_EPOLL
+
+#include <sys/epoll.h>
+#include <errno.h>
+
+static int fpm_event_epoll_init(int max);
+static int fpm_event_epoll_clean();
+static int fpm_event_epoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
+static int fpm_event_epoll_add(struct fpm_event_s *ev);
+static int fpm_event_epoll_remove(struct fpm_event_s *ev);
+
+static struct fpm_event_module_s epoll_module = {
+ .name = "epoll",
+ .support_edge_trigger = 1,
+ .init = fpm_event_epoll_init,
+ .clean = fpm_event_epoll_clean,
+ .wait = fpm_event_epoll_wait,
+ .add = fpm_event_epoll_add,
+ .remove = fpm_event_epoll_remove,
+};
+
+static struct epoll_event *epollfds = NULL;
+static int nepollfds = 0;
+static int epollfd = 0;
+
+#endif /* HAVE_EPOLL */
+
+struct fpm_event_module_s *fpm_event_epoll_module() /* {{{ */
+{
+#if HAVE_EPOLL
+ return &epoll_module;
+#else
+ return NULL;
+#endif /* HAVE_EPOLL */
+}
+/* }}} */
+
+#if HAVE_EPOLL
+
+/*
+ * Init the module
+ */
+static int fpm_event_epoll_init(int max) /* {{{ */
+{
+ if (max < 1) {
+ return 0;
+ }
+
+ /* init epoll */
+ epollfd = epoll_create(max + 1);
+ if (epollfd < 0) {
+ zlog(ZLOG_ERROR, "epoll: unable to initialize");
+ return -1;
+ }
+
+ /* allocate fds */
+ epollfds = malloc(sizeof(struct epoll_event) * max);
+ if (!epollfds) {
+ zlog(ZLOG_ERROR, "epoll: unable to allocate %d events", max);
+ return -1;
+ }
+ memset(epollfds, 0, sizeof(struct epoll_event) * max);
+
+ /* save max */
+ nepollfds = max;
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * Clean the module
+ */
+static int fpm_event_epoll_clean() /* {{{ */
+{
+ /* free epollfds */
+ if (epollfds) {
+ free(epollfds);
+ epollfds = NULL;
+ }
+
+ nepollfds = 0;
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * wait for events or timeout
+ */
+static int fpm_event_epoll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
+{
+ int ret, i;
+
+ /* ensure we have a clean epoolfds before calling epoll_wait() */
+ memset(epollfds, 0, sizeof(struct epoll_event) * nepollfds);
+
+ /* wait for inconming event or timeout */
+ ret = epoll_wait(epollfd, epollfds, nepollfds, timeout);
+ if (ret == -1) {
+
+ /* trigger error unless signal interrupt */
+ if (errno != EINTR) {
+ zlog(ZLOG_WARNING, "epoll_wait() returns %d", errno);
+ return -1;
+ }
+ }
+
+ /* events have been triggered, let's fire them */
+ for (i = 0; i < ret; i++) {
+
+ /* do we have a valid ev ptr ? */
+ if (!epollfds[i].data.ptr) {
+ continue;
+ }
+
+ /* fire the event */
+ fpm_event_fire((struct fpm_event_s *)epollfds[i].data.ptr);
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return -2;
+ }
+ }
+
+ return ret;
+}
+/* }}} */
+
+/*
+ * Add a FD to the fd set
+ */
+static int fpm_event_epoll_add(struct fpm_event_s *ev) /* {{{ */
+{
+ struct epoll_event e;
+
+ /* fill epoll struct */
+ e.events = EPOLLIN;
+ e.data.fd = ev->fd;
+ e.data.ptr = (void *)ev;
+
+ if (ev->flags & FPM_EV_EDGE) {
+ e.events = e.events | EPOLLET;
+ }
+
+ /* add the event to epoll internal queue */
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ev->fd, &e) == -1) {
+ zlog(ZLOG_ERROR, "epoll: unable to add fd %d", ev->fd);
+ return -1;
+ }
+
+ /* mark the event as registered */
+ ev->index = ev->fd;
+ return 0;
+}
+/* }}} */
+
+/*
+ * Remove a FD from the fd set
+ */
+static int fpm_event_epoll_remove(struct fpm_event_s *ev) /* {{{ */
+{
+ struct epoll_event e;
+
+ /* fill epoll struct the same way we did in fpm_event_epoll_add() */
+ e.events = EPOLLIN;
+ e.data.fd = ev->fd;
+ e.data.ptr = (void *)ev;
+
+ if (ev->flags & FPM_EV_EDGE) {
+ e.events = e.events | EPOLLET;
+ }
+
+ /* remove the event from epoll internal queue */
+ if (epoll_ctl(epollfd, EPOLL_CTL_DEL, ev->fd, &e) == -1) {
+ zlog(ZLOG_ERROR, "epoll: unable to remove fd %d", ev->fd);
+ return -1;
+ }
+
+ /* mark the event as not registered */
+ ev->index = -1;
+ return 0;
+}
+/* }}} */
+
+#endif /* HAVE_EPOLL */
diff --git a/sapi/fpm/fpm/events/epoll.h b/sapi/fpm/fpm/events/epoll.h
new file mode 100644
index 0000000..abc5a21
--- /dev/null
+++ b/sapi/fpm/fpm/events/epoll.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef FPM_EVENTS_EPOLL_H
+#define FPM_EVENTS_EPOLL_H
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+
+struct fpm_event_module_s *fpm_event_epoll_module();
+
+#endif /* FPM_EVENTS_EPOLL_H */
diff --git a/sapi/fpm/fpm/events/kqueue.c b/sapi/fpm/fpm/events/kqueue.c
new file mode 100644
index 0000000..7ce0760
--- /dev/null
+++ b/sapi/fpm/fpm/events/kqueue.c
@@ -0,0 +1,208 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_KQUEUE
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+
+#include <errno.h>
+
+static int fpm_event_kqueue_init(int max);
+static int fpm_event_kqueue_clean();
+static int fpm_event_kqueue_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
+static int fpm_event_kqueue_add(struct fpm_event_s *ev);
+static int fpm_event_kqueue_remove(struct fpm_event_s *ev);
+
+static struct fpm_event_module_s kqueue_module = {
+ .name = "kqueue",
+ .support_edge_trigger = 1,
+ .init = fpm_event_kqueue_init,
+ .clean = fpm_event_kqueue_clean,
+ .wait = fpm_event_kqueue_wait,
+ .add = fpm_event_kqueue_add,
+ .remove = fpm_event_kqueue_remove,
+};
+
+static struct kevent *kevents = NULL;
+static int nkevents = 0;
+static int kfd = 0;
+
+#endif /* HAVE_KQUEUE */
+
+/*
+ * Return the module configuration
+ */
+struct fpm_event_module_s *fpm_event_kqueue_module() /* {{{ */
+{
+#if HAVE_KQUEUE
+ return &kqueue_module;
+#else
+ return NULL;
+#endif /* HAVE_KQUEUE */
+}
+/* }}} */
+
+#if HAVE_KQUEUE
+
+/*
+ * init kqueue and stuff
+ */
+static int fpm_event_kqueue_init(int max) /* {{{ */
+{
+ if (max < 1) {
+ return 0;
+ }
+
+ kfd = kqueue();
+ if (kfd < 0) {
+ zlog(ZLOG_ERROR, "kqueue: unable to initialize");
+ return -1;
+ }
+
+ kevents = malloc(sizeof(struct kevent) * max);
+ if (!kevents) {
+ zlog(ZLOG_ERROR, "epoll: unable to allocate %d events", max);
+ return -1;
+ }
+
+ memset(kevents, 0, sizeof(struct kevent) * max);
+
+ nkevents = max;
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * release kqueue stuff
+ */
+static int fpm_event_kqueue_clean() /* {{{ */
+{
+ if (kevents) {
+ free(kevents);
+ kevents = NULL;
+ }
+
+ nkevents = 0;
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * wait for events or timeout
+ */
+static int fpm_event_kqueue_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
+{
+ struct timespec t;
+ int ret, i;
+
+ /* ensure we have a clean kevents before calling kevent() */
+ memset(kevents, 0, sizeof(struct kevent) * nkevents);
+
+ /* convert ms to timespec struct */
+ t.tv_sec = timeout / 1000;
+ t.tv_nsec = (timeout % 1000) * 1000 * 1000;
+
+ /* wait for incoming event or timeout */
+ ret = kevent(kfd, NULL, 0, kevents, nkevents, &t);
+ if (ret == -1) {
+
+ /* trigger error unless signal interrupt */
+ if (errno != EINTR) {
+ zlog(ZLOG_WARNING, "epoll_wait() returns %d", errno);
+ return -1;
+ }
+ }
+
+ /* fire triggered events */
+ for (i = 0; i < ret; i++) {
+ if (kevents[i].udata) {
+ struct fpm_event_s *ev = (struct fpm_event_s *)kevents[i].udata;
+ fpm_event_fire(ev);
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return -2;
+ }
+ }
+ }
+
+ return ret;
+}
+/* }}} */
+
+/*
+ * Add a FD to to kevent queue
+ */
+static int fpm_event_kqueue_add(struct fpm_event_s *ev) /* {{{ */
+{
+ struct kevent k;
+ int flags = EV_ADD;
+
+ if (ev->flags & FPM_EV_EDGE) {
+ flags = flags | EV_CLEAR;
+ }
+
+ EV_SET(&k, ev->fd, EVFILT_READ, flags, 0, 0, (void *)ev);
+
+ if (kevent(kfd, &k, 1, NULL, 0, NULL) < 0) {
+ zlog(ZLOG_ERROR, "kevent: unable to add event");
+ return -1;
+ }
+
+ /* mark the event as registered */
+ ev->index = ev->fd;
+ return 0;
+}
+/* }}} */
+
+/*
+ * Remove a FD from the kevent queue
+ */
+static int fpm_event_kqueue_remove(struct fpm_event_s *ev) /* {{{ */
+{
+ struct kevent k;
+ int flags = EV_DELETE;
+
+ if (ev->flags & FPM_EV_EDGE) {
+ flags = flags | EV_CLEAR;
+ }
+
+ EV_SET(&k, ev->fd, EVFILT_READ, flags, 0, 0, (void *)ev);
+
+ if (kevent(kfd, &k, 1, NULL, 0, NULL) < 0) {
+ zlog(ZLOG_ERROR, "kevent: unable to add event");
+ return -1;
+ }
+
+ /* mark the vent as not registered */
+ ev->index = -1;
+ return 0;
+}
+/* }}} */
+
+#endif /* HAVE_KQUEUE */
diff --git a/sapi/fpm/fpm/events/kqueue.h b/sapi/fpm/fpm/events/kqueue.h
new file mode 100644
index 0000000..2642aca
--- /dev/null
+++ b/sapi/fpm/fpm/events/kqueue.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef FPM_EVENTS_KQUEUE_H
+#define FPM_EVENTS_KQUEUE_H
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+
+struct fpm_event_module_s *fpm_event_kqueue_module();
+
+#endif /* FPM_EVENTS_KQUEUE_H */
diff --git a/sapi/fpm/fpm/events/poll.c b/sapi/fpm/fpm/events/poll.c
new file mode 100644
index 0000000..185eceb
--- /dev/null
+++ b/sapi/fpm/fpm/events/poll.c
@@ -0,0 +1,276 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_POLL
+
+#include <poll.h>
+#include <errno.h>
+#include <string.h>
+
+static int fpm_event_poll_init(int max);
+static int fpm_event_poll_clean();
+static int fpm_event_poll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
+static int fpm_event_poll_add(struct fpm_event_s *ev);
+static int fpm_event_poll_remove(struct fpm_event_s *ev);
+
+static struct fpm_event_module_s poll_module = {
+ .name = "poll",
+ .support_edge_trigger = 0,
+ .init = fpm_event_poll_init,
+ .clean = fpm_event_poll_clean,
+ .wait = fpm_event_poll_wait,
+ .add = fpm_event_poll_add,
+ .remove = fpm_event_poll_remove,
+};
+
+static struct pollfd *pollfds = NULL;
+static struct pollfd *active_pollfds = NULL;
+static int npollfds = 0;
+static int next_free_slot = 0;
+#endif /* HAVE_POLL */
+
+/*
+ * return the module configuration
+ */
+struct fpm_event_module_s *fpm_event_poll_module() /* {{{ */
+{
+#if HAVE_POLL
+ return &poll_module;
+#else
+ return NULL;
+#endif /* HAVE_POLL */
+}
+/* }}} */
+
+#if HAVE_POLL
+
+/*
+ * Init the module
+ */
+static int fpm_event_poll_init(int max) /* {{{ */
+{
+ int i;
+
+ if (max < 1) {
+ return 0;
+ }
+
+ /* alloc and clear pollfds */
+ pollfds = malloc(sizeof(struct pollfd) * max);
+ if (!pollfds) {
+ zlog(ZLOG_ERROR, "poll: unable to allocate %d events", max);
+ return -1;
+ }
+ memset(pollfds, 0, sizeof(struct pollfd) * max);
+
+ /* set all fd to -1 in order to ensure it's not set */
+ for (i = 0; i < max; i++) {
+ pollfds[i].fd = -1;
+ }
+
+ /* alloc and clear active_pollfds */
+ active_pollfds = malloc(sizeof(struct pollfd) * max);
+ if (!active_pollfds) {
+ free(pollfds);
+ zlog(ZLOG_ERROR, "poll: unable to allocate %d events", max);
+ return -1;
+ }
+ memset(active_pollfds, 0, sizeof(struct pollfd) * max);
+
+ /* save max */
+ npollfds = max;
+ return 0;
+}
+/* }}} */
+
+/*
+ * Clean the module
+ */
+static int fpm_event_poll_clean() /* {{{ */
+{
+ /* free pollfds */
+ if (pollfds) {
+ free(pollfds);
+ pollfds = NULL;
+ }
+
+ /* free active_pollfds */
+ if (active_pollfds) {
+ free(active_pollfds);
+ active_pollfds = NULL;
+ }
+
+ npollfds = 0;
+ return 0;
+}
+/* }}} */
+
+/*
+ * wait for events or timeout
+ */
+static int fpm_event_poll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
+{
+ int ret;
+ struct fpm_event_queue_s *q;
+
+ if (npollfds > 0) {
+ /* copy pollfds because poll() alters it */
+ memcpy(active_pollfds, pollfds, sizeof(struct pollfd) * npollfds);
+ }
+
+ /* wait for inconming event or timeout */
+ ret = poll(active_pollfds, npollfds, timeout);
+ if (ret == -1) {
+
+ /* trigger error unless signal interrupt */
+ if (errno != EINTR) {
+ zlog(ZLOG_WARNING, "poll() returns %d", errno);
+ return -1;
+ }
+ }
+
+ /* events have been triggered */
+ if (ret > 0) {
+
+ /* trigger POLLIN events */
+ q = queue;
+ while (q) {
+ /* ensure ev->index is valid */
+ if (q->ev && q->ev->index >= 0 && q->ev->index < npollfds && q->ev->fd == active_pollfds[q->ev->index].fd) {
+
+ /* has the event has been triggered ? */
+ if (active_pollfds[q->ev->index].revents & POLLIN) {
+
+ /* fire the event */
+ fpm_event_fire(q->ev);
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return -2;
+ }
+ }
+ }
+ q = q->next; /* iterate */
+ }
+ }
+
+ return ret;
+}
+/* }}} */
+
+/*
+ * Add a FD to the fd set
+ */
+static int fpm_event_poll_add(struct fpm_event_s *ev) /* {{{ */
+{
+ int i;
+
+ /* do we have a direct free slot */
+ if (pollfds[next_free_slot].fd == -1) {
+ /* register the event */
+ pollfds[next_free_slot].fd = ev->fd;
+ pollfds[next_free_slot].events = POLLIN;
+
+ /* remember the event place in the fd list and suppose next slot is free */
+ ev->index = next_free_slot++;
+ if (next_free_slot >= npollfds) {
+ next_free_slot = 0;
+ }
+ return 0;
+ }
+
+ /* let's search */
+ for (i = 0; i < npollfds; i++) {
+ if (pollfds[i].fd != -1) {
+ /* not free */
+ continue;
+ }
+
+ /* register the event */
+ pollfds[i].fd = ev->fd;
+ pollfds[i].events = POLLIN;
+
+ /* remember the event place in the fd list and suppose next slot is free */
+ ev->index = next_free_slot++;
+ if (next_free_slot >= npollfds) {
+ next_free_slot = 0;
+ }
+ return 0;
+ }
+
+ zlog(ZLOG_ERROR, "poll: not enought space to add event (fd=%d)", ev->fd);
+ return -1;
+}
+/* }}} */
+
+/*
+ * Remove a FD from the fd set
+ */
+static int fpm_event_poll_remove(struct fpm_event_s *ev) /* {{{ */
+{
+ int i;
+
+ /* do we have a direct access */
+ if (ev->index >= 0 && ev->index < npollfds && pollfds[ev->index].fd == ev->fd) {
+ /* remember this slot as free */
+ next_free_slot = ev->index;
+
+ /* clear event in pollfds */
+ pollfds[ev->index].fd = -1;
+ pollfds[ev->index].events = 0;
+
+ /* mark the event as not registered */
+ ev->index = -1;
+
+ return 0;
+ }
+
+ /* let's search */
+ for (i = 0; i < npollfds; i++) {
+
+ if (pollfds[i].fd != ev->fd) {
+ /* not found */
+ continue;
+ }
+
+ /* remember this slot as free */
+ next_free_slot = i;
+
+ /* clear event in pollfds */
+ pollfds[i].fd = -1;
+ pollfds[i].events = 0;
+
+ /* mark the event as not registered */
+ ev->index = -1;
+
+ return 0;
+ }
+
+ zlog(ZLOG_ERROR, "poll: unable to remove event: not found (fd=%d, index=%d)", ev->fd, ev->index);
+ return -1;
+}
+/* }}} */
+
+#endif /* HAVE_POLL */
diff --git a/sapi/fpm/fpm/events/poll.h b/sapi/fpm/fpm/events/poll.h
new file mode 100644
index 0000000..d553192
--- /dev/null
+++ b/sapi/fpm/fpm/events/poll.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef FPM_EVENTS_POLL_H
+#define FPM_EVENTS_POLL_H
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+
+struct fpm_event_module_s *fpm_event_poll_module();
+
+#endif /* FPM_EVENTS_POLL_H */
diff --git a/sapi/fpm/fpm/events/port.c b/sapi/fpm/fpm/events/port.c
new file mode 100644
index 0000000..3cbf092
--- /dev/null
+++ b/sapi/fpm/fpm/events/port.c
@@ -0,0 +1,185 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_PORT
+
+#include <port.h>
+#include <poll.h>
+#include <errno.h>
+
+static int fpm_event_port_init(int max);
+static int fpm_event_port_clean();
+static int fpm_event_port_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
+static int fpm_event_port_add(struct fpm_event_s *ev);
+static int fpm_event_port_remove(struct fpm_event_s *ev);
+
+static struct fpm_event_module_s port_module = {
+ .name = "port",
+ .support_edge_trigger = 0,
+ .init = fpm_event_port_init,
+ .clean = fpm_event_port_clean,
+ .wait = fpm_event_port_wait,
+ .add = fpm_event_port_add,
+ .remove = fpm_event_port_remove,
+};
+
+port_event_t *events = NULL;
+int nevents = 0;
+static int pfd = -1;
+
+#endif /* HAVE_PORT */
+
+struct fpm_event_module_s *fpm_event_port_module() /* {{{ */
+{
+#if HAVE_PORT
+ return &port_module;
+#else
+ return NULL;
+#endif /* HAVE_PORT */
+}
+/* }}} */
+
+#if HAVE_PORT
+
+/*
+ * Init the module
+ */
+static int fpm_event_port_init(int max) /* {{{ */
+{
+ /* open port */
+ pfd = port_create();
+ if (pfd < 0) {
+ zlog(ZLOG_ERROR, "port: unable to initialize port_create()");
+ return -1;
+ }
+
+ if (max < 1) {
+ return 0;
+ }
+
+ /* alloc and clear active_pollfds */
+ events = malloc(sizeof(port_event_t) * max);
+ if (!events) {
+ zlog(ZLOG_ERROR, "port: Unable to allocate %d events", max);
+ return -1;
+ }
+
+ nevents = max;
+ return 0;
+}
+/* }}} */
+
+/*
+ * Clean the module
+ */
+static int fpm_event_port_clean() /* {{{ */
+{
+ if (pfd > -1) {
+ close(pfd);
+ pfd = -1;
+ }
+
+ if (events) {
+ free(events);
+ events = NULL;
+ }
+
+ nevents = 0;
+ return 0;
+}
+/* }}} */
+
+/*
+ * wait for events or timeout
+ */
+static int fpm_event_port_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
+{
+ int ret, i, nget;
+ timespec_t t;
+
+ /* convert timeout into timespec_t */
+ t.tv_sec = (int)(timeout / 1000);
+ t.tv_nsec = (timeout % 1000) * 1000 * 1000;
+
+ /* wait for inconming event or timeout. We want at least one event or timeout */
+ nget = 1;
+ ret = port_getn(pfd, events, nevents, &nget, &t);
+ if (ret < 0) {
+
+ /* trigger error unless signal interrupt or timeout */
+ if (errno != EINTR && errno != ETIME) {
+ zlog(ZLOG_WARNING, "poll() returns %d", errno);
+ return -1;
+ }
+ }
+
+ for (i = 0; i < nget; i++) {
+
+ /* do we have a ptr to the event ? */
+ if (!events[i].portev_user) {
+ continue;
+ }
+
+ /* fire the event */
+ fpm_event_fire((struct fpm_event_s *)events[i].portev_user);
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return -2;
+ }
+ }
+ return nget;
+}
+/* }}} */
+
+/*
+ * Add a FD to the fd set
+ */
+static int fpm_event_port_add(struct fpm_event_s *ev) /* {{{ */
+{
+ /* add the event to port */
+ if (port_associate(pfd, PORT_SOURCE_FD, ev->fd, POLLIN, (void *)ev) < 0) {
+ zlog(ZLOG_ERROR, "port: unable to add the event");
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+/*
+ * Remove a FD from the fd set
+ */
+static int fpm_event_port_remove(struct fpm_event_s *ev) /* {{{ */
+{
+ /* remove the event from port */
+ if (port_dissociate(pfd, PORT_SOURCE_FD, ev->fd) < 0) {
+ zlog(ZLOG_ERROR, "port: unable to add the event");
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+#endif /* HAVE_PORT */
diff --git a/sapi/fpm/fpm/events/port.h b/sapi/fpm/fpm/events/port.h
new file mode 100644
index 0000000..666a157
--- /dev/null
+++ b/sapi/fpm/fpm/events/port.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef FPM_EVENTS_PORT_H
+#define FPM_EVENTS_PORT_H
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+
+struct fpm_event_module_s *fpm_event_port_module();
+
+#endif /* FPM_EVENTS_PORT_H */
diff --git a/sapi/fpm/fpm/events/select.c b/sapi/fpm/fpm/events/select.c
new file mode 100644
index 0000000..e3af067
--- /dev/null
+++ b/sapi/fpm/fpm/events/select.c
@@ -0,0 +1,175 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+#include "../fpm.h"
+#include "../zlog.h"
+
+#if HAVE_SELECT
+
+/* According to POSIX.1-2001 */
+#include <sys/select.h>
+
+/* According to earlier standards */
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <errno.h>
+
+static int fpm_event_select_init(int max);
+static int fpm_event_select_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
+static int fpm_event_select_add(struct fpm_event_s *ev);
+static int fpm_event_select_remove(struct fpm_event_s *ev);
+
+static struct fpm_event_module_s select_module = {
+ .name = "select",
+ .support_edge_trigger = 0,
+ .init = fpm_event_select_init,
+ .clean = NULL,
+ .wait = fpm_event_select_wait,
+ .add = fpm_event_select_add,
+ .remove = fpm_event_select_remove,
+};
+
+static fd_set fds;
+
+#endif /* HAVE_SELECT */
+
+/*
+ * return the module configuration
+ */
+struct fpm_event_module_s *fpm_event_select_module() /* {{{ */
+{
+#if HAVE_SELECT
+ return &select_module;
+#else
+ return NULL;
+#endif /* HAVE_SELECT */
+}
+/* }}} */
+
+#if HAVE_SELECT
+
+/*
+ * Init the module
+ */
+static int fpm_event_select_init(int max) /* {{{ */
+{
+ FD_ZERO(&fds);
+ return 0;
+}
+/* }}} */
+
+
+/*
+ * wait for events or timeout
+ */
+static int fpm_event_select_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
+{
+ int ret;
+ struct fpm_event_queue_s *q;
+ fd_set current_fds;
+ struct timeval t;
+
+ /* copy fds because select() alters it */
+ current_fds = fds;
+
+ /* fill struct timeval with timeout */
+ t.tv_sec = timeout / 1000;
+ t.tv_usec = (timeout % 1000) * 1000;
+
+ /* wait for inconming event or timeout */
+ ret = select(FD_SETSIZE, &current_fds, NULL, NULL, &t);
+ if (ret == -1) {
+
+ /* trigger error unless signal interrupt */
+ if (errno != EINTR) {
+ zlog(ZLOG_WARNING, "poll() returns %d", errno);
+ return -1;
+ }
+ }
+
+ /* events have been triggered */
+ if (ret > 0) {
+
+ /* trigger POLLIN events */
+ q = queue;
+ while (q) {
+ if (q->ev) { /* sanity check */
+
+ /* check if the event has been triggered */
+ if (FD_ISSET(q->ev->fd, &current_fds)) {
+
+ /* fire the event */
+ fpm_event_fire(q->ev);
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return -2;
+ }
+ }
+ }
+ q = q->next; /* iterate */
+ }
+ }
+ return ret;
+
+}
+/* }}} */
+
+/*
+ * Add a FD to the fd set
+ */
+static int fpm_event_select_add(struct fpm_event_s *ev) /* {{{ */
+{
+ /* check size limitation */
+ if (ev->fd >= FD_SETSIZE) {
+ zlog(ZLOG_ERROR, "select: not enough space in the select fd list (max = %d). Please consider using another event mechanism.", FD_SETSIZE);
+ return -1;
+ }
+
+ /* add the FD if not already in */
+ if (!FD_ISSET(ev->fd, &fds)) {
+ FD_SET(ev->fd, &fds);
+ ev->index = ev->fd;
+ }
+
+ return 0;
+}
+/* }}} */
+
+/*
+ * Remove a FD from the fd set
+ */
+static int fpm_event_select_remove(struct fpm_event_s *ev) /* {{{ */
+{
+ /* remove the fd if it's in */
+ if (FD_ISSET(ev->fd, &fds)) {
+ FD_CLR(ev->fd, &fds);
+ ev->index = -1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+#endif /* HAVE_SELECT */
diff --git a/sapi/fpm/fpm/events/select.h b/sapi/fpm/fpm/events/select.h
new file mode 100644
index 0000000..db52503
--- /dev/null
+++ b/sapi/fpm/fpm/events/select.h
@@ -0,0 +1,29 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Jerome Loyet <jerome@loyet.net> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef FPM_EVENTS_SELECT_H
+#define FPM_EVENTS_SELECT_H
+
+#include "../fpm_config.h"
+#include "../fpm_events.h"
+
+struct fpm_event_module_s *fpm_event_select_module();
+
+#endif /* FPM_EVENTS_SELECT_H */
diff --git a/sapi/fpm/fpm/fastcgi.c b/sapi/fpm/fpm/fastcgi.c
new file mode 100644
index 0000000..cf3f098
--- /dev/null
+++ b/sapi/fpm/fpm/fastcgi.c
@@ -0,0 +1,1111 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: fastcgi.c 287777 2009-08-26 19:17:32Z pajoye $ */
+
+#include "php.h"
+#include "fastcgi.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <php_config.h>
+#include "fpm.h"
+#include "fpm_request.h"
+#include "zlog.h"
+
+#ifdef _WIN32
+
+#include <windows.h>
+
+ typedef unsigned int in_addr_t;
+
+ struct sockaddr_un {
+ short sun_family;
+ char sun_path[MAXPATHLEN];
+ };
+
+ static HANDLE fcgi_accept_mutex = INVALID_HANDLE_VALUE;
+ static int is_impersonate = 0;
+
+#define FCGI_LOCK(fd) \
+ if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \
+ DWORD ret; \
+ while ((ret = WaitForSingleObject(fcgi_accept_mutex, 1000)) == WAIT_TIMEOUT) { \
+ if (in_shutdown) return -1; \
+ } \
+ if (ret == WAIT_FAILED) { \
+ fprintf(stderr, "WaitForSingleObject() failed\n"); \
+ return -1; \
+ } \
+ }
+
+#define FCGI_UNLOCK(fd) \
+ if (fcgi_accept_mutex != INVALID_HANDLE_VALUE) { \
+ ReleaseMutex(fcgi_accept_mutex); \
+ }
+
+#else
+
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <unistd.h>
+# include <fcntl.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <signal.h>
+
+# define closesocket(s) close(s)
+
+# if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
+# include <sys/poll.h>
+# endif
+# if defined(HAVE_SYS_SELECT_H)
+# include <sys/select.h>
+# endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned long) -1)
+#endif
+
+# ifndef HAVE_SOCKLEN_T
+ typedef unsigned int socklen_t;
+# endif
+
+# ifdef USE_LOCKING
+# define FCGI_LOCK(fd) \
+ do { \
+ struct flock lock; \
+ lock.l_type = F_WRLCK; \
+ lock.l_start = 0; \
+ lock.l_whence = SEEK_SET; \
+ lock.l_len = 0; \
+ if (fcntl(fd, F_SETLKW, &lock) != -1) { \
+ break; \
+ } else if (errno != EINTR || in_shutdown) { \
+ return -1; \
+ } \
+ } while (1)
+
+# define FCGI_UNLOCK(fd) \
+ do { \
+ int orig_errno = errno; \
+ while (1) { \
+ struct flock lock; \
+ lock.l_type = F_UNLCK; \
+ lock.l_start = 0; \
+ lock.l_whence = SEEK_SET; \
+ lock.l_len = 0; \
+ if (fcntl(fd, F_SETLK, &lock) != -1) { \
+ break; \
+ } else if (errno != EINTR) { \
+ return -1; \
+ } \
+ } \
+ errno = orig_errno; \
+ } while (0)
+# else
+# define FCGI_LOCK(fd)
+# define FCGI_UNLOCK(fd)
+# endif
+
+#endif
+
+typedef union _sa_t {
+ struct sockaddr sa;
+ struct sockaddr_un sa_unix;
+ struct sockaddr_in sa_inet;
+} sa_t;
+
+static HashTable fcgi_mgmt_vars;
+
+static int is_initialized = 0;
+static int in_shutdown = 0;
+static in_addr_t *allowed_clients = NULL;
+
+static sa_t client_sa;
+
+#ifdef _WIN32
+
+static DWORD WINAPI fcgi_shutdown_thread(LPVOID arg)
+{
+ HANDLE shutdown_event = (HANDLE) arg;
+ WaitForSingleObject(shutdown_event, INFINITE);
+ in_shutdown = 1;
+ return 0;
+}
+
+#else
+
+static void fcgi_signal_handler(int signo)
+{
+ if (signo == SIGUSR1 || signo == SIGTERM) {
+ in_shutdown = 1;
+ }
+}
+
+static void fcgi_setup_signals(void)
+{
+ struct sigaction new_sa, old_sa;
+
+ sigemptyset(&new_sa.sa_mask);
+ new_sa.sa_flags = 0;
+ new_sa.sa_handler = fcgi_signal_handler;
+ sigaction(SIGUSR1, &new_sa, NULL);
+ sigaction(SIGTERM, &new_sa, NULL);
+ sigaction(SIGPIPE, NULL, &old_sa);
+ if (old_sa.sa_handler == SIG_DFL) {
+ sigaction(SIGPIPE, &new_sa, NULL);
+ }
+}
+#endif
+
+int fcgi_init(void)
+{
+ if (!is_initialized) {
+ zend_hash_init(&fcgi_mgmt_vars, 0, NULL, fcgi_free_mgmt_var_cb, 1);
+ fcgi_set_mgmt_var("FCGI_MPXS_CONNS", sizeof("FCGI_MPXS_CONNS") - 1, "0", sizeof("0")-1);
+
+ is_initialized = 1;
+#ifdef _WIN32
+# if 0
+ /* TODO: Support for TCP sockets */
+ WSADATA wsaData;
+
+ if (WSAStartup(MAKEWORD(2,0), &wsaData)) {
+ fprintf(stderr, "Error starting Windows Sockets. Error: %d", WSAGetLastError());
+ return 0;
+ }
+# endif
+ {
+ char *str;
+ DWORD pipe_mode = PIPE_READMODE_BYTE | PIPE_WAIT;
+ HANDLE pipe = GetStdHandle(STD_INPUT_HANDLE);
+
+ SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL);
+
+ str = getenv("_FCGI_SHUTDOWN_EVENT_");
+ if (str != NULL) {
+ HANDLE shutdown_event = (HANDLE) atoi(str);
+ if (!CreateThread(NULL, 0, fcgi_shutdown_thread,
+ shutdown_event, 0, NULL)) {
+ return -1;
+ }
+ }
+ str = getenv("_FCGI_MUTEX_");
+ if (str != NULL) {
+ fcgi_accept_mutex = (HANDLE) atoi(str);
+ }
+ return 1;
+ }
+#else
+ fcgi_setup_signals();
+ return 1;
+#endif
+ }
+ return 1;
+}
+
+void fcgi_set_in_shutdown(int new_value)
+{
+ in_shutdown = new_value;
+}
+
+void fcgi_shutdown(void)
+{
+ if (is_initialized) {
+ zend_hash_destroy(&fcgi_mgmt_vars);
+ }
+ if (allowed_clients) {
+ free(allowed_clients);
+ }
+}
+
+void fcgi_set_allowed_clients(char *ip)
+{
+ char *cur, *end;
+ int n;
+
+ if (ip) {
+ ip = strdup(ip);
+ cur = ip;
+ n = 0;
+ while (*cur) {
+ if (*cur == ',') n++;
+ cur++;
+ }
+ if (allowed_clients) free(allowed_clients);
+ allowed_clients = malloc(sizeof(in_addr_t) * (n+2));
+ n = 0;
+ cur = ip;
+ while (cur) {
+ end = strchr(cur, ',');
+ if (end) {
+ *end = 0;
+ end++;
+ }
+ allowed_clients[n] = inet_addr(cur);
+ if (allowed_clients[n] == INADDR_NONE) {
+ zlog(ZLOG_ERROR, "Wrong IP address '%s' in listen.allowed_clients", cur);
+ }
+ n++;
+ cur = end;
+ }
+ allowed_clients[n] = INADDR_NONE;
+ free(ip);
+ }
+}
+
+void fcgi_init_request(fcgi_request *req, int listen_socket)
+{
+ memset(req, 0, sizeof(fcgi_request));
+ req->listen_socket = listen_socket;
+ req->fd = -1;
+ req->id = -1;
+
+ req->in_len = 0;
+ req->in_pad = 0;
+
+ req->out_hdr = NULL;
+ req->out_pos = req->out_buf;
+
+#ifdef _WIN32
+ req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL);
+#endif
+}
+
+static inline ssize_t safe_write(fcgi_request *req, const void *buf, size_t count)
+{
+ int ret;
+ size_t n = 0;
+
+ do {
+ errno = 0;
+#ifdef _WIN32
+ if (!req->tcp) {
+ ret = write(req->fd, ((char*)buf)+n, count-n);
+ } else {
+ ret = send(req->fd, ((char*)buf)+n, count-n, 0);
+ if (ret <= 0) {
+ errno = WSAGetLastError();
+ }
+ }
+#else
+ ret = write(req->fd, ((char*)buf)+n, count-n);
+#endif
+ if (ret > 0) {
+ n += ret;
+ } else if (ret <= 0 && errno != 0 && errno != EINTR) {
+ return ret;
+ }
+ } while (n != count);
+ return n;
+}
+
+static inline ssize_t safe_read(fcgi_request *req, const void *buf, size_t count)
+{
+ int ret;
+ size_t n = 0;
+
+ do {
+ errno = 0;
+#ifdef _WIN32
+ if (!req->tcp) {
+ ret = read(req->fd, ((char*)buf)+n, count-n);
+ } else {
+ ret = recv(req->fd, ((char*)buf)+n, count-n, 0);
+ if (ret <= 0) {
+ errno = WSAGetLastError();
+ }
+ }
+#else
+ ret = read(req->fd, ((char*)buf)+n, count-n);
+#endif
+ if (ret > 0) {
+ n += ret;
+ } else if (ret == 0 && errno == 0) {
+ return n;
+ } else if (ret <= 0 && errno != 0 && errno != EINTR) {
+ return ret;
+ }
+ } while (n != count);
+ return n;
+}
+
+static inline int fcgi_make_header(fcgi_header *hdr, fcgi_request_type type, int req_id, int len)
+{
+ int pad = ((len + 7) & ~7) - len;
+
+ hdr->contentLengthB0 = (unsigned char)(len & 0xff);
+ hdr->contentLengthB1 = (unsigned char)((len >> 8) & 0xff);
+ hdr->paddingLength = (unsigned char)pad;
+ hdr->requestIdB0 = (unsigned char)(req_id & 0xff);
+ hdr->requestIdB1 = (unsigned char)((req_id >> 8) & 0xff);
+ hdr->reserved = 0;
+ hdr->type = type;
+ hdr->version = FCGI_VERSION_1;
+ if (pad) {
+ memset(((unsigned char*)hdr) + sizeof(fcgi_header) + len, 0, pad);
+ }
+ return pad;
+}
+
+static inline size_t fcgi_get_params_len( int *result, unsigned char *p, unsigned char *end)
+{
+ size_t ret = 0;
+
+ if (p < end) {
+ *result = p[0];
+ if (*result < 128) {
+ ret = 1;
+ }
+ else if (p + 3 < end) {
+ *result = ((*result & 0x7f) << 24);
+ *result |= (p[1] << 16);
+ *result |= (p[2] << 8);
+ *result |= p[3];
+ ret = 4;
+ }
+ }
+ if (*result < 0) {
+ ret = 0;
+ }
+ return ret;
+}
+
+static inline int fcgi_param_get_eff_len( unsigned char *p, unsigned char *end, uint *eff_len)
+{
+ int ret = 1;
+ int zero_found = 0;
+ *eff_len = 0;
+ for (; p != end; ++p) {
+ if (*p == '\0') {
+ zero_found = 1;
+ }
+ else {
+ if (zero_found) {
+ ret = 0;
+ break;
+ }
+ if (*eff_len < ((uint)-1)) {
+ ++*eff_len;
+ }
+ else {
+ ret = 0;
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+static int fcgi_get_params(fcgi_request *req, unsigned char *p, unsigned char *end)
+{
+ char buf[128];
+ char *tmp = buf;
+ size_t buf_size = sizeof(buf);
+ int name_len, val_len;
+ uint eff_name_len;
+ char *s;
+ int ret = 1;
+ size_t bytes_consumed;
+
+ while (p < end) {
+ bytes_consumed = fcgi_get_params_len(&name_len, p, end);
+ if (!bytes_consumed) {
+ /* Malformated request */
+ ret = 0;
+ break;
+ }
+ p += bytes_consumed;
+ bytes_consumed = fcgi_get_params_len(&val_len, p, end);
+ if (!bytes_consumed) {
+ /* Malformated request */
+ ret = 0;
+ break;
+ }
+ p += bytes_consumed;
+ if (name_len > (INT_MAX - val_len) || /* would the addition overflow? */
+ name_len + val_len > end - p) { /* would we exceed the buffer? */
+ /* Malformated request */
+ ret = 0;
+ break;
+ }
+
+ /*
+ * get the effective length of the name in case it's not a valid string
+ * don't do this on the value because it can be binary data
+ */
+ if (!fcgi_param_get_eff_len(p, p+name_len, &eff_name_len)){
+ /* Malicious request */
+ ret = 0;
+ break;
+ }
+ if (eff_name_len >= buf_size-1) {
+ if (eff_name_len > ((uint)-1)-64) {
+ ret = 0;
+ break;
+ }
+ buf_size = eff_name_len + 64;
+ tmp = (tmp == buf ? emalloc(buf_size): erealloc(tmp, buf_size));
+ if (tmp == NULL) {
+ ret = 0;
+ break;
+ }
+ }
+ memcpy(tmp, p, eff_name_len);
+ tmp[eff_name_len] = 0;
+ s = estrndup((char*)p + name_len, val_len);
+ if (s == NULL) {
+ ret = 0;
+ break;
+ }
+ zend_hash_update(req->env, tmp, eff_name_len+1, &s, sizeof(char*), NULL);
+ p += name_len + val_len;
+ }
+ if (tmp != buf && tmp != NULL) {
+ efree(tmp);
+ }
+ return ret;
+}
+
+static void fcgi_free_var(char **s)
+{
+ efree(*s);
+}
+
+static int fcgi_read_request(fcgi_request *req)
+{
+ fcgi_header hdr;
+ int len, padding;
+ unsigned char buf[FCGI_MAX_LENGTH+8];
+
+ req->keep = 0;
+ req->closed = 0;
+ req->in_len = 0;
+ req->out_hdr = NULL;
+ req->out_pos = req->out_buf;
+ ALLOC_HASHTABLE(req->env);
+ zend_hash_init(req->env, 0, NULL, (void (*)(void *)) fcgi_free_var, 0);
+
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1) {
+ return 0;
+ }
+
+ len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ padding = hdr.paddingLength;
+
+ while (hdr.type == FCGI_STDIN && len == 0) {
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1) {
+ return 0;
+ }
+
+ len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ padding = hdr.paddingLength;
+ }
+
+ if (len + padding > FCGI_MAX_LENGTH) {
+ return 0;
+ }
+
+ req->id = (hdr.requestIdB1 << 8) + hdr.requestIdB0;
+
+ if (hdr.type == FCGI_BEGIN_REQUEST && len == sizeof(fcgi_begin_request)) {
+ char *val;
+
+ if (safe_read(req, buf, len+padding) != len+padding) {
+ return 0;
+ }
+
+ req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN);
+ switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) {
+ case FCGI_RESPONDER:
+ val = estrdup("RESPONDER");
+ zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
+ break;
+ case FCGI_AUTHORIZER:
+ val = estrdup("AUTHORIZER");
+ zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
+ break;
+ case FCGI_FILTER:
+ val = estrdup("FILTER");
+ zend_hash_update(req->env, "FCGI_ROLE", sizeof("FCGI_ROLE"), &val, sizeof(char*), NULL);
+ break;
+ default:
+ return 0;
+ }
+
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1) {
+ return 0;
+ }
+
+ len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ padding = hdr.paddingLength;
+
+ while (hdr.type == FCGI_PARAMS && len > 0) {
+ if (len + padding > FCGI_MAX_LENGTH) {
+ return 0;
+ }
+
+ if (safe_read(req, buf, len+padding) != len+padding) {
+ req->keep = 0;
+ return 0;
+ }
+
+ if (!fcgi_get_params(req, buf, buf+len)) {
+ req->keep = 0;
+ return 0;
+ }
+
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1) {
+ req->keep = 0;
+ return 0;
+ }
+ len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ padding = hdr.paddingLength;
+ }
+ } else if (hdr.type == FCGI_GET_VALUES) {
+ unsigned char *p = buf + sizeof(fcgi_header);
+ HashPosition pos;
+ char * str_index;
+ uint str_length;
+ ulong num_index;
+ int key_type;
+ zval ** value;
+
+ if (safe_read(req, buf, len+padding) != len+padding) {
+ req->keep = 0;
+ return 0;
+ }
+
+ if (!fcgi_get_params(req, buf, buf+len)) {
+ req->keep = 0;
+ return 0;
+ }
+
+ zend_hash_internal_pointer_reset_ex(req->env, &pos);
+ while ((key_type = zend_hash_get_current_key_ex(req->env, &str_index, &str_length, &num_index, 0, &pos)) != HASH_KEY_NON_EXISTANT) {
+ int zlen;
+ zend_hash_move_forward_ex(req->env, &pos);
+ if (key_type != HASH_KEY_IS_STRING) {
+ continue;
+ }
+ if (zend_hash_find(&fcgi_mgmt_vars, str_index, str_length, (void**) &value) != SUCCESS) {
+ continue;
+ }
+ --str_length;
+ zlen = Z_STRLEN_PP(value);
+ if ((p + 4 + 4 + str_length + zlen) >= (buf + sizeof(buf))) {
+ break;
+ }
+ if (str_length < 0x80) {
+ *p++ = str_length;
+ } else {
+ *p++ = ((str_length >> 24) & 0xff) | 0x80;
+ *p++ = (str_length >> 16) & 0xff;
+ *p++ = (str_length >> 8) & 0xff;
+ *p++ = str_length & 0xff;
+ }
+ if (zlen < 0x80) {
+ *p++ = zlen;
+ } else {
+ *p++ = ((zlen >> 24) & 0xff) | 0x80;
+ *p++ = (zlen >> 16) & 0xff;
+ *p++ = (zlen >> 8) & 0xff;
+ *p++ = zlen & 0xff;
+ }
+ memcpy(p, str_index, str_length);
+ p += str_length;
+ memcpy(p, Z_STRVAL_PP(value), zlen);
+ p += zlen;
+ }
+ len = p - buf - sizeof(fcgi_header);
+ len += fcgi_make_header((fcgi_header*)buf, FCGI_GET_VALUES_RESULT, 0, len);
+ if (safe_write(req, buf, sizeof(fcgi_header)+len) != (int)sizeof(fcgi_header)+len) {
+ req->keep = 0;
+ return 0;
+ }
+ return 0;
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+int fcgi_read(fcgi_request *req, char *str, int len)
+{
+ int ret, n, rest;
+ fcgi_header hdr;
+ unsigned char buf[255];
+
+ n = 0;
+ rest = len;
+ while (rest > 0) {
+ if (req->in_len == 0) {
+ if (safe_read(req, &hdr, sizeof(fcgi_header)) != sizeof(fcgi_header) ||
+ hdr.version < FCGI_VERSION_1 ||
+ hdr.type != FCGI_STDIN) {
+ req->keep = 0;
+ return 0;
+ }
+ req->in_len = (hdr.contentLengthB1 << 8) | hdr.contentLengthB0;
+ req->in_pad = hdr.paddingLength;
+ if (req->in_len == 0) {
+ return n;
+ }
+ }
+
+ if (req->in_len >= rest) {
+ ret = safe_read(req, str, rest);
+ } else {
+ ret = safe_read(req, str, req->in_len);
+ }
+ if (ret < 0) {
+ req->keep = 0;
+ return ret;
+ } else if (ret > 0) {
+ req->in_len -= ret;
+ rest -= ret;
+ n += ret;
+ str += ret;
+ if (req->in_len == 0) {
+ if (req->in_pad) {
+ if (safe_read(req, buf, req->in_pad) != req->in_pad) {
+ req->keep = 0;
+ return ret;
+ }
+ }
+ } else {
+ return n;
+ }
+ } else {
+ return n;
+ }
+ }
+ return n;
+}
+
+void fcgi_close(fcgi_request *req, int force, int destroy)
+{
+ if (destroy && req->env) {
+ zend_hash_destroy(req->env);
+ FREE_HASHTABLE(req->env);
+ req->env = NULL;
+ }
+
+#ifdef _WIN32
+ if (is_impersonate && !req->tcp) {
+ RevertToSelf();
+ }
+#endif
+
+ if ((force || !req->keep) && req->fd >= 0) {
+#ifdef _WIN32
+ if (!req->tcp) {
+ HANDLE pipe = (HANDLE)_get_osfhandle(req->fd);
+
+ if (!force) {
+ FlushFileBuffers(pipe);
+ }
+ DisconnectNamedPipe(pipe);
+ } else {
+ if (!force) {
+ char buf[8];
+
+ shutdown(req->fd, 1);
+ while (recv(req->fd, buf, sizeof(buf), 0) > 0) {}
+ }
+ closesocket(req->fd);
+ }
+#else
+ if (!force) {
+ char buf[8];
+
+ shutdown(req->fd, 1);
+ while (recv(req->fd, buf, sizeof(buf), 0) > 0) {}
+ }
+ close(req->fd);
+#endif
+ req->fd = -1;
+ fpm_request_finished();
+ }
+}
+
+int fcgi_accept_request(fcgi_request *req)
+{
+#ifdef _WIN32
+ HANDLE pipe;
+ OVERLAPPED ov;
+#endif
+
+ while (1) {
+ if (req->fd < 0) {
+ while (1) {
+ if (in_shutdown) {
+ return -1;
+ }
+#ifdef _WIN32
+ if (!req->tcp) {
+ pipe = (HANDLE)_get_osfhandle(req->listen_socket);
+ FCGI_LOCK(req->listen_socket);
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!ConnectNamedPipe(pipe, &ov)) {
+ errno = GetLastError();
+ if (errno == ERROR_IO_PENDING) {
+ while (WaitForSingleObject(ov.hEvent, 1000) == WAIT_TIMEOUT) {
+ if (in_shutdown) {
+ CloseHandle(ov.hEvent);
+ FCGI_UNLOCK(req->listen_socket);
+ return -1;
+ }
+ }
+ } else if (errno != ERROR_PIPE_CONNECTED) {
+ }
+ }
+ CloseHandle(ov.hEvent);
+ req->fd = req->listen_socket;
+ FCGI_UNLOCK(req->listen_socket);
+ } else {
+ SOCKET listen_socket = (SOCKET)_get_osfhandle(req->listen_socket);
+#else
+ {
+ int listen_socket = req->listen_socket;
+#endif
+ sa_t sa;
+ socklen_t len = sizeof(sa);
+
+ fpm_request_accepting();
+
+ FCGI_LOCK(req->listen_socket);
+ req->fd = accept(listen_socket, (struct sockaddr *)&sa, &len);
+ FCGI_UNLOCK(req->listen_socket);
+
+ client_sa = sa;
+ if (sa.sa.sa_family == AF_INET && req->fd >= 0 && allowed_clients) {
+ int n = 0;
+ int allowed = 0;
+
+ while (allowed_clients[n] != INADDR_NONE) {
+ if (allowed_clients[n] == sa.sa_inet.sin_addr.s_addr) {
+ allowed = 1;
+ break;
+ }
+ n++;
+ }
+ if (!allowed) {
+ zlog(ZLOG_ERROR, "Connection disallowed: IP address '%s' has been dropped.", inet_ntoa(sa.sa_inet.sin_addr));
+ closesocket(req->fd);
+ req->fd = -1;
+ continue;
+ }
+ }
+ }
+
+#ifdef _WIN32
+ if (req->fd < 0 && (in_shutdown || errno != EINTR)) {
+#else
+ if (req->fd < 0 && (in_shutdown || (errno != EINTR && errno != ECONNABORTED))) {
+#endif
+ return -1;
+ }
+
+#ifdef _WIN32
+ break;
+#else
+ if (req->fd >= 0) {
+#if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
+ struct pollfd fds;
+ int ret;
+
+ fpm_request_reading_headers();
+
+ fds.fd = req->fd;
+ fds.events = POLLIN;
+ fds.revents = 0;
+ do {
+ errno = 0;
+ ret = poll(&fds, 1, 5000);
+ } while (ret < 0 && errno == EINTR);
+ if (ret > 0 && (fds.revents & POLLIN)) {
+ break;
+ }
+ fcgi_close(req, 1, 0);
+#else
+ fpm_request_reading_headers();
+
+ if (req->fd < FD_SETSIZE) {
+ struct timeval tv = {5,0};
+ fd_set set;
+ int ret;
+
+ FD_ZERO(&set);
+ FD_SET(req->fd, &set);
+ do {
+ errno = 0;
+ ret = select(req->fd + 1, &set, NULL, NULL, &tv) >= 0;
+ } while (ret < 0 && errno == EINTR);
+ if (ret > 0 && FD_ISSET(req->fd, &set)) {
+ break;
+ }
+ fcgi_close(req, 1, 0);
+ } else {
+ zlog(ZLOG_ERROR, "Too many open file descriptors. FD_SETSIZE limit exceeded.");
+ fcgi_close(req, 1, 0);
+ }
+#endif
+ }
+#endif
+ }
+ } else if (in_shutdown) {
+ return -1;
+ }
+ if (fcgi_read_request(req)) {
+#ifdef _WIN32
+ if (is_impersonate && !req->tcp) {
+ pipe = (HANDLE)_get_osfhandle(req->fd);
+ if (!ImpersonateNamedPipeClient(pipe)) {
+ fcgi_close(req, 1, 1);
+ continue;
+ }
+ }
+#endif
+ return req->fd;
+ } else {
+ fcgi_close(req, 1, 1);
+ }
+ }
+}
+
+static inline fcgi_header* open_packet(fcgi_request *req, fcgi_request_type type)
+{
+ req->out_hdr = (fcgi_header*) req->out_pos;
+ req->out_hdr->type = type;
+ req->out_pos += sizeof(fcgi_header);
+ return req->out_hdr;
+}
+
+static inline void close_packet(fcgi_request *req)
+{
+ if (req->out_hdr) {
+ int len = req->out_pos - ((unsigned char*)req->out_hdr + sizeof(fcgi_header));
+
+ req->out_pos += fcgi_make_header(req->out_hdr, (fcgi_request_type)req->out_hdr->type, req->id, len);
+ req->out_hdr = NULL;
+ }
+}
+
+int fcgi_flush(fcgi_request *req, int close)
+{
+ int len;
+
+ close_packet(req);
+
+ len = req->out_pos - req->out_buf;
+
+ if (close) {
+ fcgi_end_request_rec *rec = (fcgi_end_request_rec*)(req->out_pos);
+
+ fcgi_make_header(&rec->hdr, FCGI_END_REQUEST, req->id, sizeof(fcgi_end_request));
+ rec->body.appStatusB3 = 0;
+ rec->body.appStatusB2 = 0;
+ rec->body.appStatusB1 = 0;
+ rec->body.appStatusB0 = 0;
+ rec->body.protocolStatus = FCGI_REQUEST_COMPLETE;
+ len += sizeof(fcgi_end_request_rec);
+ }
+
+ if (safe_write(req, req->out_buf, len) != len) {
+ req->keep = 0;
+ return 0;
+ }
+
+ req->out_pos = req->out_buf;
+ return 1;
+}
+
+ssize_t fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len)
+{
+ int limit, rest;
+
+ if (len <= 0) {
+ return 0;
+ }
+
+ if (req->out_hdr && req->out_hdr->type != type) {
+ close_packet(req);
+ }
+
+ /* Optimized version */
+ limit = sizeof(req->out_buf) - (req->out_pos - req->out_buf);
+ if (!req->out_hdr) {
+ limit -= sizeof(fcgi_header);
+ if (limit < 0) limit = 0;
+ }
+
+ if (len < limit) {
+ if (!req->out_hdr) {
+ open_packet(req, type);
+ }
+ memcpy(req->out_pos, str, len);
+ req->out_pos += len;
+ } else if (len - limit < sizeof(req->out_buf) - sizeof(fcgi_header)) {
+ if (!req->out_hdr) {
+ open_packet(req, type);
+ }
+ if (limit > 0) {
+ memcpy(req->out_pos, str, limit);
+ req->out_pos += limit;
+ }
+ if (!fcgi_flush(req, 0)) {
+ return -1;
+ }
+ if (len > limit) {
+ open_packet(req, type);
+ memcpy(req->out_pos, str + limit, len - limit);
+ req->out_pos += len - limit;
+ }
+ } else {
+ int pos = 0;
+ int pad;
+
+ close_packet(req);
+ while ((len - pos) > 0xffff) {
+ open_packet(req, type);
+ fcgi_make_header(req->out_hdr, type, req->id, 0xfff8);
+ req->out_hdr = NULL;
+ if (!fcgi_flush(req, 0)) {
+ return -1;
+ }
+ if (safe_write(req, str + pos, 0xfff8) != 0xfff8) {
+ req->keep = 0;
+ return -1;
+ }
+ pos += 0xfff8;
+ }
+
+ pad = (((len - pos) + 7) & ~7) - (len - pos);
+ rest = pad ? 8 - pad : 0;
+
+ open_packet(req, type);
+ fcgi_make_header(req->out_hdr, type, req->id, (len - pos) - rest);
+ req->out_hdr = NULL;
+ if (!fcgi_flush(req, 0)) {
+ return -1;
+ }
+ if (safe_write(req, str + pos, (len - pos) - rest) != (len - pos) - rest) {
+ req->keep = 0;
+ return -1;
+ }
+ if (pad) {
+ open_packet(req, type);
+ memcpy(req->out_pos, str + len - rest, rest);
+ req->out_pos += rest;
+ }
+ }
+
+ return len;
+}
+
+int fcgi_finish_request(fcgi_request *req, int force_close)
+{
+ int ret = 1;
+
+ if (req->fd >= 0) {
+ if (!req->closed) {
+ ret = fcgi_flush(req, 1);
+ req->closed = 1;
+ }
+ fcgi_close(req, force_close, 1);
+ }
+ return ret;
+}
+
+char* fcgi_getenv(fcgi_request *req, const char* var, int var_len)
+{
+ char **val;
+
+ if (!req) return NULL;
+
+ if (zend_hash_find(req->env, (char*)var, var_len+1, (void**)&val) == SUCCESS) {
+ return *val;
+ }
+ return NULL;
+}
+
+char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val)
+{
+ if (var && req) {
+ if (val == NULL) {
+ zend_hash_del(req->env, var, var_len+1);
+ } else {
+ char **ret;
+
+ val = estrdup(val);
+ if (zend_hash_update(req->env, var, var_len+1, &val, sizeof(char*), (void**)&ret) == SUCCESS) {
+ return *ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len)
+{
+ zval * zvalue;
+ zvalue = pemalloc(sizeof(*zvalue), 1);
+ Z_TYPE_P(zvalue) = IS_STRING;
+ Z_STRVAL_P(zvalue) = pestrndup(value, value_len, 1);
+ Z_STRLEN_P(zvalue) = value_len;
+ zend_hash_add(&fcgi_mgmt_vars, name, name_len + 1, &zvalue, sizeof(zvalue), NULL);
+}
+
+void fcgi_free_mgmt_var_cb(void * ptr)
+{
+ zval ** var = (zval **)ptr;
+ pefree(Z_STRVAL_PP(var), 1);
+ pefree(*var, 1);
+}
+
+char *fcgi_get_last_client_ip() /* {{{ */
+{
+ if (client_sa.sa.sa_family == AF_UNIX) {
+ return NULL;
+ }
+ return inet_ntoa(client_sa.sa_inet.sin_addr);
+}
+/* }}} */
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/fpm/fpm/fastcgi.h b/sapi/fpm/fpm/fastcgi.h
new file mode 100644
index 0000000..ee78675
--- /dev/null
+++ b/sapi/fpm/fpm/fastcgi.h
@@ -0,0 +1,145 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: fastcgi.h 272370 2008-12-31 11:15:49Z sebastian $ */
+
+/* FastCGI protocol */
+
+#define FCGI_VERSION_1 1
+
+#define FCGI_MAX_LENGTH 0xffff
+
+#define FCGI_KEEP_CONN 1
+
+typedef enum _fcgi_role {
+ FCGI_RESPONDER = 1,
+ FCGI_AUTHORIZER = 2,
+ FCGI_FILTER = 3
+} fcgi_role;
+
+typedef enum _fcgi_request_type {
+ FCGI_BEGIN_REQUEST = 1, /* [in] */
+ FCGI_ABORT_REQUEST = 2, /* [in] (not supported) */
+ FCGI_END_REQUEST = 3, /* [out] */
+ FCGI_PARAMS = 4, /* [in] environment variables */
+ FCGI_STDIN = 5, /* [in] post data */
+ FCGI_STDOUT = 6, /* [out] response */
+ FCGI_STDERR = 7, /* [out] errors */
+ FCGI_DATA = 8, /* [in] filter data (not supported) */
+ FCGI_GET_VALUES = 9, /* [in] */
+ FCGI_GET_VALUES_RESULT = 10 /* [out] */
+} fcgi_request_type;
+
+typedef enum _fcgi_protocol_status {
+ FCGI_REQUEST_COMPLETE = 0,
+ FCGI_CANT_MPX_CONN = 1,
+ FCGI_OVERLOADED = 2,
+ FCGI_UNKNOWN_ROLE = 3
+} dcgi_protocol_status;
+
+typedef struct _fcgi_header {
+ unsigned char version;
+ unsigned char type;
+ unsigned char requestIdB1;
+ unsigned char requestIdB0;
+ unsigned char contentLengthB1;
+ unsigned char contentLengthB0;
+ unsigned char paddingLength;
+ unsigned char reserved;
+} fcgi_header;
+
+typedef struct _fcgi_begin_request {
+ unsigned char roleB1;
+ unsigned char roleB0;
+ unsigned char flags;
+ unsigned char reserved[5];
+} fcgi_begin_request;
+
+typedef struct _fcgi_begin_request_rec {
+ fcgi_header hdr;
+ fcgi_begin_request body;
+} fcgi_begin_request_rec;
+
+typedef struct _fcgi_end_request {
+ unsigned char appStatusB3;
+ unsigned char appStatusB2;
+ unsigned char appStatusB1;
+ unsigned char appStatusB0;
+ unsigned char protocolStatus;
+ unsigned char reserved[3];
+} fcgi_end_request;
+
+typedef struct _fcgi_end_request_rec {
+ fcgi_header hdr;
+ fcgi_end_request body;
+} fcgi_end_request_rec;
+
+/* FastCGI client API */
+
+typedef struct _fcgi_request {
+ int listen_socket;
+#ifdef _WIN32
+ int tcp;
+#endif
+ int fd;
+ int id;
+ int keep;
+ int closed;
+
+ int in_len;
+ int in_pad;
+
+ fcgi_header *out_hdr;
+ unsigned char *out_pos;
+ unsigned char out_buf[1024*8];
+ unsigned char reserved[sizeof(fcgi_end_request_rec)];
+
+ HashTable *env;
+} fcgi_request;
+
+int fcgi_init(void);
+void fcgi_shutdown(void);
+void fcgi_init_request(fcgi_request *req, int listen_socket);
+int fcgi_accept_request(fcgi_request *req);
+int fcgi_finish_request(fcgi_request *req, int force_close);
+
+void fcgi_set_in_shutdown(int);
+void fcgi_set_allowed_clients(char *);
+void fcgi_close(fcgi_request *req, int force, int destroy);
+
+char* fcgi_getenv(fcgi_request *req, const char* var, int var_len);
+char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val);
+
+int fcgi_read(fcgi_request *req, char *str, int len);
+
+ssize_t fcgi_write(fcgi_request *req, fcgi_request_type type, const char *str, int len);
+int fcgi_flush(fcgi_request *req, int close);
+
+void fcgi_set_mgmt_var(const char * name, size_t name_len, const char * value, size_t value_len);
+void fcgi_free_mgmt_var_cb(void * ptr);
+
+char *fcgi_get_last_client_ip();
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/fpm/fpm/fpm.c b/sapi/fpm/fpm/fpm.c
new file mode 100644
index 0000000..b866f37
--- /dev/null
+++ b/sapi/fpm/fpm/fpm.c
@@ -0,0 +1,123 @@
+
+ /* $Id: fpm.c,v 1.23 2008/07/20 16:38:31 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <stdlib.h> /* for exit */
+
+#include "fpm.h"
+#include "fpm_children.h"
+#include "fpm_signals.h"
+#include "fpm_env.h"
+#include "fpm_events.h"
+#include "fpm_cleanup.h"
+#include "fpm_php.h"
+#include "fpm_sockets.h"
+#include "fpm_unix.h"
+#include "fpm_process_ctl.h"
+#include "fpm_conf.h"
+#include "fpm_worker_pool.h"
+#include "fpm_scoreboard.h"
+#include "fpm_stdio.h"
+#include "fpm_log.h"
+#include "zlog.h"
+
+struct fpm_globals_s fpm_globals = {
+ .parent_pid = 0,
+ .argc = 0,
+ .argv = NULL,
+ .config = NULL,
+ .prefix = NULL,
+ .pid = NULL,
+ .running_children = 0,
+ .error_log_fd = 0,
+ .log_level = 0,
+ .listening_socket = 0,
+ .max_requests = 0,
+ .is_child = 0,
+ .test_successful = 0,
+ .heartbeat = 0,
+ .run_as_root = 0,
+ .send_config_pipe = {0, 0},
+};
+
+int fpm_init(int argc, char **argv, char *config, char *prefix, char *pid, int test_conf, int run_as_root, int force_daemon) /* {{{ */
+{
+ fpm_globals.argc = argc;
+ fpm_globals.argv = argv;
+ if (config && *config) {
+ fpm_globals.config = strdup(config);
+ }
+ fpm_globals.prefix = prefix;
+ fpm_globals.pid = pid;
+ fpm_globals.run_as_root = run_as_root;
+
+ if (0 > fpm_php_init_main() ||
+ 0 > fpm_stdio_init_main() ||
+ 0 > fpm_conf_init_main(test_conf, force_daemon) ||
+ 0 > fpm_unix_init_main() ||
+ 0 > fpm_scoreboard_init_main() ||
+ 0 > fpm_pctl_init_main() ||
+ 0 > fpm_env_init_main() ||
+ 0 > fpm_signals_init_main() ||
+ 0 > fpm_children_init_main() ||
+ 0 > fpm_sockets_init_main() ||
+ 0 > fpm_worker_pool_init_main() ||
+ 0 > fpm_event_init_main()) {
+
+ if (fpm_globals.test_successful) {
+ exit(FPM_EXIT_OK);
+ } else {
+ zlog(ZLOG_ERROR, "FPM initialization failed");
+ return -1;
+ }
+ }
+
+ if (0 > fpm_conf_write_pid()) {
+ zlog(ZLOG_ERROR, "FPM initialization failed");
+ return -1;
+ }
+
+ fpm_stdio_init_final();
+ zlog(ZLOG_NOTICE, "fpm is running, pid %d", (int) fpm_globals.parent_pid);
+
+ return 0;
+}
+/* }}} */
+
+/* children: return listening socket
+ parent: never return */
+int fpm_run(int *max_requests) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+
+ /* create initial children in all pools */
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ int is_parent;
+
+ is_parent = fpm_children_create_initial(wp);
+
+ if (!is_parent) {
+ goto run_child;
+ }
+
+ /* handle error */
+ if (is_parent == 2) {
+ fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
+ fpm_event_loop(1);
+ }
+ }
+
+ /* run event loop forever */
+ fpm_event_loop(0);
+
+run_child: /* only workers reach this point */
+
+ fpm_cleanups_run(FPM_CLEANUP_CHILD);
+
+ *max_requests = fpm_globals.max_requests;
+ return fpm_globals.listening_socket;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm.h b/sapi/fpm/fpm/fpm.h
new file mode 100644
index 0000000..65d0e0d
--- /dev/null
+++ b/sapi/fpm/fpm/fpm.h
@@ -0,0 +1,63 @@
+
+ /* $Id: fpm.h,v 1.13 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_H
+#define FPM_H 1
+
+#include <unistd.h>
+
+#ifdef HAVE_SYSEXITS_H
+#include <sysexits.h>
+#endif
+
+#ifdef EX_OK
+#define FPM_EXIT_OK EX_OK
+#else
+#define FPM_EXIT_OK 0
+#endif
+
+#ifdef EX_USAGE
+#define FPM_EXIT_USAGE EX_USAGE
+#else
+#define FPM_EXIT_USAGE 64
+#endif
+
+#ifdef EX_SOFTWARE
+#define FPM_EXIT_SOFTWARE EX_SOFTWARE
+#else
+#define FPM_EXIT_SOFTWARE 70
+#endif
+
+#ifdef EX_CONFIG
+#define FPM_EXIT_CONFIG EX_CONFIG
+#else
+#define FPM_EXIT_CONFIG 78
+#endif
+
+
+int fpm_run(int *max_requests);
+int fpm_init(int argc, char **argv, char *config, char *prefix, char *pid, int test_conf, int run_as_root, int force_daemon);
+
+struct fpm_globals_s {
+ pid_t parent_pid;
+ int argc;
+ char **argv;
+ char *config;
+ char *prefix;
+ char *pid;
+ int running_children;
+ int error_log_fd;
+ int log_level;
+ int listening_socket; /* for this child */
+ int max_requests; /* for this child */
+ int is_child;
+ int test_successful;
+ int heartbeat;
+ int run_as_root;
+ int send_config_pipe[2];
+};
+
+extern struct fpm_globals_s fpm_globals;
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_arrays.h b/sapi/fpm/fpm/fpm_arrays.h
new file mode 100644
index 0000000..02846b7
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_arrays.h
@@ -0,0 +1,116 @@
+
+ /* $Id: fpm_arrays.h,v 1.2 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_ARRAYS_H
+#define FPM_ARRAYS_H 1
+
+#include <stdlib.h>
+#include <string.h>
+
+struct fpm_array_s {
+ void *data;
+ size_t sz;
+ size_t used;
+ size_t allocated;
+};
+
+static inline struct fpm_array_s *fpm_array_init(struct fpm_array_s *a, unsigned int sz, unsigned int initial_num) /* {{{ */
+{
+ void *allocated = 0;
+
+ if (!a) {
+ a = malloc(sizeof(struct fpm_array_s));
+
+ if (!a) {
+ return 0;
+ }
+
+ allocated = a;
+ }
+
+ a->sz = sz;
+
+ a->data = calloc(sz, initial_num);
+
+ if (!a->data) {
+ free(allocated);
+ return 0;
+ }
+
+ a->allocated = initial_num;
+ a->used = 0;
+
+ return a;
+}
+/* }}} */
+
+static inline void *fpm_array_item(struct fpm_array_s *a, unsigned int n) /* {{{ */
+{
+ char *ret;
+
+ ret = (char *) a->data + a->sz * n;
+
+ return ret;
+}
+/* }}} */
+
+static inline void *fpm_array_item_last(struct fpm_array_s *a) /* {{{ */
+{
+ return fpm_array_item(a, a->used - 1);
+}
+/* }}} */
+
+static inline int fpm_array_item_remove(struct fpm_array_s *a, unsigned int n) /* {{{ */
+{
+ int ret = -1;
+
+ if (n < a->used - 1) {
+ void *last = fpm_array_item(a, a->used - 1);
+ void *to_remove = fpm_array_item(a, n);
+
+ memcpy(to_remove, last, a->sz);
+
+ ret = n;
+ }
+
+ --a->used;
+
+ return ret;
+}
+/* }}} */
+
+static inline void *fpm_array_push(struct fpm_array_s *a) /* {{{ */
+{
+ void *ret;
+
+ if (a->used == a->allocated) {
+ size_t new_allocated = a->allocated ? a->allocated * 2 : 20;
+ void *new_ptr = realloc(a->data, a->sz * new_allocated);
+
+ if (!new_ptr) {
+ return 0;
+ }
+
+ a->data = new_ptr;
+ a->allocated = new_allocated;
+ }
+
+ ret = fpm_array_item(a, a->used);
+
+ ++a->used;
+
+ return ret;
+}
+/* }}} */
+
+static inline void fpm_array_free(struct fpm_array_s *a) /* {{{ */
+{
+ free(a->data);
+ a->data = 0;
+ a->sz = 0;
+ a->used = a->allocated = 0;
+}
+/* }}} */
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_atomic.h b/sapi/fpm/fpm/fpm_atomic.h
new file mode 100644
index 0000000..662dd47
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_atomic.h
@@ -0,0 +1,168 @@
+
+ /* $Id: fpm_atomic.h,v 1.3 2008/09/18 23:34:11 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_ATOMIC_H
+#define FPM_ATOMIC_H 1
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# include <stdint.h>
+#endif
+#include <sched.h>
+
+#ifdef HAVE_BUILTIN_ATOMIC
+
+/**
+ * all the cases below (as provided by upstream) define:
+ * word as atomic_int_t, and
+ * unsigned word as atomic_uint_t
+ * and only use volatile atomic_uint_t as atomic_t
+ */
+
+typedef volatile unsigned long atomic_t;
+#define atomic_cmp_set(a,b,c) __sync_bool_compare_and_swap(a,b,c)
+
+#elif ( __i386__ || __i386 )
+
+typedef int32_t atomic_int_t;
+typedef uint32_t atomic_uint_t;
+typedef volatile atomic_uint_t atomic_t;
+
+
+static inline atomic_int_t atomic_fetch_add(atomic_t *value, atomic_int_t add) /* {{{ */
+{
+ __asm__ volatile ( "lock;" "xaddl %0, %1;" :
+ "+r" (add) : "m" (*value) : "memory");
+
+ return add;
+}
+/* }}} */
+
+static inline atomic_uint_t atomic_cmp_set(atomic_t *lock, atomic_uint_t old, atomic_uint_t set) /* {{{ */
+{
+ unsigned char res;
+
+ __asm__ volatile ( "lock;" "cmpxchgl %3, %1;" "sete %0;" :
+ "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "memory");
+
+ return res;
+}
+/* }}} */
+
+#elif ( __amd64__ || __amd64 || __x86_64__ )
+
+typedef int64_t atomic_int_t;
+typedef uint64_t atomic_uint_t;
+typedef volatile atomic_uint_t atomic_t;
+
+static inline atomic_int_t atomic_fetch_add(atomic_t *value, atomic_int_t add) /* {{{ */
+{
+ __asm__ volatile ( "lock;" "xaddq %0, %1;" :
+ "+r" (add) : "m" (*value) : "memory");
+
+ return add;
+}
+/* }}} */
+
+static inline atomic_uint_t atomic_cmp_set(atomic_t *lock, atomic_uint_t old, atomic_uint_t set) /* {{{ */
+{
+ unsigned char res;
+
+ __asm__ volatile ( "lock;" "cmpxchgq %3, %1;" "sete %0;" :
+ "=a" (res) : "m" (*lock), "a" (old), "r" (set) : "memory");
+
+ return res;
+}
+/* }}} */
+
+#if (__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))
+
+#elif ( __arm__ || __arm ) /* W-Mark Kubacki */
+
+#if (__arch64__ || __arch64)
+typedef int64_t atomic_int_t;
+typedef uint64_t atomic_uint_t;
+#else
+typedef int32_t atomic_int_t;
+typedef uint32_t atomic_uint_t;
+#endif
+
+#define atomic_cmp_set(a,b,c) __sync_bool_compare_and_swap(a,b,c)
+
+#endif /* defined (__GNUC__) &&... */
+
+#elif ( __sparc__ || __sparc ) /* Marcin Ochab */
+
+#if (__sparcv9 || __sparcv9__)
+
+#if (__arch64__ || __arch64)
+typedef uint64_t atomic_uint_t;
+typedef volatile atomic_uint_t atomic_t;
+
+static inline int atomic_cas_64(atomic_t *lock, atomic_uint_t old, atomic_uint_t new) /* {{{ */
+{
+ __asm__ __volatile__("casx [%2], %3, %0 " : "=&r"(new) : "0"(new), "r"(lock), "r"(old): "memory");
+
+ return new;
+}
+/* }}} */
+
+static inline atomic_uint_t atomic_cmp_set(atomic_t *lock, atomic_uint_t old, atomic_uint_t set) /* {{{ */
+{
+ return (atomic_cas_64(lock, old, set)==old);
+}
+/* }}} */
+#else
+typedef uint32_t atomic_uint_t;
+typedef volatile atomic_uint_t atomic_t;
+
+static inline int atomic_cas_32(atomic_t *lock, atomic_uint_t old, atomic_uint_t new) /* {{{ */
+{
+ __asm__ __volatile__("cas [%2], %3, %0 " : "=&r"(new) : "0"(new), "r"(lock), "r"(old): "memory");
+
+ return new;
+}
+/* }}} */
+
+static inline atomic_uint_t atomic_cmp_set(atomic_t *lock, atomic_uint_t old, atomic_uint_t set) /* {{{ */
+{
+ return (atomic_cas_32(lock, old, set)==old);
+}
+/* }}} */
+#endif
+
+#else /* #if (__sparcv9 || __sparcv9__) */
+#error Sparc v8 and predecessors are not and will not be supported (see bug report 53310)
+#endif /* #if (__sparcv9 || __sparcv9__) */
+
+#else
+
+#error Unsupported processor. Please open a bug report (bugs.php.net).
+
+#endif
+
+static inline int fpm_spinlock(atomic_t *lock, int try_once) /* {{{ */
+{
+ if (try_once) {
+ return atomic_cmp_set(lock, 0, 1) ? 1 : 0;
+ }
+
+ for (;;) {
+
+ if (atomic_cmp_set(lock, 0, 1)) {
+ break;
+ }
+
+ sched_yield();
+ }
+
+ return 1;
+}
+/* }}} */
+
+#define fpm_unlock(lock) lock = 0
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_children.c b/sapi/fpm/fpm/fpm_children.c
new file mode 100644
index 0000000..84a9474
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_children.c
@@ -0,0 +1,478 @@
+
+ /* $Id: fpm_children.c,v 1.32.2.2 2008/12/13 03:21:18 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "fpm.h"
+#include "fpm_children.h"
+#include "fpm_signals.h"
+#include "fpm_worker_pool.h"
+#include "fpm_sockets.h"
+#include "fpm_process_ctl.h"
+#include "fpm_php.h"
+#include "fpm_conf.h"
+#include "fpm_cleanup.h"
+#include "fpm_events.h"
+#include "fpm_clock.h"
+#include "fpm_stdio.h"
+#include "fpm_unix.h"
+#include "fpm_env.h"
+#include "fpm_scoreboard.h"
+#include "fpm_status.h"
+#include "fpm_log.h"
+
+#include "zlog.h"
+
+static time_t *last_faults;
+static int fault;
+
+static void fpm_children_cleanup(int which, void *arg) /* {{{ */
+{
+ free(last_faults);
+}
+/* }}} */
+
+static struct fpm_child_s *fpm_child_alloc() /* {{{ */
+{
+ struct fpm_child_s *ret;
+
+ ret = malloc(sizeof(struct fpm_child_s));
+
+ if (!ret) {
+ return 0;
+ }
+
+ memset(ret, 0, sizeof(*ret));
+ ret->scoreboard_i = -1;
+ return ret;
+}
+/* }}} */
+
+static void fpm_child_free(struct fpm_child_s *child) /* {{{ */
+{
+ free(child);
+}
+/* }}} */
+
+static void fpm_child_close(struct fpm_child_s *child, int in_event_loop) /* {{{ */
+{
+ if (child->fd_stdout != -1) {
+ if (in_event_loop) {
+ fpm_event_fire(&child->ev_stdout);
+ }
+ if (child->fd_stdout != -1) {
+ close(child->fd_stdout);
+ }
+ }
+
+ if (child->fd_stderr != -1) {
+ if (in_event_loop) {
+ fpm_event_fire(&child->ev_stderr);
+ }
+ if (child->fd_stderr != -1) {
+ close(child->fd_stderr);
+ }
+ }
+
+ fpm_child_free(child);
+}
+/* }}} */
+
+static void fpm_child_link(struct fpm_child_s *child) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp = child->wp;
+
+ ++wp->running_children;
+ ++fpm_globals.running_children;
+
+ child->next = wp->children;
+ if (child->next) {
+ child->next->prev = child;
+ }
+ child->prev = 0;
+ wp->children = child;
+}
+/* }}} */
+
+static void fpm_child_unlink(struct fpm_child_s *child) /* {{{ */
+{
+ --child->wp->running_children;
+ --fpm_globals.running_children;
+
+ if (child->prev) {
+ child->prev->next = child->next;
+ } else {
+ child->wp->children = child->next;
+ }
+
+ if (child->next) {
+ child->next->prev = child->prev;
+ }
+}
+/* }}} */
+
+static struct fpm_child_s *fpm_child_find(pid_t pid) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ struct fpm_child_s *child = 0;
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+
+ for (child = wp->children; child; child = child->next) {
+ if (child->pid == pid) {
+ break;
+ }
+ }
+
+ if (child) break;
+ }
+
+ if (!child) {
+ return 0;
+ }
+
+ return child;
+}
+/* }}} */
+
+static void fpm_child_init(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ fpm_globals.max_requests = wp->config->pm_max_requests;
+
+ if (0 > fpm_stdio_init_child(wp) ||
+ 0 > fpm_log_init_child(wp) ||
+ 0 > fpm_status_init_child(wp) ||
+ 0 > fpm_unix_init_child(wp) ||
+ 0 > fpm_signals_init_child() ||
+ 0 > fpm_env_init_child(wp) ||
+ 0 > fpm_php_init_child(wp)) {
+
+ zlog(ZLOG_ERROR, "[pool %s] child failed to initialize", wp->config->name);
+ exit(FPM_EXIT_SOFTWARE);
+ }
+}
+/* }}} */
+
+int fpm_children_free(struct fpm_child_s *child) /* {{{ */
+{
+ struct fpm_child_s *next;
+
+ for (; child; child = next) {
+ next = child->next;
+ fpm_child_close(child, 0 /* in_event_loop */);
+ }
+
+ return 0;
+}
+/* }}} */
+
+void fpm_children_bury() /* {{{ */
+{
+ int status;
+ pid_t pid;
+ struct fpm_child_s *child;
+
+ while ( (pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
+ char buf[128];
+ int severity = ZLOG_NOTICE;
+ int restart_child = 1;
+
+ child = fpm_child_find(pid);
+
+ if (WIFEXITED(status)) {
+
+ snprintf(buf, sizeof(buf), "with code %d", WEXITSTATUS(status));
+
+ /* if it's been killed because of dynamic process management
+ * don't restart it automaticaly
+ */
+ if (child && child->idle_kill) {
+ restart_child = 0;
+ }
+
+ if (WEXITSTATUS(status) != FPM_EXIT_OK) {
+ severity = ZLOG_WARNING;
+ }
+
+ } else if (WIFSIGNALED(status)) {
+ const char *signame = fpm_signal_names[WTERMSIG(status)];
+ const char *have_core = WCOREDUMP(status) ? " - core dumped" : "";
+
+ if (signame == NULL) {
+ signame = "";
+ }
+
+ snprintf(buf, sizeof(buf), "on signal %d (%s%s)", WTERMSIG(status), signame, have_core);
+
+ /* if it's been killed because of dynamic process management
+ * don't restart it automaticaly
+ */
+ if (child && child->idle_kill && WTERMSIG(status) == SIGQUIT) {
+ restart_child = 0;
+ }
+
+ if (WTERMSIG(status) != SIGQUIT) { /* possible request loss */
+ severity = ZLOG_WARNING;
+ }
+ } else if (WIFSTOPPED(status)) {
+
+ zlog(ZLOG_NOTICE, "child %d stopped for tracing", (int) pid);
+
+ if (child && child->tracer) {
+ child->tracer(child);
+ }
+
+ continue;
+ }
+
+ if (child) {
+ struct fpm_worker_pool_s *wp = child->wp;
+ struct timeval tv1, tv2;
+
+ fpm_child_unlink(child);
+
+ fpm_scoreboard_proc_free(wp->scoreboard, child->scoreboard_i);
+
+ fpm_clock_get(&tv1);
+
+ timersub(&tv1, &child->started, &tv2);
+
+ if (restart_child) {
+ if (!fpm_pctl_can_spawn_children()) {
+ severity = ZLOG_DEBUG;
+ }
+ zlog(severity, "[pool %s] child %d exited %s after %ld.%06d seconds from start", child->wp->config->name, (int) pid, buf, tv2.tv_sec, (int) tv2.tv_usec);
+ } else {
+ zlog(ZLOG_DEBUG, "[pool %s] child %d has been killed by the process managment after %ld.%06d seconds from start", child->wp->config->name, (int) pid, tv2.tv_sec, (int) tv2.tv_usec);
+ }
+
+ fpm_child_close(child, 1 /* in event_loop */);
+
+ fpm_pctl_child_exited();
+
+ if (last_faults && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS)) {
+ time_t now = tv1.tv_sec;
+ int restart_condition = 1;
+ int i;
+
+ last_faults[fault++] = now;
+
+ if (fault == fpm_global_config.emergency_restart_threshold) {
+ fault = 0;
+ }
+
+ for (i = 0; i < fpm_global_config.emergency_restart_threshold; i++) {
+ if (now - last_faults[i] > fpm_global_config.emergency_restart_interval) {
+ restart_condition = 0;
+ break;
+ }
+ }
+
+ if (restart_condition) {
+
+ zlog(ZLOG_WARNING, "failed processes threshold (%d in %d sec) is reached, initiating reload", fpm_global_config.emergency_restart_threshold, fpm_global_config.emergency_restart_interval);
+
+ fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);
+ }
+ }
+
+ if (restart_child) {
+ fpm_children_make(wp, 1 /* in event loop */, 1, 0);
+
+ if (fpm_globals.is_child) {
+ break;
+ }
+ }
+ } else {
+ zlog(ZLOG_ALERT, "oops, unknown child (%d) exited %s. Please open a bug report (https://bugs.php.net).", pid, buf);
+ }
+ }
+}
+/* }}} */
+
+static struct fpm_child_s *fpm_resources_prepare(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct fpm_child_s *c;
+
+ c = fpm_child_alloc();
+
+ if (!c) {
+ zlog(ZLOG_ERROR, "[pool %s] unable to malloc new child", wp->config->name);
+ return 0;
+ }
+
+ c->wp = wp;
+ c->fd_stdout = -1; c->fd_stderr = -1;
+
+ if (0 > fpm_stdio_prepare_pipes(c)) {
+ fpm_child_free(c);
+ return 0;
+ }
+
+ if (0 > fpm_scoreboard_proc_alloc(wp->scoreboard, &c->scoreboard_i)) {
+ fpm_stdio_discard_pipes(c);
+ fpm_child_free(c);
+ return 0;
+ }
+
+ return c;
+}
+/* }}} */
+
+static void fpm_resources_discard(struct fpm_child_s *child) /* {{{ */
+{
+ fpm_scoreboard_proc_free(child->wp->scoreboard, child->scoreboard_i);
+ fpm_stdio_discard_pipes(child);
+ fpm_child_free(child);
+}
+/* }}} */
+
+static void fpm_child_resources_use(struct fpm_child_s *child) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (wp == child->wp) {
+ continue;
+ }
+ fpm_scoreboard_free(wp->scoreboard);
+ }
+
+ fpm_scoreboard_child_use(child->wp->scoreboard, child->scoreboard_i, getpid());
+ fpm_stdio_child_use_pipes(child);
+ fpm_child_free(child);
+}
+/* }}} */
+
+static void fpm_parent_resources_use(struct fpm_child_s *child) /* {{{ */
+{
+ fpm_stdio_parent_use_pipes(child);
+ fpm_child_link(child);
+}
+/* }}} */
+
+int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug) /* {{{ */
+{
+ pid_t pid;
+ struct fpm_child_s *child;
+ int max;
+ static int warned = 0;
+
+ if (wp->config->pm == PM_STYLE_DYNAMIC) {
+ if (!in_event_loop) { /* starting */
+ max = wp->config->pm_start_servers;
+ } else {
+ max = wp->running_children + nb_to_spawn;
+ }
+ } else if (wp->config->pm == PM_STYLE_ONDEMAND) {
+ if (!in_event_loop) { /* starting */
+ max = 0; /* do not create any child at startup */
+ } else {
+ max = wp->running_children + nb_to_spawn;
+ }
+ } else { /* PM_STYLE_STATIC */
+ max = wp->config->pm_max_children;
+ }
+
+ /*
+ * fork children while:
+ * - fpm_pctl_can_spawn_children : FPM is running in a NORMAL state (aka not restart, stop or reload)
+ * - wp->running_children < max : there is less than the max process for the current pool
+ * - (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max):
+ * if fpm_global_config.process_max is set, FPM has not fork this number of processes (globaly)
+ */
+ while (fpm_pctl_can_spawn_children() && wp->running_children < max && (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max)) {
+
+ warned = 0;
+ child = fpm_resources_prepare(wp);
+
+ if (!child) {
+ return 2;
+ }
+
+ pid = fork();
+
+ switch (pid) {
+
+ case 0 :
+ fpm_child_resources_use(child);
+ fpm_globals.is_child = 1;
+ fpm_child_init(wp);
+ return 0;
+
+ case -1 :
+ zlog(ZLOG_SYSERROR, "fork() failed");
+
+ fpm_resources_discard(child);
+ return 2;
+
+ default :
+ child->pid = pid;
+ fpm_clock_get(&child->started);
+ fpm_parent_resources_use(child);
+
+ zlog(is_debug ? ZLOG_DEBUG : ZLOG_NOTICE, "[pool %s] child %d started", wp->config->name, (int) pid);
+ }
+
+ }
+
+ if (!warned && fpm_global_config.process_max > 0 && fpm_globals.running_children >= fpm_global_config.process_max) {
+ warned = 1;
+ zlog(ZLOG_WARNING, "The maximum number of processes has been reached. Please review your configuration and consider raising 'process.max'");
+ }
+
+ return 1; /* we are done */
+}
+/* }}} */
+
+int fpm_children_create_initial(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ if (wp->config->pm == PM_STYLE_ONDEMAND) {
+ wp->ondemand_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s));
+
+ if (!wp->ondemand_event) {
+ zlog(ZLOG_ERROR, "[pool %s] unable to malloc the ondemand socket event", wp->config->name);
+ // FIXME handle crash
+ return 1;
+ }
+
+ memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s));
+ fpm_event_set(wp->ondemand_event, wp->listening_socket, FPM_EV_READ | FPM_EV_EDGE, fpm_pctl_on_socket_accept, wp);
+ wp->socket_event_set = 1;
+ fpm_event_add(wp->ondemand_event, 0);
+
+ return 1;
+ }
+ return fpm_children_make(wp, 0 /* not in event loop yet */, 0, 1);
+}
+/* }}} */
+
+int fpm_children_init_main() /* {{{ */
+{
+ if (fpm_global_config.emergency_restart_threshold &&
+ fpm_global_config.emergency_restart_interval) {
+
+ last_faults = malloc(sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
+
+ if (!last_faults) {
+ return -1;
+ }
+
+ memset(last_faults, 0, sizeof(time_t) * fpm_global_config.emergency_restart_threshold);
+ }
+
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_children_cleanup, 0)) {
+ return -1;
+ }
+
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_children.h b/sapi/fpm/fpm/fpm_children.h
new file mode 100644
index 0000000..9c79f23
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_children.h
@@ -0,0 +1,36 @@
+
+ /* $Id: fpm_children.h,v 1.9 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_CHILDREN_H
+#define FPM_CHILDREN_H 1
+
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include "fpm_worker_pool.h"
+#include "fpm_events.h"
+
+int fpm_children_create_initial(struct fpm_worker_pool_s *wp);
+int fpm_children_free(struct fpm_child_s *child);
+void fpm_children_bury();
+int fpm_children_init_main();
+int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug);
+
+struct fpm_child_s;
+
+struct fpm_child_s {
+ struct fpm_child_s *prev, *next;
+ struct timeval started;
+ struct fpm_worker_pool_s *wp;
+ struct fpm_event_s ev_stdout, ev_stderr;
+ int shm_slot_i;
+ int fd_stdout, fd_stderr;
+ void (*tracer)(struct fpm_child_s *);
+ struct timeval slow_logged;
+ int idle_kill;
+ pid_t pid;
+ int scoreboard_i;
+};
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_cleanup.c b/sapi/fpm/fpm/fpm_cleanup.c
new file mode 100644
index 0000000..260ddb3
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_cleanup.c
@@ -0,0 +1,52 @@
+
+ /* $Id: fpm_cleanup.c,v 1.8 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <stdlib.h>
+
+#include "fpm_arrays.h"
+#include "fpm_cleanup.h"
+
+struct cleanup_s {
+ int type;
+ void (*cleanup)(int, void *);
+ void *arg;
+};
+
+static struct fpm_array_s cleanups = { .sz = sizeof(struct cleanup_s) };
+
+int fpm_cleanup_add(int type, void (*cleanup)(int, void *), void *arg) /* {{{ */
+{
+ struct cleanup_s *c;
+
+ c = fpm_array_push(&cleanups);
+
+ if (!c) {
+ return -1;
+ }
+
+ c->type = type;
+ c->cleanup = cleanup;
+ c->arg = arg;
+
+ return 0;
+}
+/* }}} */
+
+void fpm_cleanups_run(int type) /* {{{ */
+{
+ struct cleanup_s *c = fpm_array_item_last(&cleanups);
+ int cl = cleanups.used;
+
+ for ( ; cl--; c--) {
+ if (c->type & type) {
+ c->cleanup(type, c->arg);
+ }
+ }
+
+ fpm_array_free(&cleanups);
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_cleanup.h b/sapi/fpm/fpm/fpm_cleanup.h
new file mode 100644
index 0000000..4d7cf39
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_cleanup.h
@@ -0,0 +1,21 @@
+
+ /* $Id: fpm_cleanup.h,v 1.5 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_CLEANUP_H
+#define FPM_CLEANUP_H 1
+
+int fpm_cleanup_add(int type, void (*cleanup)(int, void *), void *);
+void fpm_cleanups_run(int type);
+
+enum {
+ FPM_CLEANUP_CHILD = (1 << 0),
+ FPM_CLEANUP_PARENT_EXIT = (1 << 1),
+ FPM_CLEANUP_PARENT_EXIT_MAIN = (1 << 2),
+ FPM_CLEANUP_PARENT_EXEC = (1 << 3),
+ FPM_CLEANUP_PARENT = (1 << 1) | (1 << 2) | (1 << 3),
+ FPM_CLEANUP_ALL = ~0,
+};
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_clock.c b/sapi/fpm/fpm/fpm_clock.c
new file mode 100644
index 0000000..66751b8
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_clock.c
@@ -0,0 +1,121 @@
+
+ /* $Id: fpm_clock.c,v 1.4 2008/09/18 23:19:59 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#if defined(HAVE_CLOCK_GETTIME)
+#include <time.h> /* for CLOCK_MONOTONIC */
+#endif
+
+#include "fpm_clock.h"
+#include "zlog.h"
+
+
+/* posix monotonic clock - preferred source of time */
+#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
+
+static int monotonic_works;
+
+int fpm_clock_init() /* {{{ */
+{
+ struct timespec ts;
+
+ monotonic_works = 0;
+
+ if (0 == clock_gettime(CLOCK_MONOTONIC, &ts)) {
+ monotonic_works = 1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+int fpm_clock_get(struct timeval *tv) /* {{{ */
+{
+ if (monotonic_works) {
+ struct timespec ts;
+
+ if (0 > clock_gettime(CLOCK_MONOTONIC, &ts)) {
+ zlog(ZLOG_SYSERROR, "clock_gettime() failed");
+ return -1;
+ }
+
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / 1000;
+ return 0;
+ }
+
+ return gettimeofday(tv, 0);
+}
+/* }}} */
+
+/* macosx clock */
+#elif defined(HAVE_CLOCK_GET_TIME)
+
+#include <mach/mach.h>
+#include <mach/clock.h>
+#include <mach/mach_error.h>
+
+static clock_serv_t mach_clock;
+
+/* this code borrowed from here: http://lists.apple.com/archives/Darwin-development/2002/Mar/msg00746.html */
+/* mach_clock also should be re-initialized in child process after fork */
+int fpm_clock_init() /* {{{ */
+{
+ kern_return_t ret;
+ mach_timespec_t aTime;
+
+ ret = host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &mach_clock);
+
+ if (ret != KERN_SUCCESS) {
+ zlog(ZLOG_ERROR, "host_get_clock_service() failed: %s", mach_error_string(ret));
+ return -1;
+ }
+
+ /* test if it works */
+ ret = clock_get_time(mach_clock, &aTime);
+
+ if (ret != KERN_SUCCESS) {
+ zlog(ZLOG_ERROR, "clock_get_time() failed: %s", mach_error_string(ret));
+ return -1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+int fpm_clock_get(struct timeval *tv) /* {{{ */
+{
+ kern_return_t ret;
+ mach_timespec_t aTime;
+
+ ret = clock_get_time(mach_clock, &aTime);
+
+ if (ret != KERN_SUCCESS) {
+ zlog(ZLOG_ERROR, "clock_get_time() failed: %s", mach_error_string(ret));
+ return -1;
+ }
+
+ tv->tv_sec = aTime.tv_sec;
+ tv->tv_usec = aTime.tv_nsec / 1000;
+
+ return 0;
+}
+/* }}} */
+
+#else /* no clock */
+
+int fpm_clock_init() /* {{{ */
+{
+ return 0;
+}
+/* }}} */
+
+int fpm_clock_get(struct timeval *tv) /* {{{ */
+{
+ return gettimeofday(tv, 0);
+}
+/* }}} */
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_clock.h b/sapi/fpm/fpm/fpm_clock.h
new file mode 100644
index 0000000..6aab959
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_clock.h
@@ -0,0 +1,13 @@
+
+ /* $Id: fpm_clock.h,v 1.2 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_CLOCK_H
+#define FPM_CLOCK_H 1
+
+#include <sys/time.h>
+
+int fpm_clock_init();
+int fpm_clock_get(struct timeval *tv);
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c
new file mode 100644
index 0000000..25e2cc4
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_conf.c
@@ -0,0 +1,1658 @@
+
+ /* $Id: fpm_conf.c,v 1.33.2.3 2008/12/13 03:50:29 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# include <stdint.h>
+#endif
+#ifdef HAVE_GLOB
+# ifndef PHP_WIN32
+# include <glob.h>
+# else
+# include "win32/glob.h"
+# endif
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "php.h"
+#include "zend_ini_scanner.h"
+#include "zend_globals.h"
+#include "zend_stream.h"
+#include "php_syslog.h"
+
+#include "fpm.h"
+#include "fpm_conf.h"
+#include "fpm_stdio.h"
+#include "fpm_worker_pool.h"
+#include "fpm_cleanup.h"
+#include "fpm_php.h"
+#include "fpm_sockets.h"
+#include "fpm_shm.h"
+#include "fpm_status.h"
+#include "fpm_log.h"
+#include "fpm_events.h"
+#include "zlog.h"
+
+#define STR2STR(a) (a ? a : "undefined")
+#define BOOL2STR(a) (a ? "yes" : "no")
+#define GO(field) offsetof(struct fpm_global_config_s, field)
+#define WPO(field) offsetof(struct fpm_worker_pool_config_s, field)
+
+static int fpm_conf_load_ini_file(char *filename TSRMLS_DC);
+static char *fpm_conf_set_integer(zval *value, void **config, intptr_t offset);
+#if 0 /* not used for now */
+static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset);
+#endif
+static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset);
+static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset);
+static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset);
+static char *fpm_conf_set_log_level(zval *value, void **config, intptr_t offset);
+static char *fpm_conf_set_rlimit_core(zval *value, void **config, intptr_t offset);
+static char *fpm_conf_set_pm(zval *value, void **config, intptr_t offset);
+#ifdef HAVE_SYSLOG_H
+static char *fpm_conf_set_syslog_facility(zval *value, void **config, intptr_t offset);
+#endif
+
+struct fpm_global_config_s fpm_global_config = {
+ .daemonize = 1,
+#ifdef HAVE_SYSLOG_H
+ .syslog_facility = -1,
+#endif
+ .process_max = 0,
+ .process_priority = 64, /* 64 means unset */
+};
+static struct fpm_worker_pool_s *current_wp = NULL;
+static int ini_recursion = 0;
+static char *ini_filename = NULL;
+static int ini_lineno = 0;
+static char *ini_include = NULL;
+
+/*
+ * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
+ */
+static struct ini_value_parser_s ini_fpm_global_options[] = {
+ { "pid", &fpm_conf_set_string, GO(pid_file) },
+ { "error_log", &fpm_conf_set_string, GO(error_log) },
+#ifdef HAVE_SYSLOG_H
+ { "syslog.ident", &fpm_conf_set_string, GO(syslog_ident) },
+ { "syslog.facility", &fpm_conf_set_syslog_facility, GO(syslog_facility) },
+#endif
+ { "log_level", &fpm_conf_set_log_level, GO(log_level) },
+ { "emergency_restart_threshold", &fpm_conf_set_integer, GO(emergency_restart_threshold) },
+ { "emergency_restart_interval", &fpm_conf_set_time, GO(emergency_restart_interval) },
+ { "process_control_timeout", &fpm_conf_set_time, GO(process_control_timeout) },
+ { "process.max", &fpm_conf_set_integer, GO(process_max) },
+ { "process.priority", &fpm_conf_set_integer, GO(process_priority) },
+ { "daemonize", &fpm_conf_set_boolean, GO(daemonize) },
+ { "rlimit_files", &fpm_conf_set_integer, GO(rlimit_files) },
+ { "rlimit_core", &fpm_conf_set_rlimit_core, GO(rlimit_core) },
+ { "events.mechanism", &fpm_conf_set_string, GO(events_mechanism) },
+ { 0, 0, 0 }
+};
+
+/*
+ * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
+ */
+static struct ini_value_parser_s ini_fpm_pool_options[] = {
+ { "prefix", &fpm_conf_set_string, WPO(prefix) },
+ { "user", &fpm_conf_set_string, WPO(user) },
+ { "group", &fpm_conf_set_string, WPO(group) },
+ { "listen", &fpm_conf_set_string, WPO(listen_address) },
+ { "listen.backlog", &fpm_conf_set_integer, WPO(listen_backlog) },
+ { "listen.owner", &fpm_conf_set_string, WPO(listen_owner) },
+ { "listen.group", &fpm_conf_set_string, WPO(listen_group) },
+ { "listen.mode", &fpm_conf_set_string, WPO(listen_mode) },
+ { "listen.allowed_clients", &fpm_conf_set_string, WPO(listen_allowed_clients) },
+ { "process.priority", &fpm_conf_set_integer, WPO(process_priority) },
+ { "pm", &fpm_conf_set_pm, WPO(pm) },
+ { "pm.max_children", &fpm_conf_set_integer, WPO(pm_max_children) },
+ { "pm.start_servers", &fpm_conf_set_integer, WPO(pm_start_servers) },
+ { "pm.min_spare_servers", &fpm_conf_set_integer, WPO(pm_min_spare_servers) },
+ { "pm.max_spare_servers", &fpm_conf_set_integer, WPO(pm_max_spare_servers) },
+ { "pm.process_idle_timeout", &fpm_conf_set_time, WPO(pm_process_idle_timeout) },
+ { "pm.max_requests", &fpm_conf_set_integer, WPO(pm_max_requests) },
+ { "pm.status_path", &fpm_conf_set_string, WPO(pm_status_path) },
+ { "ping.path", &fpm_conf_set_string, WPO(ping_path) },
+ { "ping.response", &fpm_conf_set_string, WPO(ping_response) },
+ { "access.log", &fpm_conf_set_string, WPO(access_log) },
+ { "access.format", &fpm_conf_set_string, WPO(access_format) },
+ { "slowlog", &fpm_conf_set_string, WPO(slowlog) },
+ { "request_slowlog_timeout", &fpm_conf_set_time, WPO(request_slowlog_timeout) },
+ { "request_terminate_timeout", &fpm_conf_set_time, WPO(request_terminate_timeout) },
+ { "rlimit_files", &fpm_conf_set_integer, WPO(rlimit_files) },
+ { "rlimit_core", &fpm_conf_set_rlimit_core, WPO(rlimit_core) },
+ { "chroot", &fpm_conf_set_string, WPO(chroot) },
+ { "chdir", &fpm_conf_set_string, WPO(chdir) },
+ { "catch_workers_output", &fpm_conf_set_boolean, WPO(catch_workers_output) },
+ { "security.limit_extensions", &fpm_conf_set_string, WPO(security_limit_extensions) },
+ { 0, 0, 0 }
+};
+
+static int fpm_conf_is_dir(char *path) /* {{{ */
+{
+ struct stat sb;
+
+ if (stat(path, &sb) != 0) {
+ return 0;
+ }
+
+ return (sb.st_mode & S_IFMT) == S_IFDIR;
+}
+/* }}} */
+
+/*
+ * Expands the '$pool' token in a dynamically allocated string
+ */
+static int fpm_conf_expand_pool_name(char **value) {
+ char *token;
+
+ if (!value || !*value) {
+ return 0;
+ }
+
+ while (*value && (token = strstr(*value, "$pool"))) {
+ char *buf;
+ char *p2 = token + strlen("$pool");
+
+ /* If we are not in a pool, we cannot expand this name now */
+ if (!current_wp || !current_wp->config || !current_wp->config->name) {
+ return -1;
+ }
+
+ /* "aaa$poolbbb" becomes "aaa\0oolbbb" */
+ token[0] = '\0';
+
+ /* Build a brand new string with the expanded token */
+ spprintf(&buf, 0, "%s%s%s", *value, current_wp->config->name, p2);
+
+ /* Free the previous value and save the new one */
+ free(*value);
+ *value = strdup(buf);
+ efree(buf);
+ }
+
+ return 0;
+}
+
+static char *fpm_conf_set_boolean(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ long value_y = !strcasecmp(val, "1");
+ long value_n = !strcasecmp(val, "");
+
+ if (!value_y && !value_n) {
+ return "invalid boolean value";
+ }
+
+ * (int *) ((char *) *config + offset) = value_y ? 1 : 0;
+ return NULL;
+}
+/* }}} */
+
+static char *fpm_conf_set_string(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char **config_val = (char **) ((char *) *config + offset);
+
+ if (!config_val) {
+ return "internal error: NULL value";
+ }
+
+ /* Check if there is a previous value to deallocate */
+ if (*config_val) {
+ free(*config_val);
+ }
+
+ *config_val = strdup(Z_STRVAL_P(value));
+ if (!*config_val) {
+ return "fpm_conf_set_string(): strdup() failed";
+ }
+ if (fpm_conf_expand_pool_name(config_val) == -1) {
+ return "Can't use '$pool' when the pool is not defined";
+ }
+
+ return NULL;
+}
+/* }}} */
+
+static char *fpm_conf_set_integer(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ char *p;
+
+ /* we don't use strtol because we don't want to allow negative values */
+ for (p = val; *p; p++) {
+ if (p == val && *p == '-') continue;
+ if (*p < '0' || *p > '9') {
+ return "is not a valid number (greater or equal than zero)";
+ }
+ }
+ * (int *) ((char *) *config + offset) = atoi(val);
+ return NULL;
+}
+/* }}} */
+
+#if 0 /* not used for now */
+static char *fpm_conf_set_long(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ char *p;
+
+ for (p = val; *p; p++) {
+ if ( p == val && *p == '-' ) continue;
+ if (*p < '0' || *p > '9') {
+ return "is not a valid number (greater or equal than zero)";
+ }
+ }
+ * (long int *) ((char *) *config + offset) = atol(val);
+ return NULL;
+}
+/* }}} */
+#endif
+
+static char *fpm_conf_set_time(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ int len = strlen(val);
+ char suffix;
+ int seconds;
+ if (!len) {
+ return "invalid time value";
+ }
+
+ suffix = val[len-1];
+ switch (suffix) {
+ case 'm' :
+ val[len-1] = '\0';
+ seconds = 60 * atoi(val);
+ break;
+ case 'h' :
+ val[len-1] = '\0';
+ seconds = 60 * 60 * atoi(val);
+ break;
+ case 'd' :
+ val[len-1] = '\0';
+ seconds = 24 * 60 * 60 * atoi(val);
+ break;
+ case 's' : /* s is the default suffix */
+ val[len-1] = '\0';
+ suffix = '0';
+ default :
+ if (suffix < '0' || suffix > '9') {
+ return "unknown suffix used in time value";
+ }
+ seconds = atoi(val);
+ break;
+ }
+
+ * (int *) ((char *) *config + offset) = seconds;
+ return NULL;
+}
+/* }}} */
+
+static char *fpm_conf_set_log_level(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ int log_level;
+
+ if (!strcasecmp(val, "debug")) {
+ log_level = ZLOG_DEBUG;
+ } else if (!strcasecmp(val, "notice")) {
+ log_level = ZLOG_NOTICE;
+ } else if (!strcasecmp(val, "warning") || !strcasecmp(val, "warn")) {
+ log_level = ZLOG_WARNING;
+ } else if (!strcasecmp(val, "error")) {
+ log_level = ZLOG_ERROR;
+ } else if (!strcasecmp(val, "alert")) {
+ log_level = ZLOG_ALERT;
+ } else {
+ return "invalid value for 'log_level'";
+ }
+
+ * (int *) ((char *) *config + offset) = log_level;
+ return NULL;
+}
+/* }}} */
+
+#ifdef HAVE_SYSLOG_H
+static char *fpm_conf_set_syslog_facility(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ int *conf = (int *) ((char *) *config + offset);
+
+#ifdef LOG_AUTH
+ if (!strcasecmp(val, "AUTH")) {
+ *conf = LOG_AUTH;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_AUTHPRIV
+ if (!strcasecmp(val, "AUTHPRIV")) {
+ *conf = LOG_AUTHPRIV;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_CRON
+ if (!strcasecmp(val, "CRON")) {
+ *conf = LOG_CRON;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_DAEMON
+ if (!strcasecmp(val, "DAEMON")) {
+ *conf = LOG_DAEMON;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_FTP
+ if (!strcasecmp(val, "FTP")) {
+ *conf = LOG_FTP;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_KERN
+ if (!strcasecmp(val, "KERN")) {
+ *conf = LOG_KERN;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LPR
+ if (!strcasecmp(val, "LPR")) {
+ *conf = LOG_LPR;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_MAIL
+ if (!strcasecmp(val, "MAIL")) {
+ *conf = LOG_MAIL;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_NEWS
+ if (!strcasecmp(val, "NEWS")) {
+ *conf = LOG_NEWS;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_SYSLOG
+ if (!strcasecmp(val, "SYSLOG")) {
+ *conf = LOG_SYSLOG;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_USER
+ if (!strcasecmp(val, "USER")) {
+ *conf = LOG_USER;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_UUCP
+ if (!strcasecmp(val, "UUCP")) {
+ *conf = LOG_UUCP;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL0
+ if (!strcasecmp(val, "LOCAL0")) {
+ *conf = LOG_LOCAL0;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL1
+ if (!strcasecmp(val, "LOCAL1")) {
+ *conf = LOG_LOCAL1;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL2
+ if (!strcasecmp(val, "LOCAL2")) {
+ *conf = LOG_LOCAL2;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL3
+ if (!strcasecmp(val, "LOCAL3")) {
+ *conf = LOG_LOCAL3;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL4
+ if (!strcasecmp(val, "LOCAL4")) {
+ *conf = LOG_LOCAL4;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL5
+ if (!strcasecmp(val, "LOCAL5")) {
+ *conf = LOG_LOCAL5;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL6
+ if (!strcasecmp(val, "LOCAL6")) {
+ *conf = LOG_LOCAL6;
+ return NULL;
+ }
+#endif
+
+#ifdef LOG_LOCAL7
+ if (!strcasecmp(val, "LOCAL7")) {
+ *conf = LOG_LOCAL7;
+ return NULL;
+ }
+#endif
+
+ return "invalid value";
+}
+/* }}} */
+#endif
+
+static char *fpm_conf_set_rlimit_core(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ int *ptr = (int *) ((char *) *config + offset);
+
+ if (!strcasecmp(val, "unlimited")) {
+ *ptr = -1;
+ } else {
+ int int_value;
+ void *subconf = &int_value;
+ char *error;
+
+ error = fpm_conf_set_integer(value, &subconf, 0);
+
+ if (error) {
+ return error;
+ }
+
+ if (int_value < 0) {
+ return "must be greater than zero or 'unlimited'";
+ }
+
+ *ptr = int_value;
+ }
+
+ return NULL;
+}
+/* }}} */
+
+static char *fpm_conf_set_pm(zval *value, void **config, intptr_t offset) /* {{{ */
+{
+ char *val = Z_STRVAL_P(value);
+ struct fpm_worker_pool_config_s *c = *config;
+ if (!strcasecmp(val, "static")) {
+ c->pm = PM_STYLE_STATIC;
+ } else if (!strcasecmp(val, "dynamic")) {
+ c->pm = PM_STYLE_DYNAMIC;
+ } else if (!strcasecmp(val, "ondemand")) {
+ c->pm = PM_STYLE_ONDEMAND;
+ } else {
+ return "invalid process manager (static, dynamic or ondemand)";
+ }
+ return NULL;
+}
+/* }}} */
+
+static char *fpm_conf_set_array(zval *key, zval *value, void **config, int convert_to_bool) /* {{{ */
+{
+ struct key_value_s *kv;
+ struct key_value_s ***parent = (struct key_value_s ***) config;
+ int b;
+ void *subconf = &b;
+
+ kv = malloc(sizeof(*kv));
+
+ if (!kv) {
+ return "malloc() failed";
+ }
+
+ memset(kv, 0, sizeof(*kv));
+ kv->key = strdup(Z_STRVAL_P(key));
+
+ if (!kv->key) {
+ return "fpm_conf_set_array: strdup(key) failed";
+ }
+
+ if (convert_to_bool) {
+ char *err = fpm_conf_set_boolean(value, &subconf, 0);
+ if (err) return err;
+ kv->value = strdup(b ? "1" : "0");
+ } else {
+ kv->value = strdup(Z_STRVAL_P(value));
+ if (fpm_conf_expand_pool_name(&kv->value) == -1) {
+ return "Can't use '$pool' when the pool is not defined";
+ }
+ }
+
+ if (!kv->value) {
+ free(kv->key);
+ return "fpm_conf_set_array: strdup(value) failed";
+ }
+
+ kv->next = **parent;
+ **parent = kv;
+ return NULL;
+}
+/* }}} */
+
+static void *fpm_worker_pool_config_alloc() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+
+ wp = fpm_worker_pool_alloc();
+
+ if (!wp) {
+ return 0;
+ }
+
+ wp->config = malloc(sizeof(struct fpm_worker_pool_config_s));
+
+ if (!wp->config) {
+ return 0;
+ }
+
+ memset(wp->config, 0, sizeof(struct fpm_worker_pool_config_s));
+ wp->config->listen_backlog = FPM_BACKLOG_DEFAULT;
+ wp->config->pm_process_idle_timeout = 10; /* 10s by default */
+ wp->config->process_priority = 64; /* 64 means unset */
+
+ if (!fpm_worker_all_pools) {
+ fpm_worker_all_pools = wp;
+ } else {
+ struct fpm_worker_pool_s *tmp = fpm_worker_all_pools;
+ while (tmp) {
+ if (!tmp->next) {
+ tmp->next = wp;
+ break;
+ }
+ tmp = tmp->next;
+ }
+ }
+
+ current_wp = wp;
+ return wp->config;
+}
+/* }}} */
+
+int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc) /* {{{ */
+{
+ struct key_value_s *kv, *kv_next;
+
+ free(wpc->name);
+ free(wpc->prefix);
+ free(wpc->user);
+ free(wpc->group);
+ free(wpc->listen_address);
+ free(wpc->listen_owner);
+ free(wpc->listen_group);
+ free(wpc->listen_mode);
+ free(wpc->listen_allowed_clients);
+ free(wpc->pm_status_path);
+ free(wpc->ping_path);
+ free(wpc->ping_response);
+ free(wpc->access_log);
+ free(wpc->access_format);
+ free(wpc->slowlog);
+ free(wpc->chroot);
+ free(wpc->chdir);
+ free(wpc->security_limit_extensions);
+
+ for (kv = wpc->php_values; kv; kv = kv_next) {
+ kv_next = kv->next;
+ free(kv->key);
+ free(kv->value);
+ free(kv);
+ }
+ for (kv = wpc->php_admin_values; kv; kv = kv_next) {
+ kv_next = kv->next;
+ free(kv->key);
+ free(kv->value);
+ free(kv);
+ }
+ for (kv = wpc->env; kv; kv = kv_next) {
+ kv_next = kv->next;
+ free(kv->key);
+ free(kv->value);
+ free(kv);
+ }
+
+ return 0;
+}
+/* }}} */
+
+static int fpm_evaluate_full_path(char **path, struct fpm_worker_pool_s *wp, char *default_prefix, int expand) /* {{{ */
+{
+ char *prefix = NULL;
+ char *full_path;
+
+ if (!path || !*path || **path == '/') {
+ return 0;
+ }
+
+ if (wp && wp->config) {
+ prefix = wp->config->prefix;
+ }
+
+ /* if the wp prefix is not set */
+ if (prefix == NULL) {
+ prefix = fpm_globals.prefix;
+ }
+
+ /* if the global prefix is not set */
+ if (prefix == NULL) {
+ prefix = default_prefix ? default_prefix : PHP_PREFIX;
+ }
+
+ if (expand) {
+ char *tmp;
+ tmp = strstr(*path, "$prefix");
+ if (tmp != NULL) {
+
+ if (tmp != *path) {
+ zlog(ZLOG_ERROR, "'$prefix' must be use at the begining of the value");
+ return -1;
+ }
+
+ if (strlen(*path) > strlen("$prefix")) {
+ free(*path);
+ tmp = strdup((*path) + strlen("$prefix"));
+ *path = tmp;
+ } else {
+ free(*path);
+ *path = NULL;
+ }
+ }
+ }
+
+ if (*path) {
+ spprintf(&full_path, 0, "%s/%s", prefix, *path);
+ free(*path);
+ *path = strdup(full_path);
+ efree(full_path);
+ } else {
+ *path = strdup(prefix);
+ }
+
+ if (**path != '/' && wp != NULL && wp->config) {
+ return fpm_evaluate_full_path(path, NULL, default_prefix, expand);
+ }
+ return 0;
+}
+/* }}} */
+
+static int fpm_conf_process_all_pools() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp, *wp2;
+
+ if (!fpm_worker_all_pools) {
+ zlog(ZLOG_ERROR, "No pool defined. at least one pool section must be specified in config file");
+ return -1;
+ }
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+
+ /* prefix */
+ if (wp->config->prefix && *wp->config->prefix) {
+ fpm_evaluate_full_path(&wp->config->prefix, NULL, NULL, 0);
+
+ if (!fpm_conf_is_dir(wp->config->prefix)) {
+ zlog(ZLOG_ERROR, "[pool %s] the prefix '%s' does not exist or is not a directory", wp->config->name, wp->config->prefix);
+ return -1;
+ }
+ }
+
+ /* alert if user is not set only if we are not root*/
+ if (!wp->config->user && !geteuid()) {
+ zlog(ZLOG_ALERT, "[pool %s] user has not been defined", wp->config->name);
+ return -1;
+ }
+
+ /* listen */
+ if (wp->config->listen_address && *wp->config->listen_address) {
+ wp->listen_address_domain = fpm_sockets_domain_from_address(wp->config->listen_address);
+
+ if (wp->listen_address_domain == FPM_AF_UNIX && *wp->config->listen_address != '/') {
+ fpm_evaluate_full_path(&wp->config->listen_address, wp, NULL, 0);
+ }
+ } else {
+ zlog(ZLOG_ALERT, "[pool %s] no listen address have been defined!", wp->config->name);
+ return -1;
+ }
+
+ if (wp->config->process_priority != 64 && (wp->config->process_priority < -19 || wp->config->process_priority > 20)) {
+ zlog(ZLOG_ERROR, "[pool %s] process.priority must be included into [-19,20]", wp->config->name);
+ return -1;
+ }
+
+ /* pm */
+ if (wp->config->pm != PM_STYLE_STATIC && wp->config->pm != PM_STYLE_DYNAMIC && wp->config->pm != PM_STYLE_ONDEMAND) {
+ zlog(ZLOG_ALERT, "[pool %s] the process manager is missing (static, dynamic or ondemand)", wp->config->name);
+ return -1;
+ }
+
+ /* pm.max_children */
+ if (wp->config->pm_max_children < 1) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.max_children must be a positive value", wp->config->name);
+ return -1;
+ }
+
+ /* pm.start_servers, pm.min_spare_servers, pm.max_spare_servers */
+ if (wp->config->pm == PM_STYLE_DYNAMIC) {
+ struct fpm_worker_pool_config_s *config = wp->config;
+
+ if (config->pm_min_spare_servers <= 0) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.min_spare_servers(%d) must be a positive value", wp->config->name, config->pm_min_spare_servers);
+ return -1;
+ }
+
+ if (config->pm_max_spare_servers <= 0) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.max_spare_servers(%d) must be a positive value", wp->config->name, config->pm_max_spare_servers);
+ return -1;
+ }
+
+ if (config->pm_min_spare_servers > config->pm_max_children ||
+ config->pm_max_spare_servers > config->pm_max_children) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.min_spare_servers(%d) and pm.max_spare_servers(%d) cannot be greater than pm.max_children(%d)", wp->config->name, config->pm_min_spare_servers, config->pm_max_spare_servers, config->pm_max_children);
+ return -1;
+ }
+
+ if (config->pm_max_spare_servers < config->pm_min_spare_servers) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.max_spare_servers(%d) must not be less than pm.min_spare_servers(%d)", wp->config->name, config->pm_max_spare_servers, config->pm_min_spare_servers);
+ return -1;
+ }
+
+ if (config->pm_start_servers <= 0) {
+ config->pm_start_servers = config->pm_min_spare_servers + ((config->pm_max_spare_servers - config->pm_min_spare_servers) / 2);
+ zlog(ZLOG_WARNING, "[pool %s] pm.start_servers is not set. It's been set to %d.", wp->config->name, config->pm_start_servers);
+
+ } else if (config->pm_start_servers < config->pm_min_spare_servers || config->pm_start_servers > config->pm_max_spare_servers) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.start_servers(%d) must not be less than pm.min_spare_servers(%d) and not greater than pm.max_spare_servers(%d)", wp->config->name, config->pm_start_servers, config->pm_min_spare_servers, config->pm_max_spare_servers);
+ return -1;
+ }
+ } else if (wp->config->pm == PM_STYLE_ONDEMAND) {
+ struct fpm_worker_pool_config_s *config = wp->config;
+
+ if (!fpm_event_support_edge_trigger()) {
+ zlog(ZLOG_ALERT, "[pool %s] ondemand process manager can ONLY be used when events.mechanisme is either epoll (Linux) or kqueue (*BSD).", wp->config->name);
+ return -1;
+ }
+
+ if (config->pm_process_idle_timeout < 1) {
+ zlog(ZLOG_ALERT, "[pool %s] pm.process_idle_timeout(%ds) must be greater than 0s", wp->config->name, config->pm_process_idle_timeout);
+ return -1;
+ }
+
+ if (config->listen_backlog < FPM_BACKLOG_DEFAULT) {
+ zlog(ZLOG_WARNING, "[pool %s] listen.backlog(%d) was too low for the ondemand process manager. I updated it for you to %d.", wp->config->name, config->listen_backlog, FPM_BACKLOG_DEFAULT);
+ config->listen_backlog = FPM_BACKLOG_DEFAULT;
+ }
+
+ /* certainely useless but proper */
+ config->pm_start_servers = 0;
+ config->pm_min_spare_servers = 0;
+ config->pm_max_spare_servers = 0;
+ }
+
+ /* status */
+ if (wp->config->pm_status_path && *wp->config->pm_status_path) {
+ int i;
+ char *status = wp->config->pm_status_path;
+
+ if (*status != '/') {
+ zlog(ZLOG_ERROR, "[pool %s] the status path '%s' must start with a '/'", wp->config->name, status);
+ return -1;
+ }
+
+ if (strlen(status) < 2) {
+ zlog(ZLOG_ERROR, "[pool %s] the status path '%s' is not long enough", wp->config->name, status);
+ return -1;
+ }
+
+ for (i = 0; i < strlen(status); i++) {
+ if (!isalnum(status[i]) && status[i] != '/' && status[i] != '-' && status[i] != '_' && status[i] != '.') {
+ zlog(ZLOG_ERROR, "[pool %s] the status path '%s' must contain only the following characters '[alphanum]/_-.'", wp->config->name, status);
+ return -1;
+ }
+ }
+ }
+
+ /* ping */
+ if (wp->config->ping_path && *wp->config->ping_path) {
+ char *ping = wp->config->ping_path;
+ int i;
+
+ if (*ping != '/') {
+ zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' must start with a '/'", wp->config->name, ping);
+ return -1;
+ }
+
+ if (strlen(ping) < 2) {
+ zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' is not long enough", wp->config->name, ping);
+ return -1;
+ }
+
+ for (i = 0; i < strlen(ping); i++) {
+ if (!isalnum(ping[i]) && ping[i] != '/' && ping[i] != '-' && ping[i] != '_' && ping[i] != '.') {
+ zlog(ZLOG_ERROR, "[pool %s] the ping path '%s' must containt only the following characters '[alphanum]/_-.'", wp->config->name, ping);
+ return -1;
+ }
+ }
+
+ if (!wp->config->ping_response) {
+ wp->config->ping_response = strdup("pong");
+ } else {
+ if (strlen(wp->config->ping_response) < 1) {
+ zlog(ZLOG_ERROR, "[pool %s] the ping response page '%s' is not long enough", wp->config->name, wp->config->ping_response);
+ return -1;
+ }
+ }
+ } else {
+ if (wp->config->ping_response) {
+ free(wp->config->ping_response);
+ wp->config->ping_response = NULL;
+ }
+ }
+
+ /* access.log, access.format */
+ if (wp->config->access_log && *wp->config->access_log) {
+ fpm_evaluate_full_path(&wp->config->access_log, wp, NULL, 0);
+ if (!wp->config->access_format) {
+ wp->config->access_format = strdup("%R - %u %t \"%m %r\" %s");
+ }
+ }
+
+ if (wp->config->request_terminate_timeout) {
+ fpm_globals.heartbeat = fpm_globals.heartbeat ? MIN(fpm_globals.heartbeat, (wp->config->request_terminate_timeout * 1000) / 3) : (wp->config->request_terminate_timeout * 1000) / 3;
+ }
+
+ /* slowlog */
+ if (wp->config->slowlog && *wp->config->slowlog) {
+ fpm_evaluate_full_path(&wp->config->slowlog, wp, NULL, 0);
+ }
+
+ /* request_slowlog_timeout */
+ if (wp->config->request_slowlog_timeout) {
+#if HAVE_FPM_TRACE
+ if (! (wp->config->slowlog && *wp->config->slowlog)) {
+ zlog(ZLOG_ERROR, "[pool %s] 'slowlog' must be specified for use with 'request_slowlog_timeout'", wp->config->name);
+ return -1;
+ }
+#else
+ static int warned = 0;
+
+ if (!warned) {
+ zlog(ZLOG_WARNING, "[pool %s] 'request_slowlog_timeout' is not supported on your system", wp->config->name);
+ warned = 1;
+ }
+
+ wp->config->request_slowlog_timeout = 0;
+#endif
+
+ if (wp->config->slowlog && *wp->config->slowlog) {
+ int fd;
+
+ fd = open(wp->config->slowlog, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+
+ if (0 > fd) {
+ zlog(ZLOG_SYSERROR, "Unable to create or open slowlog(%s)", wp->config->slowlog);
+ return -1;
+ }
+ close(fd);
+ }
+
+ fpm_globals.heartbeat = fpm_globals.heartbeat ? MIN(fpm_globals.heartbeat, (wp->config->request_slowlog_timeout * 1000) / 3) : (wp->config->request_slowlog_timeout * 1000) / 3;
+
+ if (wp->config->request_terminate_timeout && wp->config->request_slowlog_timeout > wp->config->request_terminate_timeout) {
+ zlog(ZLOG_ERROR, "[pool %s] 'request_slowlog_timeout' (%d) can't be greater than 'request_terminate_timeout' (%d)", wp->config->name, wp->config->request_slowlog_timeout, wp->config->request_terminate_timeout);
+ return -1;
+ }
+ }
+
+ /* chroot */
+ if (wp->config->chroot && *wp->config->chroot) {
+
+ fpm_evaluate_full_path(&wp->config->chroot, wp, NULL, 1);
+
+ if (*wp->config->chroot != '/') {
+ zlog(ZLOG_ERROR, "[pool %s] the chroot path '%s' must start with a '/'", wp->config->name, wp->config->chroot);
+ return -1;
+ }
+
+ if (!fpm_conf_is_dir(wp->config->chroot)) {
+ zlog(ZLOG_ERROR, "[pool %s] the chroot path '%s' does not exist or is not a directory", wp->config->name, wp->config->chroot);
+ return -1;
+ }
+ }
+
+ /* chdir */
+ if (wp->config->chdir && *wp->config->chdir) {
+
+ fpm_evaluate_full_path(&wp->config->chdir, wp, NULL, 0);
+
+ if (*wp->config->chdir != '/') {
+ zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' must start with a '/'", wp->config->name, wp->config->chdir);
+ return -1;
+ }
+
+ if (wp->config->chroot) {
+ char *buf;
+
+ spprintf(&buf, 0, "%s/%s", wp->config->chroot, wp->config->chdir);
+
+ if (!fpm_conf_is_dir(buf)) {
+ zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' within the chroot path '%s' ('%s') does not exist or is not a directory", wp->config->name, wp->config->chdir, wp->config->chroot, buf);
+ efree(buf);
+ return -1;
+ }
+
+ efree(buf);
+ } else {
+ if (!fpm_conf_is_dir(wp->config->chdir)) {
+ zlog(ZLOG_ERROR, "[pool %s] the chdir path '%s' does not exist or is not a directory", wp->config->name, wp->config->chdir);
+ return -1;
+ }
+ }
+ }
+
+ /* security.limit_extensions */
+ if (!wp->config->security_limit_extensions) {
+ wp->config->security_limit_extensions = strdup(".php .phar");
+ }
+
+ if (*wp->config->security_limit_extensions) {
+ int nb_ext;
+ char *ext;
+ char *security_limit_extensions;
+ char *limit_extensions;
+
+
+ /* strdup because strtok(3) alters the string it parses */
+ security_limit_extensions = strdup(wp->config->security_limit_extensions);
+ limit_extensions = security_limit_extensions;
+ nb_ext = 0;
+
+ /* find the number of extensions */
+ while ((ext = strtok(limit_extensions, " \t"))) {
+ limit_extensions = NULL;
+ nb_ext++;
+ }
+ free(security_limit_extensions);
+
+ /* if something found */
+ if (nb_ext > 0) {
+
+ /* malloc the extension array */
+ wp->limit_extensions = malloc(sizeof(char *) * (nb_ext + 1));
+ if (!wp->limit_extensions) {
+ zlog(ZLOG_ERROR, "[pool %s] unable to malloc extensions array", wp->config->name);
+ return -1;
+ }
+
+ /* strdup because strtok(3) alters the string it parses */
+ security_limit_extensions = strdup(wp->config->security_limit_extensions);
+ limit_extensions = security_limit_extensions;
+ nb_ext = 0;
+
+ /* parse the string and save the extension in the array */
+ while ((ext = strtok(security_limit_extensions, " \t"))) {
+ security_limit_extensions = NULL;
+ wp->limit_extensions[nb_ext++] = strdup(ext);
+ }
+
+ /* end the array with NULL in order to parse it */
+ wp->limit_extensions[nb_ext] = NULL;
+ free(security_limit_extensions);
+ }
+ }
+
+ /* env[], php_value[], php_admin_values[] */
+ if (!wp->config->chroot) {
+ struct key_value_s *kv;
+ char *options[] = FPM_PHP_INI_TO_EXPAND;
+ char **p;
+
+ for (kv = wp->config->php_values; kv; kv = kv->next) {
+ for (p = options; *p; p++) {
+ if (!strcasecmp(kv->key, *p)) {
+ fpm_evaluate_full_path(&kv->value, wp, NULL, 0);
+ }
+ }
+ }
+ for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
+ for (p = options; *p; p++) {
+ if (!strcasecmp(kv->key, *p)) {
+ fpm_evaluate_full_path(&kv->value, wp, NULL, 0);
+ }
+ }
+ }
+ }
+ }
+
+ /* ensure 2 pools do not use the same listening address */
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ for (wp2 = fpm_worker_all_pools; wp2; wp2 = wp2->next) {
+ if (wp == wp2) {
+ continue;
+ }
+
+ if (wp->config->listen_address && *wp->config->listen_address && wp2->config->listen_address && *wp2->config->listen_address && !strcmp(wp->config->listen_address, wp2->config->listen_address)) {
+ zlog(ZLOG_ERROR, "[pool %s] unable to set listen address as it's already used in another pool '%s'", wp2->config->name, wp->config->name);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_conf_unlink_pid() /* {{{ */
+{
+ if (fpm_global_config.pid_file) {
+ if (0 > unlink(fpm_global_config.pid_file)) {
+ zlog(ZLOG_SYSERROR, "Unable to remove the PID file (%s).", fpm_global_config.pid_file);
+ return -1;
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_conf_write_pid() /* {{{ */
+{
+ int fd;
+
+ if (fpm_global_config.pid_file) {
+ char buf[64];
+ int len;
+
+ unlink(fpm_global_config.pid_file);
+ fd = creat(fpm_global_config.pid_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ if (fd < 0) {
+ zlog(ZLOG_SYSERROR, "Unable to create the PID file (%s).", fpm_global_config.pid_file);
+ return -1;
+ }
+
+ len = sprintf(buf, "%d", (int) fpm_globals.parent_pid);
+
+ if (len != write(fd, buf, len)) {
+ zlog(ZLOG_SYSERROR, "Unable to write to the PID file.");
+ return -1;
+ }
+ close(fd);
+ }
+ return 0;
+}
+/* }}} */
+
+static int fpm_conf_post_process(int force_daemon TSRMLS_DC) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+
+ if (fpm_global_config.pid_file) {
+ fpm_evaluate_full_path(&fpm_global_config.pid_file, NULL, PHP_LOCALSTATEDIR, 0);
+ }
+
+ if (force_daemon >= 0) {
+ /* forced from command line options */
+ fpm_global_config.daemonize = force_daemon;
+ }
+
+ fpm_globals.log_level = fpm_global_config.log_level;
+
+ if (fpm_global_config.process_max < 0) {
+ zlog(ZLOG_ERROR, "process_max can't be negative");
+ return -1;
+ }
+
+ if (fpm_global_config.process_priority != 64 && (fpm_global_config.process_priority < -19 || fpm_global_config.process_priority > 20)) {
+ zlog(ZLOG_ERROR, "process.priority must be included into [-19,20]");
+ return -1;
+ }
+
+ if (!fpm_global_config.error_log) {
+ fpm_global_config.error_log = strdup("log/php-fpm.log");
+ }
+
+#ifdef HAVE_SYSLOG_H
+ if (!fpm_global_config.syslog_ident) {
+ fpm_global_config.syslog_ident = strdup("php-fpm");
+ }
+
+ if (fpm_global_config.syslog_facility < 0) {
+ fpm_global_config.syslog_facility = LOG_DAEMON;
+ }
+
+ if (strcasecmp(fpm_global_config.error_log, "syslog") != 0)
+#endif
+ {
+ fpm_evaluate_full_path(&fpm_global_config.error_log, NULL, PHP_LOCALSTATEDIR, 0);
+ }
+
+ if (0 > fpm_stdio_open_error_log(0)) {
+ return -1;
+ }
+
+ if (0 > fpm_log_open(0)) {
+ return -1;
+ }
+
+ if (0 > fpm_event_pre_init(fpm_global_config.events_mechanism)) {
+ return -1;
+ }
+
+ if (0 > fpm_conf_process_all_pools()) {
+ return -1;
+ }
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (!wp->config->access_log || !*wp->config->access_log) {
+ continue;
+ }
+ if (0 > fpm_log_write(wp->config->access_format TSRMLS_CC)) {
+ zlog(ZLOG_ERROR, "[pool %s] wrong format for access.format '%s'", wp->config->name, wp->config->access_format);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+
+static void fpm_conf_cleanup(int which, void *arg) /* {{{ */
+{
+ free(fpm_global_config.pid_file);
+ free(fpm_global_config.error_log);
+ free(fpm_global_config.events_mechanism);
+ fpm_global_config.pid_file = 0;
+ fpm_global_config.error_log = 0;
+#ifdef HAVE_SYSLOG_H
+ free(fpm_global_config.syslog_ident);
+ fpm_global_config.syslog_ident = 0;
+#endif
+ free(fpm_globals.config);
+}
+/* }}} */
+
+static void fpm_conf_ini_parser_include(char *inc, void *arg TSRMLS_DC) /* {{{ */
+{
+ char *filename;
+ int *error = (int *)arg;;
+#ifdef HAVE_GLOB
+ glob_t g;
+#endif
+ int i;
+
+ if (!inc || !arg) return;
+ if (*error) return; /* We got already an error. Switch to the end. */
+ spprintf(&filename, 0, "%s", ini_filename);
+
+#ifdef HAVE_GLOB
+ {
+ g.gl_offs = 0;
+ if ((i = glob(inc, GLOB_ERR | GLOB_MARK | GLOB_NOSORT, NULL, &g)) != 0) {
+#ifdef GLOB_NOMATCH
+ if (i == GLOB_NOMATCH) {
+ zlog(ZLOG_WARNING, "Nothing matches the include pattern '%s' from %s at line %d.", inc, filename, ini_lineno);
+ efree(filename);
+ return;
+ }
+#endif /* GLOB_NOMATCH */
+ zlog(ZLOG_ERROR, "Unable to globalize '%s' (ret=%d) from %s at line %d.", inc, i, filename, ini_lineno);
+ *error = 1;
+ efree(filename);
+ return;
+ }
+
+ for (i = 0; i < g.gl_pathc; i++) {
+ int len = strlen(g.gl_pathv[i]);
+ if (len < 1) continue;
+ if (g.gl_pathv[i][len - 1] == '/') continue; /* don't parse directories */
+ if (0 > fpm_conf_load_ini_file(g.gl_pathv[i] TSRMLS_CC)) {
+ zlog(ZLOG_ERROR, "Unable to include %s from %s at line %d", g.gl_pathv[i], filename, ini_lineno);
+ *error = 1;
+ efree(filename);
+ return;
+ }
+ }
+ globfree(&g);
+ }
+#else /* HAVE_GLOB */
+ if (0 > fpm_conf_load_ini_file(inc TSRMLS_CC)) {
+ zlog(ZLOG_ERROR, "Unable to include %s from %s at line %d", inc, filename, ini_lineno);
+ *error = 1;
+ efree(filename);
+ return;
+ }
+#endif /* HAVE_GLOB */
+
+ efree(filename);
+}
+/* }}} */
+
+static void fpm_conf_ini_parser_section(zval *section, void *arg TSRMLS_DC) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ struct fpm_worker_pool_config_s *config;
+ int *error = (int *)arg;
+
+ /* switch to global conf */
+ if (!strcasecmp(Z_STRVAL_P(section), "global")) {
+ current_wp = NULL;
+ return;
+ }
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (!wp->config) continue;
+ if (!wp->config->name) continue;
+ if (!strcasecmp(wp->config->name, Z_STRVAL_P(section))) {
+ /* Found a wp with the same name. Bring it back */
+ current_wp = wp;
+ return;
+ }
+ }
+
+ /* it's a new pool */
+ config = (struct fpm_worker_pool_config_s *)fpm_worker_pool_config_alloc();
+ if (!current_wp || !config) {
+ zlog(ZLOG_ERROR, "[%s:%d] Unable to alloc a new WorkerPool for worker '%s'", ini_filename, ini_lineno, Z_STRVAL_P(section));
+ *error = 1;
+ return;
+ }
+ config->name = strdup(Z_STRVAL_P(section));
+ if (!config->name) {
+ zlog(ZLOG_ERROR, "[%s:%d] Unable to alloc memory for configuration name for worker '%s'", ini_filename, ini_lineno, Z_STRVAL_P(section));
+ *error = 1;
+ return;
+ }
+}
+/* }}} */
+
+static void fpm_conf_ini_parser_entry(zval *name, zval *value, void *arg TSRMLS_DC) /* {{{ */
+{
+ struct ini_value_parser_s *parser;
+ void *config = NULL;
+
+ int *error = (int *)arg;
+ if (!value) {
+ zlog(ZLOG_ERROR, "[%s:%d] value is NULL for a ZEND_INI_PARSER_ENTRY", ini_filename, ini_lineno);
+ *error = 1;
+ return;
+ }
+
+ if (!strcmp(Z_STRVAL_P(name), "include")) {
+ if (ini_include) {
+ zlog(ZLOG_ERROR, "[%s:%d] two includes at the same time !", ini_filename, ini_lineno);
+ *error = 1;
+ return;
+ }
+ ini_include = strdup(Z_STRVAL_P(value));
+ return;
+ }
+
+ if (!current_wp) { /* we are in the global section */
+ parser = ini_fpm_global_options;
+ config = &fpm_global_config;
+ } else {
+ parser = ini_fpm_pool_options;
+ config = current_wp->config;
+ }
+
+ for (; parser->name; parser++) {
+ if (!strcasecmp(parser->name, Z_STRVAL_P(name))) {
+ char *ret;
+ if (!parser->parser) {
+ zlog(ZLOG_ERROR, "[%s:%d] the parser for entry '%s' is not defined", ini_filename, ini_lineno, parser->name);
+ *error = 1;
+ return;
+ }
+
+ ret = parser->parser(value, &config, parser->offset);
+ if (ret) {
+ zlog(ZLOG_ERROR, "[%s:%d] unable to parse value for entry '%s': %s", ini_filename, ini_lineno, parser->name, ret);
+ *error = 1;
+ return;
+ }
+
+ /* all is good ! */
+ return;
+ }
+ }
+
+ /* nothing has been found if we got here */
+ zlog(ZLOG_ERROR, "[%s:%d] unknown entry '%s'", ini_filename, ini_lineno, Z_STRVAL_P(name));
+ *error = 1;
+}
+/* }}} */
+
+static void fpm_conf_ini_parser_array(zval *name, zval *key, zval *value, void *arg TSRMLS_DC) /* {{{ */
+{
+ int *error = (int *)arg;
+ char *err = NULL;
+ void *config;
+
+ if (!Z_STRVAL_P(key) || !Z_STRVAL_P(value) || !*Z_STRVAL_P(key)) {
+ zlog(ZLOG_ERROR, "[%s:%d] Misspelled array ?", ini_filename, ini_lineno);
+ *error = 1;
+ return;
+ }
+ if (!current_wp || !current_wp->config) {
+ zlog(ZLOG_ERROR, "[%s:%d] Array are not allowed in the global section", ini_filename, ini_lineno);
+ *error = 1;
+ return;
+ }
+
+ if (!strcmp("env", Z_STRVAL_P(name))) {
+ if (!*Z_STRVAL_P(value)) {
+ zlog(ZLOG_ERROR, "[%s:%d] empty value", ini_filename, ini_lineno);
+ *error = 1;
+ return;
+ }
+ config = (char *)current_wp->config + WPO(env);
+ err = fpm_conf_set_array(key, value, &config, 0);
+
+ } else if (!strcmp("php_value", Z_STRVAL_P(name))) {
+ config = (char *)current_wp->config + WPO(php_values);
+ err = fpm_conf_set_array(key, value, &config, 0);
+
+ } else if (!strcmp("php_admin_value", Z_STRVAL_P(name))) {
+ config = (char *)current_wp->config + WPO(php_admin_values);
+ err = fpm_conf_set_array(key, value, &config, 0);
+
+ } else if (!strcmp("php_flag", Z_STRVAL_P(name))) {
+ config = (char *)current_wp->config + WPO(php_values);
+ err = fpm_conf_set_array(key, value, &config, 1);
+
+ } else if (!strcmp("php_admin_flag", Z_STRVAL_P(name))) {
+ config = (char *)current_wp->config + WPO(php_admin_values);
+ err = fpm_conf_set_array(key, value, &config, 1);
+
+ } else {
+ zlog(ZLOG_ERROR, "[%s:%d] unknown directive '%s'", ini_filename, ini_lineno, Z_STRVAL_P(name));
+ *error = 1;
+ return;
+ }
+
+ if (err) {
+ zlog(ZLOG_ERROR, "[%s:%d] error while parsing '%s[%s]' : %s", ini_filename, ini_lineno, Z_STRVAL_P(name), Z_STRVAL_P(key), err);
+ *error = 1;
+ return;
+ }
+}
+/* }}} */
+
+static void fpm_conf_ini_parser(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg TSRMLS_DC) /* {{{ */
+{
+ int *error;
+
+ if (!arg1 || !arg) return;
+ error = (int *)arg;
+ if (*error) return; /* We got already an error. Switch to the end. */
+
+ switch(callback_type) {
+ case ZEND_INI_PARSER_ENTRY:
+ fpm_conf_ini_parser_entry(arg1, arg2, error TSRMLS_CC);
+ break;;
+ case ZEND_INI_PARSER_SECTION:
+ fpm_conf_ini_parser_section(arg1, error TSRMLS_CC);
+ break;;
+ case ZEND_INI_PARSER_POP_ENTRY:
+ fpm_conf_ini_parser_array(arg1, arg3, arg2, error TSRMLS_CC);
+ break;;
+ default:
+ zlog(ZLOG_ERROR, "[%s:%d] Unknown INI syntax", ini_filename, ini_lineno);
+ *error = 1;
+ break;;
+ }
+}
+/* }}} */
+
+int fpm_conf_load_ini_file(char *filename TSRMLS_DC) /* {{{ */
+{
+ int error = 0;
+ char buf[1024+1];
+ int fd, n;
+ int nb_read = 1;
+ char c = '*';
+
+ int ret = 1;
+
+ if (!filename || !filename[0]) {
+ zlog(ZLOG_ERROR, "configuration filename is empty");
+ return -1;
+ }
+
+ fd = open(filename, O_RDONLY, 0);
+ if (fd < 0) {
+ zlog(ZLOG_SYSERROR, "failed to open configuration file '%s'", filename);
+ return -1;
+ }
+
+ if (ini_recursion++ > 4) {
+ zlog(ZLOG_ERROR, "failed to include more than 5 files recusively");
+ return -1;
+ }
+
+ ini_lineno = 0;
+ while (nb_read > 0) {
+ int tmp;
+ memset(buf, 0, sizeof(char) * (1024 + 1));
+ for (n = 0; n < 1024 && (nb_read = read(fd, &c, sizeof(char))) == sizeof(char) && c != '\n'; n++) {
+ buf[n] = c;
+ }
+ buf[n++] = '\n';
+ ini_lineno++;
+ ini_filename = filename;
+ tmp = zend_parse_ini_string(buf, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t)fpm_conf_ini_parser, &error TSRMLS_CC);
+ ini_filename = filename;
+ if (error || tmp == FAILURE) {
+ if (ini_include) free(ini_include);
+ ini_recursion--;
+ close(fd);
+ return -1;
+ }
+ if (ini_include) {
+ char *tmp = ini_include;
+ ini_include = NULL;
+ fpm_evaluate_full_path(&tmp, NULL, NULL, 0);
+ fpm_conf_ini_parser_include(tmp, &error TSRMLS_CC);
+ if (error) {
+ free(tmp);
+ ini_recursion--;
+ close(fd);
+ return -1;
+ }
+ free(tmp);
+ }
+ }
+
+ ini_recursion--;
+ close(fd);
+ return ret;
+
+}
+/* }}} */
+
+static void fpm_conf_dump() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+
+ /*
+ * Please keep the same order as in fpm_conf.h and in php-fpm.conf.in
+ */
+ zlog(ZLOG_NOTICE, "[General]");
+ zlog(ZLOG_NOTICE, "\tpid = %s", STR2STR(fpm_global_config.pid_file));
+ zlog(ZLOG_NOTICE, "\terror_log = %s", STR2STR(fpm_global_config.error_log));
+#ifdef HAVE_SYSLOG_H
+ zlog(ZLOG_NOTICE, "\tsyslog.ident = %s", STR2STR(fpm_global_config.syslog_ident));
+ zlog(ZLOG_NOTICE, "\tsyslog.facility = %d", fpm_global_config.syslog_facility); /* FIXME: convert to string */
+#endif
+ zlog(ZLOG_NOTICE, "\tlog_level = %s", zlog_get_level_name(fpm_globals.log_level));
+ zlog(ZLOG_NOTICE, "\temergency_restart_interval = %ds", fpm_global_config.emergency_restart_interval);
+ zlog(ZLOG_NOTICE, "\temergency_restart_threshold = %d", fpm_global_config.emergency_restart_threshold);
+ zlog(ZLOG_NOTICE, "\tprocess_control_timeout = %ds", fpm_global_config.process_control_timeout);
+ zlog(ZLOG_NOTICE, "\tprocess.max = %d", fpm_global_config.process_max);
+ if (fpm_global_config.process_priority == 64) {
+ zlog(ZLOG_NOTICE, "\tprocess.priority = undefined");
+ } else {
+ zlog(ZLOG_NOTICE, "\tprocess.priority = %d", fpm_global_config.process_priority);
+ }
+ zlog(ZLOG_NOTICE, "\tdaemonize = %s", BOOL2STR(fpm_global_config.daemonize));
+ zlog(ZLOG_NOTICE, "\trlimit_files = %d", fpm_global_config.rlimit_files);
+ zlog(ZLOG_NOTICE, "\trlimit_core = %d", fpm_global_config.rlimit_core);
+ zlog(ZLOG_NOTICE, "\tevents.mechanism = %s", fpm_event_machanism_name());
+ zlog(ZLOG_NOTICE, " ");
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ struct key_value_s *kv;
+ if (!wp->config) continue;
+ zlog(ZLOG_NOTICE, "[%s]", STR2STR(wp->config->name));
+ zlog(ZLOG_NOTICE, "\tprefix = %s", STR2STR(wp->config->prefix));
+ zlog(ZLOG_NOTICE, "\tuser = %s", STR2STR(wp->config->user));
+ zlog(ZLOG_NOTICE, "\tgroup = %s", STR2STR(wp->config->group));
+ zlog(ZLOG_NOTICE, "\tlisten = %s", STR2STR(wp->config->listen_address));
+ zlog(ZLOG_NOTICE, "\tlisten.backlog = %d", wp->config->listen_backlog);
+ zlog(ZLOG_NOTICE, "\tlisten.owner = %s", STR2STR(wp->config->listen_owner));
+ zlog(ZLOG_NOTICE, "\tlisten.group = %s", STR2STR(wp->config->listen_group));
+ zlog(ZLOG_NOTICE, "\tlisten.mode = %s", STR2STR(wp->config->listen_mode));
+ zlog(ZLOG_NOTICE, "\tlisten.allowed_clients = %s", STR2STR(wp->config->listen_allowed_clients));
+ if (wp->config->process_priority == 64) {
+ zlog(ZLOG_NOTICE, "\tprocess.priority = undefined");
+ } else {
+ zlog(ZLOG_NOTICE, "\tprocess.priority = %d", wp->config->process_priority);
+ }
+ zlog(ZLOG_NOTICE, "\tpm = %s", PM2STR(wp->config->pm));
+ zlog(ZLOG_NOTICE, "\tpm.max_children = %d", wp->config->pm_max_children);
+ zlog(ZLOG_NOTICE, "\tpm.start_servers = %d", wp->config->pm_start_servers);
+ zlog(ZLOG_NOTICE, "\tpm.min_spare_servers = %d", wp->config->pm_min_spare_servers);
+ zlog(ZLOG_NOTICE, "\tpm.max_spare_servers = %d", wp->config->pm_max_spare_servers);
+ zlog(ZLOG_NOTICE, "\tpm.process_idle_timeout = %d", wp->config->pm_process_idle_timeout);
+ zlog(ZLOG_NOTICE, "\tpm.max_requests = %d", wp->config->pm_max_requests);
+ zlog(ZLOG_NOTICE, "\tpm.status_path = %s", STR2STR(wp->config->pm_status_path));
+ zlog(ZLOG_NOTICE, "\tping.path = %s", STR2STR(wp->config->ping_path));
+ zlog(ZLOG_NOTICE, "\tping.response = %s", STR2STR(wp->config->ping_response));
+ zlog(ZLOG_NOTICE, "\taccess.log = %s", STR2STR(wp->config->access_log));
+ zlog(ZLOG_NOTICE, "\taccess.format = %s", STR2STR(wp->config->access_format));
+ zlog(ZLOG_NOTICE, "\tslowlog = %s", STR2STR(wp->config->slowlog));
+ zlog(ZLOG_NOTICE, "\trequest_slowlog_timeout = %ds", wp->config->request_slowlog_timeout);
+ zlog(ZLOG_NOTICE, "\trequest_terminate_timeout = %ds", wp->config->request_terminate_timeout);
+ zlog(ZLOG_NOTICE, "\trlimit_files = %d", wp->config->rlimit_files);
+ zlog(ZLOG_NOTICE, "\trlimit_core = %d", wp->config->rlimit_core);
+ zlog(ZLOG_NOTICE, "\tchroot = %s", STR2STR(wp->config->chroot));
+ zlog(ZLOG_NOTICE, "\tchdir = %s", STR2STR(wp->config->chdir));
+ zlog(ZLOG_NOTICE, "\tcatch_workers_output = %s", BOOL2STR(wp->config->catch_workers_output));
+ zlog(ZLOG_NOTICE, "\tsecurity.limit_extensions = %s", wp->config->security_limit_extensions);
+
+ for (kv = wp->config->env; kv; kv = kv->next) {
+ zlog(ZLOG_NOTICE, "\tenv[%s] = %s", kv->key, kv->value);
+ }
+
+ for (kv = wp->config->php_values; kv; kv = kv->next) {
+ zlog(ZLOG_NOTICE, "\tphp_value[%s] = %s", kv->key, kv->value);
+ }
+
+ for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
+ zlog(ZLOG_NOTICE, "\tphp_admin_value[%s] = %s", kv->key, kv->value);
+ }
+ zlog(ZLOG_NOTICE, " ");
+ }
+}
+/* }}} */
+
+int fpm_conf_init_main(int test_conf, int force_daemon) /* {{{ */
+{
+ int ret;
+ TSRMLS_FETCH();
+
+ if (fpm_globals.prefix && *fpm_globals.prefix) {
+ if (!fpm_conf_is_dir(fpm_globals.prefix)) {
+ zlog(ZLOG_ERROR, "the global prefix '%s' does not exist or is not a directory", fpm_globals.prefix);
+ return -1;
+ }
+ }
+
+ if (fpm_globals.pid && *fpm_globals.pid) {
+ fpm_global_config.pid_file = strdup(fpm_globals.pid);
+ }
+
+ if (fpm_globals.config == NULL) {
+ char *tmp;
+
+ if (fpm_globals.prefix == NULL) {
+ spprintf(&tmp, 0, "%s/php-fpm.conf", PHP_SYSCONFDIR);
+ } else {
+ spprintf(&tmp, 0, "%s/etc/php-fpm.conf", fpm_globals.prefix);
+ }
+
+ if (!tmp) {
+ zlog(ZLOG_SYSERROR, "spprintf() failed (tmp for fpm_globals.config)");
+ return -1;
+ }
+
+ fpm_globals.config = strdup(tmp);
+ efree(tmp);
+
+ if (!fpm_globals.config) {
+ zlog(ZLOG_SYSERROR, "spprintf() failed (fpm_globals.config)");
+ return -1;
+ }
+ }
+
+ ret = fpm_conf_load_ini_file(fpm_globals.config TSRMLS_CC);
+
+ if (0 > ret) {
+ zlog(ZLOG_ERROR, "failed to load configuration file '%s'", fpm_globals.config);
+ return -1;
+ }
+
+ if (0 > fpm_conf_post_process(force_daemon TSRMLS_CC)) {
+ zlog(ZLOG_ERROR, "failed to post process the configuration");
+ return -1;
+ }
+
+ if (test_conf) {
+ if (test_conf > 1) {
+ fpm_conf_dump();
+ }
+ zlog(ZLOG_NOTICE, "configuration file %s test is successful\n", fpm_globals.config);
+ fpm_globals.test_successful = 1;
+ return -1;
+ }
+
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_conf_cleanup, 0)) {
+ return -1;
+ }
+
+ return 0;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_conf.h b/sapi/fpm/fpm/fpm_conf.h
new file mode 100644
index 0000000..dc54133
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_conf.h
@@ -0,0 +1,106 @@
+
+ /* $Id: fpm_conf.h,v 1.12.2.2 2008/12/13 03:46:49 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_CONF_H
+#define FPM_CONF_H 1
+
+#include <stdint.h>
+#include "php.h"
+
+#define PM2STR(a) (a == PM_STYLE_STATIC ? "static" : (a == PM_STYLE_DYNAMIC ? "dynamic" : "ondemand"))
+
+#define FPM_CONF_MAX_PONG_LENGTH 64
+
+struct key_value_s;
+
+struct key_value_s {
+ struct key_value_s *next;
+ char *key;
+ char *value;
+};
+
+/*
+ * Please keep the same order as in fpm_conf.c and in php-fpm.conf.in
+ */
+struct fpm_global_config_s {
+ char *pid_file;
+ char *error_log;
+#ifdef HAVE_SYSLOG_H
+ char *syslog_ident;
+ int syslog_facility;
+#endif
+ int log_level;
+ int emergency_restart_threshold;
+ int emergency_restart_interval;
+ int process_control_timeout;
+ int process_max;
+ int process_priority;
+ int daemonize;
+ int rlimit_files;
+ int rlimit_core;
+ char *events_mechanism;
+};
+
+extern struct fpm_global_config_s fpm_global_config;
+
+/*
+ * Please keep the same order as in fpm_conf.c and in php-fpm.conf.in
+ */
+struct fpm_worker_pool_config_s {
+ char *name;
+ char *prefix;
+ char *user;
+ char *group;
+ char *listen_address;
+ int listen_backlog;
+ char *listen_owner;
+ char *listen_group;
+ char *listen_mode;
+ char *listen_allowed_clients;
+ int process_priority;
+ int pm;
+ int pm_max_children;
+ int pm_start_servers;
+ int pm_min_spare_servers;
+ int pm_max_spare_servers;
+ int pm_process_idle_timeout;
+ int pm_max_requests;
+ char *pm_status_path;
+ char *ping_path;
+ char *ping_response;
+ char *access_log;
+ char *access_format;
+ char *slowlog;
+ int request_slowlog_timeout;
+ int request_terminate_timeout;
+ int rlimit_files;
+ int rlimit_core;
+ char *chroot;
+ char *chdir;
+ int catch_workers_output;
+ char *security_limit_extensions;
+ struct key_value_s *env;
+ struct key_value_s *php_admin_values;
+ struct key_value_s *php_values;
+};
+
+struct ini_value_parser_s {
+ char *name;
+ char *(*parser)(zval *, void **, intptr_t);
+ intptr_t offset;
+};
+
+enum {
+ PM_STYLE_STATIC = 1,
+ PM_STYLE_DYNAMIC = 2,
+ PM_STYLE_ONDEMAND = 3
+};
+
+int fpm_conf_init_main(int test_conf, int force_daemon);
+int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc);
+int fpm_conf_write_pid();
+int fpm_conf_unlink_pid();
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_config.h b/sapi/fpm/fpm/fpm_config.h
new file mode 100644
index 0000000..856414a
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_config.h
@@ -0,0 +1,79 @@
+/* $Id: fpm_config.h,v 1.16 2008/05/25 00:30:43 anight Exp $ */
+/* (c) 2007,2008 Andrei Nigmatulin */
+
+#include <php_config.h>
+
+/* Solaris does not have it */
+#ifndef INADDR_NONE
+# define INADDR_NONE (-1)
+#endif
+
+
+/* If we're not using GNU C, elide __attribute__ */
+#ifndef __GNUC__
+# define __attribute__(x) /*NOTHING*/
+#endif
+
+/* Missing timer* macros (for solaris) */
+#ifndef timerisset
+# define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+#endif
+
+#ifndef timerclear
+# define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
+#endif
+
+#ifndef timersub
+# define timersub(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
+ if ((vvp)->tv_usec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif
+
+#ifndef timeradd
+# define timeradd(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
+ if ((result)->tv_usec >= 1000000) \
+ { \
+ ++(result)->tv_sec; \
+ (result)->tv_usec -= 1000000; \
+ } \
+ } while (0)
+#endif
+
+#ifndef timercmp
+/* does not work for >= and <= */
+# define timercmp(a, b, CMP) \
+ (((a)->tv_sec == (b)->tv_sec) ? \
+ ((a)->tv_usec CMP (b)->tv_usec) : \
+ ((a)->tv_sec CMP (b)->tv_sec))
+#endif
+/* endof timer* macros */
+
+#ifndef MIN
+# define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+
+#ifndef MAX
+# define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+#if defined(HAVE_PTRACE) || defined(PROC_MEM_FILE) || defined(HAVE_MACH_VM_READ)
+# define HAVE_FPM_TRACE 1
+#else
+# define HAVE_FPM_TRACE 0
+#endif
+
+#if defined(HAVE_LQ_TCP_INFO) || defined(HAVE_LQ_SO_LISTENQ)
+# define HAVE_FPM_LQ 1
+#else
+# define HAVE_FPM_LQ 0
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_env.c b/sapi/fpm/fpm/fpm_env.c
new file mode 100644
index 0000000..6b64fed
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_env.c
@@ -0,0 +1,276 @@
+
+ /* $Id: fpm_env.c,v 1.15 2008/09/18 23:19:59 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fpm_env.h"
+#include "fpm.h"
+
+#ifndef HAVE_SETPROCTITLE
+#ifdef __linux__
+static char **fpm_env_argv = NULL;
+static size_t fpm_env_argv_len = 0;
+#endif
+#endif
+
+#ifndef HAVE_SETENV
+# ifdef (__sparc__ || __sparc)
+int setenv(char *name, char *value, int clobber) /* {{{ */
+{
+ char *malloc();
+ char *getenv();
+ char *cp;
+
+ if (clobber == 0 && getenv(name) != 0) {
+ return 0;
+ }
+
+ if ((cp = malloc(strlen(name) + strlen(value) + 2)) == 0) {
+ return 1;
+ }
+ sprintf(cp, "%s=%s", name, value);
+ return putenv(cp);
+}
+/* }}} */
+# else
+int setenv(char *name, char *value, int overwrite) /* {{{ */
+{
+ int name_len = strlen(name);
+ int value_len = strlen(value);
+ char *var = alloca(name_len + 1 + value_len + 1);
+
+ memcpy(var, name, name_len);
+
+ var[name_len] = '=';
+
+ memcpy(var + name_len + 1, value, value_len);
+
+ var[name_len + 1 + value_len] = '\0';
+
+ return putenv(var);
+}
+/* }}} */
+# endif
+#endif
+
+#ifndef HAVE_CLEARENV
+void clearenv() /* {{{ */
+{
+ char **envp;
+ char *s;
+
+ /* this algo is the only one known to me
+ that works well on all systems */
+ while (*(envp = environ)) {
+ char *eq = strchr(*envp, '=');
+
+ s = strdup(*envp);
+
+ if (eq) s[eq - *envp] = '\0';
+
+ unsetenv(s);
+ free(s);
+ }
+
+}
+/* }}} */
+#endif
+
+#ifndef HAVE_UNSETENV
+void unsetenv(const char *name) /* {{{ */
+{
+ if(getenv(name) != NULL) {
+ int ct = 0;
+ int del = 0;
+
+ while(environ[ct] != NULL) {
+ if (nvmatch(name, environ[ct]) != 0) del=ct; /* <--- WTF?! */
+ { ct++; } /* <--- WTF?! */
+ }
+ /* isn't needed free here?? */
+ environ[del] = environ[ct-1];
+ environ[ct-1] = NULL;
+ }
+}
+/* }}} */
+
+static char * nvmatch(char *s1, char *s2) /* {{{ */
+{
+ while(*s1 == *s2++)
+ {
+ if(*s1++ == '=') {
+ return s2;
+ }
+ }
+ if(*s1 == '\0' && *(s2-1) == '=') {
+ return s2;
+ }
+ return NULL;
+}
+/* }}} */
+#endif
+
+void fpm_env_setproctitle(char *title) /* {{{ */
+{
+#ifdef HAVE_SETPROCTITLE
+ setproctitle("%s", title);
+#else
+#ifdef __linux__
+ if (fpm_env_argv != NULL && fpm_env_argv_len > strlen(SETPROCTITLE_PREFIX) + 3) {
+ memset(fpm_env_argv[0], 0, fpm_env_argv_len);
+ strncpy(fpm_env_argv[0], SETPROCTITLE_PREFIX, fpm_env_argv_len - 2);
+ strncpy(fpm_env_argv[0] + strlen(SETPROCTITLE_PREFIX), title, fpm_env_argv_len - strlen(SETPROCTITLE_PREFIX) - 2);
+ fpm_env_argv[1] = NULL;
+ }
+#endif
+#endif
+}
+/* }}} */
+
+int fpm_env_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct key_value_s *kv;
+ char *title;
+ spprintf(&title, 0, "pool %s", wp->config->name);
+ fpm_env_setproctitle(title);
+ efree(title);
+
+ clearenv();
+
+ for (kv = wp->config->env; kv; kv = kv->next) {
+ setenv(kv->key, kv->value, 1);
+ }
+
+ if (wp->user) {
+ setenv("USER", wp->user, 1);
+ }
+
+ if (wp->home) {
+ setenv("HOME", wp->home, 1);
+ }
+
+ return 0;
+}
+/* }}} */
+
+static int fpm_env_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct key_value_s *kv;
+
+ for (kv = wp->config->env; kv; kv = kv->next) {
+ if (*kv->value == '$') {
+ char *value = getenv(kv->value + 1);
+
+ if (!value) {
+ value = "";
+ }
+
+ free(kv->value);
+ kv->value = strdup(value);
+ }
+
+ /* autodetected values should be removed
+ if these vars specified in config */
+ if (!strcmp(kv->key, "USER")) {
+ free(wp->user);
+ wp->user = 0;
+ }
+
+ if (!strcmp(kv->key, "HOME")) {
+ free(wp->home);
+ wp->home = 0;
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+
+int fpm_env_init_main() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ int i;
+ char *first = NULL;
+ char *last = NULL;
+ char *title;
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (0 > fpm_env_conf_wp(wp)) {
+ return -1;
+ }
+ }
+#ifndef HAVE_SETPROCTITLE
+#ifdef __linux__
+ /*
+ * This piece of code has been inspirated from nginx and pureftpd code, whic
+ * are under BSD licence.
+ *
+ * To change the process title in Linux we have to set argv[1] to NULL
+ * and to copy the title to the same place where the argv[0] points to.
+ * However, argv[0] may be too small to hold a new title. Fortunately, Linux
+ * store argv[] and environ[] one after another. So we should ensure that is
+ * the continuous memory and then we allocate the new memory for environ[]
+ * and copy it. After this we could use the memory starting from argv[0] for
+ * our process title.
+ */
+
+ for (i = 0; i < fpm_globals.argc; i++) {
+ if (first == NULL) {
+ first = fpm_globals.argv[i];
+ }
+ if (last == NULL || fpm_globals.argv[i] == last + 1) {
+ last = fpm_globals.argv[i] + strlen(fpm_globals.argv[i]);
+ }
+ }
+ if (environ) {
+ for (i = 0; environ[i]; i++) {
+ if (first == NULL) {
+ first = environ[i];
+ }
+ if (last == NULL || environ[i] == last + 1) {
+ last = environ[i] + strlen(environ[i]);
+ }
+ }
+ }
+ if (first == NULL || last == NULL) {
+ return 0;
+ }
+
+ fpm_env_argv_len = last - first;
+ fpm_env_argv = fpm_globals.argv;
+ if (environ != NULL) {
+ char **new_environ;
+ unsigned int env_nb = 0U;
+
+ while (environ[env_nb]) {
+ env_nb++;
+ }
+
+ if ((new_environ = malloc((1U + env_nb) * sizeof (char *))) == NULL) {
+ return -1;
+ }
+ new_environ[env_nb] = NULL;
+ while (env_nb > 0U) {
+ env_nb--;
+ new_environ[env_nb] = strdup(environ[env_nb]);
+ }
+ environ = new_environ;
+ }
+#endif
+#endif
+
+ spprintf(&title, 0, "master process (%s)", fpm_globals.config);
+ fpm_env_setproctitle(title);
+ efree(title);
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_env.h b/sapi/fpm/fpm/fpm_env.h
new file mode 100644
index 0000000..bf47236
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_env.h
@@ -0,0 +1,27 @@
+
+ /* $Id: fpm_env.h,v 1.9 2008/09/18 23:19:59 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_ENV_H
+#define FPM_ENV_H 1
+
+#include "fpm_worker_pool.h"
+
+#define SETPROCTITLE_PREFIX "php-fpm: "
+
+int fpm_env_init_child(struct fpm_worker_pool_s *wp);
+int fpm_env_init_main();
+void fpm_env_setproctitle(char *title);
+
+extern char **environ;
+
+#ifndef HAVE_SETENV
+int setenv(char *name, char *value, int overwrite);
+#endif
+
+#ifndef HAVE_CLEARENV
+void clearenv();
+#endif
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c
new file mode 100644
index 0000000..d5835f0
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_events.c
@@ -0,0 +1,533 @@
+
+ /* $Id: fpm_events.c,v 1.21.2.2 2008/12/13 03:21:18 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h> /* for putenv */
+#include <string.h>
+
+#include <php.h>
+
+#include "fpm.h"
+#include "fpm_process_ctl.h"
+#include "fpm_events.h"
+#include "fpm_cleanup.h"
+#include "fpm_stdio.h"
+#include "fpm_signals.h"
+#include "fpm_children.h"
+#include "zlog.h"
+#include "fpm_clock.h"
+#include "fpm_log.h"
+
+#include "events/select.h"
+#include "events/poll.h"
+#include "events/epoll.h"
+#include "events/devpoll.h"
+#include "events/port.h"
+#include "events/kqueue.h"
+
+#define fpm_event_set_timeout(ev, now) timeradd(&(now), &(ev)->frequency, &(ev)->timeout);
+
+static void fpm_event_cleanup(int which, void *arg);
+static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg);
+static struct fpm_event_s *fpm_event_queue_isset(struct fpm_event_queue_s *queue, struct fpm_event_s *ev);
+static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_event_s *ev);
+static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_event_s *ev);
+static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue);
+
+static struct fpm_event_module_s *module;
+static struct fpm_event_queue_s *fpm_event_queue_timer = NULL;
+static struct fpm_event_queue_s *fpm_event_queue_fd = NULL;
+
+static void fpm_event_cleanup(int which, void *arg) /* {{{ */
+{
+ fpm_event_queue_destroy(&fpm_event_queue_timer);
+ fpm_event_queue_destroy(&fpm_event_queue_fd);
+}
+/* }}} */
+
+static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ char c;
+ int res, ret;
+ int fd = ev->fd;
+
+ do {
+ do {
+ res = read(fd, &c, 1);
+ } while (res == -1 && errno == EINTR);
+
+ if (res <= 0) {
+ if (res < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
+ zlog(ZLOG_SYSERROR, "unable to read from the signal pipe");
+ }
+ return;
+ }
+
+ switch (c) {
+ case 'C' : /* SIGCHLD */
+ zlog(ZLOG_DEBUG, "received SIGCHLD");
+ fpm_children_bury();
+ break;
+ case 'I' : /* SIGINT */
+ zlog(ZLOG_DEBUG, "received SIGINT");
+ zlog(ZLOG_NOTICE, "Terminating ...");
+ fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
+ break;
+ case 'T' : /* SIGTERM */
+ zlog(ZLOG_DEBUG, "received SIGTERM");
+ zlog(ZLOG_NOTICE, "Terminating ...");
+ fpm_pctl(FPM_PCTL_STATE_TERMINATING, FPM_PCTL_ACTION_SET);
+ break;
+ case 'Q' : /* SIGQUIT */
+ zlog(ZLOG_DEBUG, "received SIGQUIT");
+ zlog(ZLOG_NOTICE, "Finishing ...");
+ fpm_pctl(FPM_PCTL_STATE_FINISHING, FPM_PCTL_ACTION_SET);
+ break;
+ case '1' : /* SIGUSR1 */
+ zlog(ZLOG_DEBUG, "received SIGUSR1");
+ if (0 == fpm_stdio_open_error_log(1)) {
+ zlog(ZLOG_NOTICE, "error log file re-opened");
+ } else {
+ zlog(ZLOG_ERROR, "unable to re-opened error log file");
+ }
+
+ ret = fpm_log_open(1);
+ if (ret == 0) {
+ zlog(ZLOG_NOTICE, "access log file re-opened");
+ } else if (ret == -1) {
+ zlog(ZLOG_ERROR, "unable to re-opened access log file");
+ }
+ /* else no access log are set */
+
+ break;
+ case '2' : /* SIGUSR2 */
+ zlog(ZLOG_DEBUG, "received SIGUSR2");
+ zlog(ZLOG_NOTICE, "Reloading in progress ...");
+ fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET);
+ break;
+ }
+
+ if (fpm_globals.is_child) {
+ break;
+ }
+ } while (1);
+ return;
+}
+/* }}} */
+
+static struct fpm_event_s *fpm_event_queue_isset(struct fpm_event_queue_s *queue, struct fpm_event_s *ev) /* {{{ */
+{
+ if (!ev) {
+ return NULL;
+ }
+
+ while (queue) {
+ if (queue->ev == ev) {
+ return ev;
+ }
+ queue = queue->next;
+ }
+
+ return NULL;
+}
+/* }}} */
+
+static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_event_s *ev) /* {{{ */
+{
+ struct fpm_event_queue_s *elt;
+
+ if (!queue || !ev) {
+ return -1;
+ }
+
+ if (fpm_event_queue_isset(*queue, ev)) {
+ return 0;
+ }
+
+ if (!(elt = malloc(sizeof(struct fpm_event_queue_s)))) {
+ zlog(ZLOG_SYSERROR, "Unable to add the event to queue: malloc() failed");
+ return -1;
+ }
+ elt->prev = NULL;
+ elt->next = NULL;
+ elt->ev = ev;
+
+ if (*queue) {
+ (*queue)->prev = elt;
+ elt->next = *queue;
+ }
+ *queue = elt;
+
+ /* ask the event module to add the fd from its own queue */
+ if (*queue == fpm_event_queue_fd && module->add) {
+ module->add(ev);
+ }
+
+ return 0;
+}
+/* }}} */
+
+static int fpm_event_queue_del(struct fpm_event_queue_s **queue, struct fpm_event_s *ev) /* {{{ */
+{
+ struct fpm_event_queue_s *q;
+ if (!queue || !ev) {
+ return -1;
+ }
+ q = *queue;
+ while (q) {
+ if (q->ev == ev) {
+ if (q->prev) {
+ q->prev->next = q->next;
+ }
+ if (q->next) {
+ q->next->prev = q->prev;
+ }
+ if (q == *queue) {
+ *queue = q->next;
+ if (*queue) {
+ (*queue)->prev = NULL;
+ }
+ }
+
+ /* ask the event module to remove the fd from its own queue */
+ if (*queue == fpm_event_queue_fd && module->remove) {
+ module->remove(ev);
+ }
+
+ free(q);
+ return 0;
+ }
+ q = q->next;
+ }
+ return -1;
+}
+/* }}} */
+
+static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue) /* {{{ */
+{
+ struct fpm_event_queue_s *q, *tmp;
+
+ if (!queue) {
+ return;
+ }
+
+ if (*queue == fpm_event_queue_fd && module->clean) {
+ module->clean();
+ }
+
+ q = *queue;
+ while (q) {
+ tmp = q;
+ q = q->next;
+ /* q->prev = NULL */
+ free(tmp);
+ }
+ *queue = NULL;
+}
+/* }}} */
+
+int fpm_event_pre_init(char *machanism) /* {{{ */
+{
+ /* kqueue */
+ module = fpm_event_kqueue_module();
+ if (module) {
+ if (!machanism || strcasecmp(module->name, machanism) == 0) {
+ return 0;
+ }
+ }
+
+ /* port */
+ module = fpm_event_port_module();
+ if (module) {
+ if (!machanism || strcasecmp(module->name, machanism) == 0) {
+ return 0;
+ }
+ }
+
+ /* epoll */
+ module = fpm_event_epoll_module();
+ if (module) {
+ if (!machanism || strcasecmp(module->name, machanism) == 0) {
+ return 0;
+ }
+ }
+
+ /* /dev/poll */
+ module = fpm_event_devpoll_module();
+ if (module) {
+ if (!machanism || strcasecmp(module->name, machanism) == 0) {
+ return 0;
+ }
+ }
+
+ /* poll */
+ module = fpm_event_poll_module();
+ if (module) {
+ if (!machanism || strcasecmp(module->name, machanism) == 0) {
+ return 0;
+ }
+ }
+
+ /* select */
+ module = fpm_event_select_module();
+ if (module) {
+ if (!machanism || strcasecmp(module->name, machanism) == 0) {
+ return 0;
+ }
+ }
+
+ if (machanism) {
+ zlog(ZLOG_ERROR, "event mechanism '%s' is not available on this system", machanism);
+ } else {
+ zlog(ZLOG_ERROR, "unable to find a suitable event mechanism on this system");
+ }
+ return -1;
+}
+/* }} */
+
+const char *fpm_event_machanism_name() /* {{{ */
+{
+ return module ? module->name : NULL;
+}
+/* }}} */
+
+int fpm_event_support_edge_trigger() /* {{{ */
+{
+ return module ? module->support_edge_trigger : 0;
+}
+/* }}} */
+
+int fpm_event_init_main() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ int max;
+
+ if (!module) {
+ zlog(ZLOG_ERROR, "no event module found");
+ return -1;
+ }
+
+ if (!module->wait) {
+ zlog(ZLOG_ERROR, "Incomplete event implementation. Please open a bug report on https://bugs.php.net.");
+ return -1;
+ }
+
+ /* count the max number of necessary fds for polling */
+ max = 1; /* only one FD is necessary at startup for the master process signal pipe */
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (!wp->config) continue;
+ if (wp->config->catch_workers_output && wp->config->pm_max_children > 0) {
+ max += (wp->config->pm_max_children * 2);
+ }
+ }
+
+ if (module->init(max) < 0) {
+ zlog(ZLOG_ERROR, "Unable to initialize the event module %s", module->name);
+ return -1;
+ }
+
+ zlog(ZLOG_DEBUG, "event module is %s and %d fds have been reserved", module->name, max);
+
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_event_cleanup, NULL)) {
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+void fpm_event_loop(int err) /* {{{ */
+{
+ static struct fpm_event_s signal_fd_event;
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return;
+ }
+
+ fpm_event_set(&signal_fd_event, fpm_signals_get_fd(), FPM_EV_READ, &fpm_got_signal, NULL);
+ fpm_event_add(&signal_fd_event, 0);
+
+ /* add timers */
+ if (fpm_globals.heartbeat > 0) {
+ fpm_pctl_heartbeat(NULL, 0, NULL);
+ }
+
+ if (!err) {
+ fpm_pctl_perform_idle_server_maintenance_heartbeat(NULL, 0, NULL);
+
+ zlog(ZLOG_DEBUG, "%zu bytes have been reserved in SHM", fpm_shm_get_size_allocated());
+ zlog(ZLOG_NOTICE, "ready to handle connections");
+ }
+
+ while (1) {
+ struct fpm_event_queue_s *q, *q2;
+ struct timeval ms;
+ struct timeval tmp;
+ struct timeval now;
+ unsigned long int timeout;
+ int ret;
+
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return;
+ }
+
+ fpm_clock_get(&now);
+ timerclear(&ms);
+
+ /* search in the timeout queue for the next timer to trigger */
+ q = fpm_event_queue_timer;
+ while (q) {
+ if (!timerisset(&ms)) {
+ ms = q->ev->timeout;
+ } else {
+ if (timercmp(&q->ev->timeout, &ms, <)) {
+ ms = q->ev->timeout;
+ }
+ }
+ q = q->next;
+ }
+
+ /* 1s timeout if none has been set */
+ if (!timerisset(&ms) || timercmp(&ms, &now, <) || timercmp(&ms, &now, ==)) {
+ timeout = 1000;
+ } else {
+ timersub(&ms, &now, &tmp);
+ timeout = (tmp.tv_sec * 1000) + (tmp.tv_usec / 1000) + 1;
+ }
+
+ ret = module->wait(fpm_event_queue_fd, timeout);
+
+ /* is a child, nothing to do here */
+ if (ret == -2) {
+ return;
+ }
+
+ if (ret > 0) {
+ zlog(ZLOG_DEBUG, "event module triggered %d events", ret);
+ }
+
+ /* trigger timers */
+ q = fpm_event_queue_timer;
+ while (q) {
+ fpm_clock_get(&now);
+ if (q->ev) {
+ if (timercmp(&now, &q->ev->timeout, >) || timercmp(&now, &q->ev->timeout, ==)) {
+ fpm_event_fire(q->ev);
+ /* sanity check */
+ if (fpm_globals.parent_pid != getpid()) {
+ return;
+ }
+ if (q->ev->flags & FPM_EV_PERSIST) {
+ fpm_event_set_timeout(q->ev, now);
+ } else { /* delete the event */
+ q2 = q;
+ if (q->prev) {
+ q->prev->next = q->next;
+ }
+ if (q->next) {
+ q->next->prev = q->prev;
+ }
+ if (q == fpm_event_queue_timer) {
+ fpm_event_queue_timer = q->next;
+ if (fpm_event_queue_timer) {
+ fpm_event_queue_timer->prev = NULL;
+ }
+ }
+ q = q->next;
+ free(q2);
+ continue;
+ }
+ }
+ }
+ q = q->next;
+ }
+ }
+}
+/* }}} */
+
+void fpm_event_fire(struct fpm_event_s *ev) /* {{{ */
+{
+ if (!ev || !ev->callback) {
+ return;
+ }
+
+ (*ev->callback)( (struct fpm_event_s *) ev, ev->which, ev->arg);
+}
+/* }}} */
+
+int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void (*callback)(struct fpm_event_s *, short, void *), void *arg) /* {{{ */
+{
+ if (!ev || !callback || fd < -1) {
+ return -1;
+ }
+ memset(ev, 0, sizeof(struct fpm_event_s));
+ ev->fd = fd;
+ ev->callback = callback;
+ ev->arg = arg;
+ ev->flags = flags;
+ return 0;
+}
+/* }}} */
+
+int fpm_event_add(struct fpm_event_s *ev, unsigned long int frequency) /* {{{ */
+{
+ struct timeval now;
+ struct timeval tmp;
+
+ if (!ev) {
+ return -1;
+ }
+
+ ev->index = -1;
+
+ /* it's a triggered event on incoming data */
+ if (ev->flags & FPM_EV_READ) {
+ ev->which = FPM_EV_READ;
+ if (fpm_event_queue_add(&fpm_event_queue_fd, ev) != 0) {
+ return -1;
+ }
+ return 0;
+ }
+
+ /* it's a timer event */
+ ev->which = FPM_EV_TIMEOUT;
+
+ fpm_clock_get(&now);
+ if (frequency >= 1000) {
+ tmp.tv_sec = frequency / 1000;
+ tmp.tv_usec = (frequency % 1000) * 1000;
+ } else {
+ tmp.tv_sec = 0;
+ tmp.tv_usec = frequency * 1000;
+ }
+ ev->frequency = tmp;
+ fpm_event_set_timeout(ev, now);
+
+ if (fpm_event_queue_add(&fpm_event_queue_timer, ev) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+int fpm_event_del(struct fpm_event_s *ev) /* {{{ */
+{
+ if (ev->index >= 0 && fpm_event_queue_del(&fpm_event_queue_fd, ev) != 0) {
+ return -1;
+ }
+
+ if (ev->index < 0 && fpm_event_queue_del(&fpm_event_queue_timer, ev) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_events.h b/sapi/fpm/fpm/fpm_events.h
new file mode 100644
index 0000000..416ec6e
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_events.h
@@ -0,0 +1,52 @@
+
+ /* $Id: fpm_events.h,v 1.9 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_EVENTS_H
+#define FPM_EVENTS_H 1
+
+#define FPM_EV_TIMEOUT (1 << 0)
+#define FPM_EV_READ (1 << 1)
+#define FPM_EV_PERSIST (1 << 2)
+#define FPM_EV_EDGE (1 << 3)
+
+#define fpm_event_set_timer(ev, flags, cb, arg) fpm_event_set((ev), -1, (flags), (cb), (arg))
+
+struct fpm_event_s {
+ int fd; /* not set with FPM_EV_TIMEOUT */
+ struct timeval timeout; /* next time to trigger */
+ struct timeval frequency;
+ void (*callback)(struct fpm_event_s *, short, void *);
+ void *arg;
+ int flags;
+ int index; /* index of the fd in the ufds array */
+ short which; /* type of event */
+};
+
+typedef struct fpm_event_queue_s {
+ struct fpm_event_queue_s *prev;
+ struct fpm_event_queue_s *next;
+ struct fpm_event_s *ev;
+} fpm_event_queue;
+
+struct fpm_event_module_s {
+ const char *name;
+ int support_edge_trigger;
+ int (*init)(int max_fd);
+ int (*clean)(void);
+ int (*wait)(struct fpm_event_queue_s *queue, unsigned long int timeout);
+ int (*add)(struct fpm_event_s *ev);
+ int (*remove)(struct fpm_event_s *ev);
+};
+
+void fpm_event_loop(int err);
+void fpm_event_fire(struct fpm_event_s *ev);
+int fpm_event_init_main();
+int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void (*callback)(struct fpm_event_s *, short, void *), void *arg);
+int fpm_event_add(struct fpm_event_s *ev, unsigned long int timeout);
+int fpm_event_del(struct fpm_event_s *ev);
+int fpm_event_pre_init(char *machanism);
+const char *fpm_event_machanism_name();
+int fpm_event_support_edge_trigger();
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_log.c b/sapi/fpm/fpm/fpm_log.c
new file mode 100644
index 0000000..6b014b5
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_log.c
@@ -0,0 +1,466 @@
+
+ /* $Id: fpm_status.c 312262 2011-06-18 17:41:56Z felipe $ */
+ /* (c) 2009 Jerome Loyet */
+
+#include "php.h"
+#include "SAPI.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef HAVE_TIMES
+#include <sys/times.h>
+#endif
+
+#include "fpm_config.h"
+#include "fpm_log.h"
+#include "fpm_clock.h"
+#include "fpm_process_ctl.h"
+#include "fpm_signals.h"
+#include "fpm_scoreboard.h"
+#include "fastcgi.h"
+#include "zlog.h"
+
+#ifdef MAX_LINE_LENGTH
+# define FPM_LOG_BUFFER MAX_LINE_LENGTH
+#else
+# define FPM_LOG_BUFFER 1024
+#endif
+
+static char *fpm_log_format = NULL;
+static int fpm_log_fd = -1;
+
+int fpm_log_open(int reopen) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ int ret = 1;
+
+ int fd;
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (!wp->config->access_log) {
+ continue;
+ }
+ ret = 0;
+
+ fd = open(wp->config->access_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+ if (0 > fd) {
+ zlog(ZLOG_SYSERROR, "failed to open access log (%s)", wp->config->access_log);
+ return -1;
+ }
+
+ if (reopen) {
+ dup2(fd, wp->log_fd);
+ close(fd);
+ fd = wp->log_fd;
+ fpm_pctl_kill_all(SIGQUIT);
+ } else {
+ wp->log_fd = fd;
+ }
+
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
+ }
+
+ return ret;
+}
+/* }}} */
+
+/* }}} */
+int fpm_log_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ if (!wp || !wp->config) {
+ return -1;
+ }
+
+ if (wp->config->access_log && *wp->config->access_log) {
+ if (wp->config->access_format) {
+ fpm_log_format = strdup(wp->config->access_format);
+ }
+ }
+
+ if (fpm_log_fd == -1) {
+ fpm_log_fd = wp->log_fd;
+ }
+
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (wp->log_fd > -1 && wp->log_fd != fpm_log_fd) {
+ close(wp->log_fd);
+ wp->log_fd = -1;
+ }
+ }
+
+ return 0;
+}
+/* }}} */
+
+int fpm_log_write(char *log_format TSRMLS_DC) /* {{{ */
+{
+ char *s, *b;
+ char buffer[FPM_LOG_BUFFER+1];
+ int token, test;
+ size_t len, len2;
+ struct fpm_scoreboard_proc_s proc, *proc_p;
+ struct fpm_scoreboard_s *scoreboard;
+ char tmp[129];
+ char format[129];
+ time_t now_epoch;
+#ifdef HAVE_TIMES
+ clock_t tms_total;
+#endif
+
+ if (!log_format && (!fpm_log_format || fpm_log_fd == -1)) {
+ return -1;
+ }
+
+ if (!log_format) {
+ log_format = fpm_log_format;
+ test = 0;
+ } else {
+ test = 1;
+ }
+
+ now_epoch = time(NULL);
+
+ if (!test) {
+ scoreboard = fpm_scoreboard_get();
+ if (!scoreboard) {
+ zlog(ZLOG_WARNING, "unable to get scoreboard while preparing the access log");
+ return -1;
+ }
+ proc_p = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (!proc_p) {
+ zlog(ZLOG_WARNING, "[pool %s] Unable to acquire shm slot while preparing the access log", scoreboard->pool);
+ return -1;
+ }
+ proc = *proc_p;
+ fpm_scoreboard_proc_release(proc_p);
+ }
+
+ token = 0;
+
+ memset(buffer, '\0', sizeof(buffer));
+ b = buffer;
+ len = 0;
+
+
+ s = log_format;
+
+ while (*s != '\0') {
+ /* Test is we have place for 1 more char. */
+ if (len >= FPM_LOG_BUFFER) {
+ zlog(ZLOG_NOTICE, "the log buffer is full (%d). The access log request has been truncated.", FPM_LOG_BUFFER);
+ len = FPM_LOG_BUFFER;
+ break;
+ }
+
+ if (!token && *s == '%') {
+ token = 1;
+ memset(format, '\0', sizeof(format)); /* reset format */
+ s++;
+ continue;
+ }
+
+ if (token) {
+ token = 0;
+ len2 = 0;
+ switch (*s) {
+
+ case '%': /* '%' */
+ *b = '%';
+ len2 = 1;
+ break;
+
+#ifdef HAVE_TIMES
+ case 'C': /* %CPU */
+ if (format[0] == '\0' || !strcasecmp(format, "total")) {
+ if (!test) {
+ tms_total = proc.last_request_cpu.tms_utime + proc.last_request_cpu.tms_stime + proc.last_request_cpu.tms_cutime + proc.last_request_cpu.tms_cstime;
+ }
+ } else if (!strcasecmp(format, "user")) {
+ if (!test) {
+ tms_total = proc.last_request_cpu.tms_utime + proc.last_request_cpu.tms_cutime;
+ }
+ } else if (!strcasecmp(format, "system")) {
+ if (!test) {
+ tms_total = proc.last_request_cpu.tms_stime + proc.last_request_cpu.tms_cstime;
+ }
+ } else {
+ zlog(ZLOG_WARNING, "only 'total', 'user' or 'system' are allowed as a modifier for %%%c ('%s')", *s, format);
+ return -1;
+ }
+
+ format[0] = '\0';
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.2f", tms_total / fpm_scoreboard_get_tick() / (proc.cpu_duration.tv_sec + proc.cpu_duration.tv_usec / 1000000.) * 100.);
+ }
+ break;
+#endif
+
+ case 'd': /* duration µs */
+ /* seconds */
+ if (format[0] == '\0' || !strcasecmp(format, "seconds")) {
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.3f", proc.duration.tv_sec + proc.duration.tv_usec / 1000000.);
+ }
+
+ /* miliseconds */
+ } else if (!strcasecmp(format, "miliseconds") || !strcasecmp(format, "mili")) {
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.3f", proc.duration.tv_sec * 1000. + proc.duration.tv_usec / 1000.);
+ }
+
+ /* microseconds */
+ } else if (!strcasecmp(format, "microseconds") || !strcasecmp(format, "micro")) {
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", proc.duration.tv_sec * 1000000UL + proc.duration.tv_usec);
+ }
+
+ } else {
+ zlog(ZLOG_WARNING, "only 'seconds', 'mili', 'miliseconds', 'micro' or 'microseconds' are allowed as a modifier for %%%c ('%s')", *s, format);
+ return -1;
+ }
+ format[0] = '\0';
+ break;
+
+ case 'e': /* fastcgi env */
+ if (format[0] == '\0') {
+ zlog(ZLOG_WARNING, "the name of the environment variable must be set between embraces for %%%c", *s);
+ return -1;
+ }
+
+ if (!test) {
+ char *env = fcgi_getenv((fcgi_request*) SG(server_context), format, strlen(format));
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", env ? env : "-");
+ }
+ format[0] = '\0';
+ break;
+
+ case 'f': /* script */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.script_filename && *proc.script_filename ? proc.script_filename : "-");
+ }
+ break;
+
+ case 'l': /* content length */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%zu", proc.content_length);
+ }
+ break;
+
+ case 'm': /* method */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.request_method && *proc.request_method ? proc.request_method : "-");
+ }
+ break;
+
+ case 'M': /* memory */
+ /* seconds */
+ if (format[0] == '\0' || !strcasecmp(format, "bytes")) {
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%zu", proc.memory);
+ }
+
+ /* kilobytes */
+ } else if (!strcasecmp(format, "kilobytes") || !strcasecmp(format, "kilo")) {
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", proc.memory / 1024);
+ }
+
+ /* megabytes */
+ } else if (!strcasecmp(format, "megabytes") || !strcasecmp(format, "mega")) {
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", proc.memory / 1024 / 1024);
+ }
+
+ } else {
+ zlog(ZLOG_WARNING, "only 'bytes', 'kilo', 'kilobytes', 'mega' or 'megabytes' are allowed as a modifier for %%%c ('%s')", *s, format);
+ return -1;
+ }
+ format[0] = '\0';
+ break;
+
+ case 'n': /* pool name */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", scoreboard->pool[0] ? scoreboard->pool : "-");
+ }
+ break;
+
+ case 'o': /* header output */
+ if (format[0] == '\0') {
+ zlog(ZLOG_WARNING, "the name of the header must be set between embraces for %%%c", *s);
+ return -1;
+ }
+ if (!test) {
+ sapi_header_struct *h;
+ zend_llist_position pos;
+ sapi_headers_struct *sapi_headers = &SG(sapi_headers);
+ size_t format_len = strlen(format);
+
+ h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+ char *header;
+ if (!h->header_len) {
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ continue;
+ }
+ if (!strstr(h->header, format)) {
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ continue;
+ }
+
+ /* test if enought char after the header name + ': ' */
+ if (h->header_len <= format_len + 2) {
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ continue;
+ }
+
+ if (h->header[format_len] != ':' || h->header[format_len + 1] != ' ') {
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ continue;
+ }
+
+ header = h->header + format_len + 2;
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", header && *header ? header : "-");
+
+ /* found, done */
+ break;
+ }
+ if (!len2) {
+ len2 = 1;
+ *b = '-';
+ }
+ }
+ format[0] = '\0';
+ break;
+
+ case 'p': /* PID */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%ld", (long)getpid());
+ }
+ break;
+
+ case 'P': /* PID */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%ld", (long)getppid());
+ }
+ break;
+
+ case 'q': /* query_string */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.query_string ? proc.query_string : "");
+ }
+ break;
+
+ case 'Q': /* '?' */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.query_string && *proc.query_string ? "?" : "");
+ }
+ break;
+
+ case 'r': /* request URI */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.request_uri ? proc.request_uri : "-");
+ }
+ break;
+
+ case 'R': /* remote IP address */
+ if (!test) {
+ char *tmp = fcgi_get_last_client_ip();
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", tmp ? tmp : "-");
+ }
+ break;
+
+ case 's': /* status */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%d", SG(sapi_headers).http_response_code);
+ }
+ break;
+
+ case 'T':
+ case 't': /* time */
+ if (!test) {
+ time_t *t;
+ if (*s == 't') {
+ t = &proc.accepted_epoch;
+ } else {
+ t = &now_epoch;
+ }
+ if (format[0] == '\0') {
+ strftime(tmp, sizeof(tmp) - 1, "%d/%b/%Y:%H:%M:%S %z", localtime(t));
+ } else {
+ strftime(tmp, sizeof(tmp) - 1, format, localtime(t));
+ }
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", tmp);
+ }
+ format[0] = '\0';
+ break;
+
+ case 'u': /* remote user */
+ if (!test) {
+ len2 = snprintf(b, FPM_LOG_BUFFER - len, "%s", proc.auth_user ? proc.auth_user : "-");
+ }
+ break;
+
+ case '{': /* complex var */
+ token = 1;
+ {
+ char *start;
+ size_t l;
+
+ start = ++s;
+
+ while (*s != '\0') {
+ if (*s == '}') {
+ l = s - start;
+
+ if (l >= sizeof(format) - 1) {
+ l = sizeof(format) - 1;
+ }
+
+ memcpy(format, start, l);
+ format[l] = '\0';
+ break;
+ }
+ s++;
+ }
+ if (s[1] == '\0') {
+ zlog(ZLOG_WARNING, "missing closing embrace in the access.format");
+ return -1;
+ }
+ }
+ break;
+
+ default:
+ zlog(ZLOG_WARNING, "Invalid token in the access.format (%%%c)", *s);
+ return -1;
+ }
+
+ if (*s != '}' && format[0] != '\0') {
+ zlog(ZLOG_WARNING, "embrace is not allowed for modifier %%%c", *s);
+ return -1;
+ }
+ s++;
+ if (!test) {
+ b += len2;
+ len += len2;
+ }
+ continue;
+ }
+
+ if (!test) {
+ // push the normal char to the output buffer
+ *b = *s;
+ b++;
+ len++;
+ }
+ s++;
+ }
+
+ if (!test && strlen(buffer) > 0) {
+ buffer[len] = '\n';
+ write(fpm_log_fd, buffer, len + 1);
+ }
+
+ return 0;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_log.h b/sapi/fpm/fpm/fpm_log.h
new file mode 100644
index 0000000..f0199d9
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_log.h
@@ -0,0 +1,13 @@
+
+ /* $Id: fpm_status.h 312263 2011-06-18 17:46:16Z felipe $ */
+ /* (c) 2009 Jerome Loyet */
+
+#ifndef FPM_LOG_H
+#define FPM_LOG_H 1
+#include "fpm_worker_pool.h"
+
+int fpm_log_init_child(struct fpm_worker_pool_s *wp);
+int fpm_log_write(char *log_format TSRMLS_DC);
+int fpm_log_open(int reopen);
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c
new file mode 100644
index 0000000..61088c4
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_main.c
@@ -0,0 +1,2000 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> |
+ | Stig Bakken <ssb@php.net> |
+ | Zeev Suraski <zeev@zend.com> |
+ | FastCGI: Ben Mansell <php@slimyhorror.com> |
+ | Shane Caraveo <shane@caraveo.com> |
+ | Dmitry Stogov <dmitry@zend.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: cgi_main.c 291497 2009-11-30 14:43:22Z dmitry $ */
+
+#include "php.h"
+#include "php_globals.h"
+#include "php_variables.h"
+#include "zend_modules.h"
+#include "php.h"
+#include "zend_ini_scanner.h"
+#include "zend_globals.h"
+#include "zend_stream.h"
+
+#include "SAPI.h"
+
+#include <stdio.h>
+#include "php.h"
+
+#ifdef PHP_WIN32
+# include "win32/time.h"
+# include "win32/signal.h"
+# include <process.h>
+#endif
+
+#if HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+
+#if HAVE_SETLOCALE
+# include <locale.h>
+#endif
+
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#if HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+
+#include "zend.h"
+#include "zend_extensions.h"
+#include "php_ini.h"
+#include "php_globals.h"
+#include "php_main.h"
+#include "fopen_wrappers.h"
+#include "ext/standard/php_standard.h"
+
+#ifdef PHP_WIN32
+# include <io.h>
+# include <fcntl.h>
+# include "win32/php_registry.h"
+#endif
+
+#ifdef __riscos__
+# include <unixlib/local.h>
+int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS;
+#endif
+
+#include "zend_compile.h"
+#include "zend_execute.h"
+#include "zend_highlight.h"
+#include "zend_indent.h"
+
+#include "php_getopt.h"
+
+#include "fastcgi.h"
+
+#include <php_config.h>
+#include "fpm.h"
+#include "fpm_request.h"
+#include "fpm_status.h"
+#include "fpm_conf.h"
+#include "fpm_php.h"
+#include "fpm_log.h"
+#include "zlog.h"
+
+#ifndef PHP_WIN32
+/* XXX this will need to change later when threaded fastcgi is implemented. shane */
+struct sigaction act, old_term, old_quit, old_int;
+#endif
+
+static void (*php_php_import_environment_variables)(zval *array_ptr TSRMLS_DC);
+
+#ifndef PHP_WIN32
+/* these globals used for forking children on unix systems */
+
+/**
+ * Set to non-zero if we are the parent process
+ */
+static int parent = 1;
+#endif
+
+static int request_body_fd;
+static int fpm_is_running = 0;
+
+static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC);
+static void fastcgi_ini_parser(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg TSRMLS_DC);
+
+#define PHP_MODE_STANDARD 1
+#define PHP_MODE_HIGHLIGHT 2
+#define PHP_MODE_INDENT 3
+#define PHP_MODE_LINT 4
+#define PHP_MODE_STRIP 5
+
+static char *php_optarg = NULL;
+static int php_optind = 1;
+static zend_module_entry cgi_module_entry;
+
+static const opt_struct OPTIONS[] = {
+ {'c', 1, "php-ini"},
+ {'d', 1, "define"},
+ {'e', 0, "profile-info"},
+ {'h', 0, "help"},
+ {'i', 0, "info"},
+ {'m', 0, "modules"},
+ {'n', 0, "no-php-ini"},
+ {'?', 0, "usage"},/* help alias (both '?' and 'usage') */
+ {'v', 0, "version"},
+ {'y', 1, "fpm-config"},
+ {'t', 0, "test"},
+ {'p', 1, "prefix"},
+ {'g', 1, "pid"},
+ {'R', 0, "allow-to-run-as-root"},
+ {'D', 0, "daemonize"},
+ {'F', 0, "nodaemonize"},
+ {'-', 0, NULL} /* end of args */
+};
+
+typedef struct _php_cgi_globals_struct {
+ zend_bool rfc2616_headers;
+ zend_bool nph;
+ zend_bool fix_pathinfo;
+ zend_bool force_redirect;
+ zend_bool discard_path;
+ zend_bool fcgi_logging;
+ char *redirect_status_env;
+ HashTable user_config_cache;
+ char *error_header;
+ char *fpm_config;
+} php_cgi_globals_struct;
+
+/* {{{ user_config_cache
+ *
+ * Key for each cache entry is dirname(PATH_TRANSLATED).
+ *
+ * NOTE: Each cache entry config_hash contains the combination from all user ini files found in
+ * the path starting from doc_root throught to dirname(PATH_TRANSLATED). There is no point
+ * storing per-file entries as it would not be possible to detect added / deleted entries
+ * between separate files.
+ */
+typedef struct _user_config_cache_entry {
+ time_t expires;
+ HashTable *user_config;
+} user_config_cache_entry;
+
+static void user_config_cache_entry_dtor(user_config_cache_entry *entry)
+{
+ zend_hash_destroy(entry->user_config);
+ free(entry->user_config);
+}
+/* }}} */
+
+#ifdef ZTS
+static int php_cgi_globals_id;
+#define CGIG(v) TSRMG(php_cgi_globals_id, php_cgi_globals_struct *, v)
+#else
+static php_cgi_globals_struct php_cgi_globals;
+#define CGIG(v) (php_cgi_globals.v)
+#endif
+
+#ifdef PHP_WIN32
+#define TRANSLATE_SLASHES(path) \
+ { \
+ char *tmp = path; \
+ while (*tmp) { \
+ if (*tmp == '\\') *tmp = '/'; \
+ tmp++; \
+ } \
+ }
+#else
+#define TRANSLATE_SLASHES(path)
+#endif
+
+static int print_module_info(zend_module_entry *module, void *arg TSRMLS_DC)
+{
+ php_printf("%s\n", module->name);
+ return 0;
+}
+
+static int module_name_cmp(const void *a, const void *b TSRMLS_DC)
+{
+ Bucket *f = *((Bucket **) a);
+ Bucket *s = *((Bucket **) b);
+
+ return strcasecmp( ((zend_module_entry *)f->pData)->name,
+ ((zend_module_entry *)s->pData)->name);
+}
+
+static void print_modules(TSRMLS_D)
+{
+ HashTable sorted_registry;
+ zend_module_entry tmp;
+
+ zend_hash_init(&sorted_registry, 50, NULL, NULL, 1);
+ zend_hash_copy(&sorted_registry, &module_registry, NULL, &tmp, sizeof(zend_module_entry));
+ zend_hash_sort(&sorted_registry, zend_qsort, module_name_cmp, 0 TSRMLS_CC);
+ zend_hash_apply_with_argument(&sorted_registry, (apply_func_arg_t) print_module_info, NULL TSRMLS_CC);
+ zend_hash_destroy(&sorted_registry);
+}
+
+static int print_extension_info(zend_extension *ext, void *arg TSRMLS_DC)
+{
+ php_printf("%s\n", ext->name);
+ return 0;
+}
+
+static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s TSRMLS_DC)
+{
+ return strcmp( ((zend_extension *)(*f)->data)->name,
+ ((zend_extension *)(*s)->data)->name);
+}
+
+static void print_extensions(TSRMLS_D)
+{
+ zend_llist sorted_exts;
+
+ zend_llist_copy(&sorted_exts, &zend_extensions);
+ sorted_exts.dtor = NULL;
+ zend_llist_sort(&sorted_exts, extension_name_cmp TSRMLS_CC);
+ zend_llist_apply_with_argument(&sorted_exts, (llist_apply_with_arg_func_t) print_extension_info, NULL TSRMLS_CC);
+ zend_llist_destroy(&sorted_exts);
+}
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO 1
+#endif
+
+static inline size_t sapi_cgibin_single_write(const char *str, uint str_length TSRMLS_DC)
+{
+ ssize_t ret;
+
+ /* sapi has started which means everyhting must be send through fcgi */
+ if (fpm_is_running) {
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+ ret = fcgi_write(request, FCGI_STDOUT, str, str_length);
+ if (ret <= 0) {
+ return 0;
+ }
+ return (size_t)ret;
+ }
+
+ /* sapi has not started, output to stdout instead of fcgi */
+#ifdef PHP_WRITE_STDOUT
+ ret = write(STDOUT_FILENO, str, str_length);
+ if (ret <= 0) {
+ return 0;
+ }
+ return (size_t)ret;
+#else
+ return fwrite(str, 1, MIN(str_length, 16384), stdout);
+#endif
+}
+
+static int sapi_cgibin_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ const char *ptr = str;
+ uint remaining = str_length;
+ size_t ret;
+
+ while (remaining > 0) {
+ ret = sapi_cgibin_single_write(ptr, remaining TSRMLS_CC);
+ if (!ret) {
+ php_handle_aborted_connection();
+ return str_length - remaining;
+ }
+ ptr += ret;
+ remaining -= ret;
+ }
+
+ return str_length;
+}
+
+
+static void sapi_cgibin_flush(void *server_context)
+{
+ /* fpm has started, let use fcgi instead of stdout */
+ if (fpm_is_running) {
+ fcgi_request *request = (fcgi_request*) server_context;
+ if (
+#ifndef PHP_WIN32
+ !parent &&
+#endif
+ request && !fcgi_flush(request, 0)) {
+ php_handle_aborted_connection();
+ }
+ return;
+ }
+
+ /* fpm has not started yet, let use stdout instead of fcgi */
+ if (fflush(stdout) == EOF) {
+ php_handle_aborted_connection();
+ }
+}
+
+#define SAPI_CGI_MAX_HEADER_LENGTH 1024
+
+typedef struct _http_error {
+ int code;
+ const char* msg;
+} http_error;
+
+static const http_error http_error_codes[] = {
+ {100, "Continue"},
+ {101, "Switching Protocols"},
+ {200, "OK"},
+ {201, "Created"},
+ {202, "Accepted"},
+ {203, "Non-Authoritative Information"},
+ {204, "No Content"},
+ {205, "Reset Content"},
+ {206, "Partial Content"},
+ {300, "Multiple Choices"},
+ {301, "Moved Permanently"},
+ {302, "Moved Temporarily"},
+ {303, "See Other"},
+ {304, "Not Modified"},
+ {305, "Use Proxy"},
+ {400, "Bad Request"},
+ {401, "Unauthorized"},
+ {402, "Payment Required"},
+ {403, "Forbidden"},
+ {404, "Not Found"},
+ {405, "Method Not Allowed"},
+ {406, "Not Acceptable"},
+ {407, "Proxy Authentication Required"},
+ {408, "Request Time-out"},
+ {409, "Conflict"},
+ {410, "Gone"},
+ {411, "Length Required"},
+ {412, "Precondition Failed"},
+ {413, "Request Entity Too Large"},
+ {414, "Request-URI Too Large"},
+ {415, "Unsupported Media Type"},
+ {428, "Precondition Required"},
+ {429, "Too Many Requests"},
+ {431, "Request Header Fields Too Large"},
+ {500, "Internal Server Error"},
+ {501, "Not Implemented"},
+ {502, "Bad Gateway"},
+ {503, "Service Unavailable"},
+ {504, "Gateway Time-out"},
+ {505, "HTTP Version not supported"},
+ {511, "Network Authentication Required"},
+ {0, NULL}
+};
+
+static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ char buf[SAPI_CGI_MAX_HEADER_LENGTH];
+ sapi_header_struct *h;
+ zend_llist_position pos;
+ zend_bool ignore_status = 0;
+ int response_status = SG(sapi_headers).http_response_code;
+
+ if (SG(request_info).no_headers == 1) {
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+ }
+
+ if (CGIG(nph) || SG(sapi_headers).http_response_code != 200)
+ {
+ int len;
+ zend_bool has_status = 0;
+
+ if (CGIG(rfc2616_headers) && SG(sapi_headers).http_status_line) {
+ char *s;
+ len = slprintf(buf, SAPI_CGI_MAX_HEADER_LENGTH, "%s\r\n", SG(sapi_headers).http_status_line);
+ if ((s = strchr(SG(sapi_headers).http_status_line, ' '))) {
+ response_status = atoi((s + 1));
+ }
+
+ if (len > SAPI_CGI_MAX_HEADER_LENGTH) {
+ len = SAPI_CGI_MAX_HEADER_LENGTH;
+ }
+
+ } else {
+ char *s;
+
+ if (SG(sapi_headers).http_status_line &&
+ (s = strchr(SG(sapi_headers).http_status_line, ' ')) != 0 &&
+ (s - SG(sapi_headers).http_status_line) >= 5 &&
+ strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0
+ ) {
+ len = slprintf(buf, sizeof(buf), "Status:%s\r\n", s);
+ response_status = atoi((s + 1));
+ } else {
+ h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+ if (h->header_len > sizeof("Status:") - 1 &&
+ strncasecmp(h->header, "Status:", sizeof("Status:") - 1) == 0
+ ) {
+ has_status = 1;
+ break;
+ }
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ }
+ if (!has_status) {
+ http_error *err = (http_error*)http_error_codes;
+
+ while (err->code != 0) {
+ if (err->code == SG(sapi_headers).http_response_code) {
+ break;
+ }
+ err++;
+ }
+ if (err->msg) {
+ len = slprintf(buf, sizeof(buf), "Status: %d %s\r\n", SG(sapi_headers).http_response_code, err->msg);
+ } else {
+ len = slprintf(buf, sizeof(buf), "Status: %d\r\n", SG(sapi_headers).http_response_code);
+ }
+ }
+ }
+ }
+
+ if (!has_status) {
+ PHPWRITE_H(buf, len);
+ ignore_status = 1;
+ }
+ }
+
+ h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+ /* prevent CRLFCRLF */
+ if (h->header_len) {
+ if (h->header_len > sizeof("Status:") - 1 &&
+ strncasecmp(h->header, "Status:", sizeof("Status:") - 1) == 0
+ ) {
+ if (!ignore_status) {
+ ignore_status = 1;
+ PHPWRITE_H(h->header, h->header_len);
+ PHPWRITE_H("\r\n", 2);
+ }
+ } else if (response_status == 304 && h->header_len > sizeof("Content-Type:") - 1 &&
+ strncasecmp(h->header, "Content-Type:", sizeof("Content-Type:") - 1) == 0
+ ) {
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ continue;
+ } else {
+ PHPWRITE_H(h->header, h->header_len);
+ PHPWRITE_H("\r\n", 2);
+ }
+ }
+ h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ }
+ PHPWRITE_H("\r\n", 2);
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+#ifndef STDIN_FILENO
+# define STDIN_FILENO 0
+#endif
+
+static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ uint read_bytes = 0;
+ int tmp_read_bytes;
+
+ count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes));
+ while (read_bytes < count_bytes) {
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+ if (request_body_fd == -1) {
+ char *request_body_filename = sapi_cgibin_getenv((char *) "REQUEST_BODY_FILE",
+ sizeof("REQUEST_BODY_FILE") - 1 TSRMLS_CC);
+
+ if (request_body_filename && *request_body_filename) {
+ request_body_fd = open(request_body_filename, O_RDONLY);
+
+ if (0 > request_body_fd) {
+ php_error(E_WARNING, "REQUEST_BODY_FILE: open('%s') failed: %s (%d)",
+ request_body_filename, strerror(errno), errno);
+ return 0;
+ }
+ }
+ }
+
+ /* If REQUEST_BODY_FILE variable not available - read post body from fastcgi stream */
+ if (request_body_fd < 0) {
+ tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes);
+ } else {
+ tmp_read_bytes = read(request_body_fd, buffer + read_bytes, count_bytes - read_bytes);
+ }
+ if (tmp_read_bytes <= 0) {
+ break;
+ }
+ read_bytes += tmp_read_bytes;
+ }
+ return read_bytes;
+}
+
+static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC)
+{
+ /* if fpm has started, use fcgi env */
+ if (fpm_is_running) {
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+ return fcgi_getenv(request, name, name_len);
+ }
+
+ /* if fpm has not started yet, use std env */
+ return getenv(name);
+}
+
+static char *_sapi_cgibin_putenv(char *name, char *value TSRMLS_DC)
+{
+ int name_len;
+
+ if (!name) {
+ return NULL;
+ }
+ name_len = strlen(name);
+
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+ return fcgi_putenv(request, name, name_len, value);
+}
+
+static char *sapi_cgi_read_cookies(TSRMLS_D)
+{
+ return sapi_cgibin_getenv((char *) "HTTP_COOKIE", sizeof("HTTP_COOKIE") - 1 TSRMLS_CC);
+}
+
+void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC)
+{
+ fcgi_request *request;
+ HashPosition pos;
+ char *var, **val;
+ uint var_len;
+ ulong idx;
+ int filter_arg;
+
+
+ if (PG(http_globals)[TRACK_VARS_ENV] &&
+ array_ptr != PG(http_globals)[TRACK_VARS_ENV] &&
+ Z_TYPE_P(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY &&
+ zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_ENV])) > 0
+ ) {
+ zval_dtor(array_ptr);
+ *array_ptr = *PG(http_globals)[TRACK_VARS_ENV];
+ INIT_PZVAL(array_ptr);
+ zval_copy_ctor(array_ptr);
+ return;
+ } else if (PG(http_globals)[TRACK_VARS_SERVER] &&
+ array_ptr != PG(http_globals)[TRACK_VARS_SERVER] &&
+ Z_TYPE_P(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY &&
+ zend_hash_num_elements(Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_SERVER])) > 0
+ ) {
+ zval_dtor(array_ptr);
+ *array_ptr = *PG(http_globals)[TRACK_VARS_SERVER];
+ INIT_PZVAL(array_ptr);
+ zval_copy_ctor(array_ptr);
+ return;
+ }
+
+ /* call php's original import as a catch-all */
+ php_php_import_environment_variables(array_ptr TSRMLS_CC);
+
+ request = (fcgi_request*) SG(server_context);
+ filter_arg = (array_ptr == PG(http_globals)[TRACK_VARS_ENV])?PARSE_ENV:PARSE_SERVER;
+
+ for (zend_hash_internal_pointer_reset_ex(request->env, &pos);
+ zend_hash_get_current_key_ex(request->env, &var, &var_len, &idx, 0, &pos) == HASH_KEY_IS_STRING &&
+ zend_hash_get_current_data_ex(request->env, (void **) &val, &pos) == SUCCESS;
+ zend_hash_move_forward_ex(request->env, &pos)
+ ) {
+ unsigned int new_val_len;
+
+ if (sapi_module.input_filter(filter_arg, var, val, strlen(*val), &new_val_len TSRMLS_CC)) {
+ php_register_variable_safe(var, *val, new_val_len, array_ptr TSRMLS_CC);
+ }
+ }
+}
+
+static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ unsigned int php_self_len;
+ char *php_self;
+
+ /* In CGI mode, we consider the environment to be a part of the server
+ * variables
+ */
+ php_import_environment_variables(track_vars_array TSRMLS_CC);
+
+ if (CGIG(fix_pathinfo)) {
+ char *script_name = SG(request_info).request_uri;
+ unsigned int script_name_len = script_name ? strlen(script_name) : 0;
+ char *path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO") - 1 TSRMLS_CC);
+ unsigned int path_info_len = path_info ? strlen(path_info) : 0;
+
+ php_self_len = script_name_len + path_info_len;
+ php_self = emalloc(php_self_len + 1);
+
+ /* Concat script_name and path_info into php_self */
+ if (script_name) {
+ memcpy(php_self, script_name, script_name_len + 1);
+ }
+ if (path_info) {
+ memcpy(php_self + script_name_len, path_info, path_info_len + 1);
+ }
+
+ /* Build the special-case PHP_SELF variable for the CGI version */
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) {
+ php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC);
+ }
+ efree(php_self);
+ } else {
+ php_self = SG(request_info).request_uri ? SG(request_info).request_uri : "";
+ php_self_len = strlen(php_self);
+ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) {
+ php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC);
+ }
+ }
+}
+
+/* {{{ sapi_cgi_log_fastcgi
+ *
+ * Ignore level, we want to send all messages through fastcgi
+ */
+void sapi_cgi_log_fastcgi(int level, char *message, size_t len)
+{
+ TSRMLS_FETCH();
+
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+
+ /* ensure we want:
+ * - to log (fastcgi.logging in php.ini)
+ * - we are currently dealing with a request
+ * - the message is not empty
+ */
+ if (CGIG(fcgi_logging) && request && message && len > 0) {
+ char *buf = malloc(len + 2);
+ memcpy(buf, message, len);
+ memcpy(buf + len, "\n", sizeof("\n"));
+ fcgi_write(request, FCGI_STDERR, buf, len+1);
+ free(buf);
+ }
+}
+/* }}} */
+
+/* {{{ sapi_cgi_log_message
+ */
+static void sapi_cgi_log_message(char *message)
+{
+ zlog(ZLOG_NOTICE, "PHP message: %s", message);
+}
+/* }}} */
+
+/* {{{ php_cgi_ini_activate_user_config
+ */
+static void php_cgi_ini_activate_user_config(char *path, int path_len, const char *doc_root, int doc_root_len, int start TSRMLS_DC)
+{
+ char *ptr;
+ user_config_cache_entry *new_entry, *entry;
+ time_t request_time = sapi_get_request_time(TSRMLS_C);
+
+ /* Find cached config entry: If not found, create one */
+ if (zend_hash_find(&CGIG(user_config_cache), path, path_len + 1, (void **) &entry) == FAILURE) {
+ new_entry = pemalloc(sizeof(user_config_cache_entry), 1);
+ new_entry->expires = 0;
+ new_entry->user_config = (HashTable *) pemalloc(sizeof(HashTable), 1);
+ zend_hash_init(new_entry->user_config, 0, NULL, (dtor_func_t) config_zval_dtor, 1);
+ zend_hash_update(&CGIG(user_config_cache), path, path_len + 1, new_entry, sizeof(user_config_cache_entry), (void **) &entry);
+ free(new_entry);
+ }
+
+ /* Check whether cache entry has expired and rescan if it is */
+ if (request_time > entry->expires) {
+ char * real_path;
+ int real_path_len;
+ char *s1, *s2;
+ int s_len;
+
+ /* Clear the expired config */
+ zend_hash_clean(entry->user_config);
+
+ if (!IS_ABSOLUTE_PATH(path, path_len)) {
+ real_path = tsrm_realpath(path, NULL TSRMLS_CC);
+ if (real_path == NULL) {
+ return;
+ }
+ real_path_len = strlen(real_path);
+ path = real_path;
+ path_len = real_path_len;
+ }
+
+ if (path_len > doc_root_len) {
+ s1 = (char *) doc_root;
+ s2 = path;
+ s_len = doc_root_len;
+ } else {
+ s1 = path;
+ s2 = (char *) doc_root;
+ s_len = path_len;
+ }
+
+ /* we have to test if path is part of DOCUMENT_ROOT.
+ if it is inside the docroot, we scan the tree up to the docroot
+ to find more user.ini, if not we only scan the current path.
+ */
+#ifdef PHP_WIN32
+ if (strnicmp(s1, s2, s_len) == 0) {
+#else
+ if (strncmp(s1, s2, s_len) == 0) {
+#endif
+ ptr = s2 + start; /* start is the point where doc_root ends! */
+ while ((ptr = strchr(ptr, DEFAULT_SLASH)) != NULL) {
+ *ptr = 0;
+ php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC);
+ *ptr = '/';
+ ptr++;
+ }
+ } else {
+ php_parse_user_ini_file(path, PG(user_ini_filename), entry->user_config TSRMLS_CC);
+ }
+
+ entry->expires = request_time + PG(user_ini_cache_ttl);
+ }
+
+ /* Activate ini entries with values from the user config hash */
+ php_ini_activate_config(entry->user_config, PHP_INI_PERDIR, PHP_INI_STAGE_HTACCESS TSRMLS_CC);
+}
+/* }}} */
+
+static int sapi_cgi_activate(TSRMLS_D)
+{
+ char *path, *doc_root, *server_name;
+ uint path_len, doc_root_len, server_name_len;
+
+ /* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */
+ if (!SG(request_info).path_translated) {
+ return FAILURE;
+ }
+
+ if (php_ini_has_per_host_config()) {
+ /* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */
+ server_name = sapi_cgibin_getenv("SERVER_NAME", sizeof("SERVER_NAME") - 1 TSRMLS_CC);
+ /* SERVER_NAME should also be defined at this stage..but better check it anyway */
+ if (server_name) {
+ server_name_len = strlen(server_name);
+ server_name = estrndup(server_name, server_name_len);
+ zend_str_tolower(server_name, server_name_len);
+ php_ini_activate_per_host_config(server_name, server_name_len + 1 TSRMLS_CC);
+ efree(server_name);
+ }
+ }
+
+ if (php_ini_has_per_dir_config() ||
+ (PG(user_ini_filename) && *PG(user_ini_filename))
+ ) {
+ /* Prepare search path */
+ path_len = strlen(SG(request_info).path_translated);
+
+ /* Make sure we have trailing slash! */
+ if (!IS_SLASH(SG(request_info).path_translated[path_len])) {
+ path = emalloc(path_len + 2);
+ memcpy(path, SG(request_info).path_translated, path_len + 1);
+ path_len = zend_dirname(path, path_len);
+ path[path_len++] = DEFAULT_SLASH;
+ } else {
+ path = estrndup(SG(request_info).path_translated, path_len);
+ path_len = zend_dirname(path, path_len);
+ }
+ path[path_len] = 0;
+
+ /* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */
+ php_ini_activate_per_dir_config(path, path_len TSRMLS_CC); /* Note: for global settings sake we check from root to path */
+
+ /* Load and activate user ini files in path starting from DOCUMENT_ROOT */
+ if (PG(user_ini_filename) && *PG(user_ini_filename)) {
+ doc_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1 TSRMLS_CC);
+ /* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */
+ if (doc_root) {
+ doc_root_len = strlen(doc_root);
+ if (doc_root_len > 0 && IS_SLASH(doc_root[doc_root_len - 1])) {
+ --doc_root_len;
+ }
+#ifdef PHP_WIN32
+ /* paths on windows should be case-insensitive */
+ doc_root = estrndup(doc_root, doc_root_len);
+ zend_str_tolower(doc_root, doc_root_len);
+#endif
+ php_cgi_ini_activate_user_config(path, path_len, doc_root, doc_root_len, doc_root_len - 1 TSRMLS_CC);
+ }
+ }
+
+#ifdef PHP_WIN32
+ efree(doc_root);
+#endif
+ efree(path);
+ }
+
+ return SUCCESS;
+}
+
+static int sapi_cgi_deactivate(TSRMLS_D)
+{
+ /* flush only when SAPI was started. The reasons are:
+ 1. SAPI Deactivate is called from two places: module init and request shutdown
+ 2. When the first call occurs and the request is not set up, flush fails on FastCGI.
+ */
+ if (SG(sapi_started)) {
+ if (
+#ifndef PHP_WIN32
+ !parent &&
+#endif
+ !fcgi_finish_request((fcgi_request*)SG(server_context), 0)) {
+ php_handle_aborted_connection();
+ }
+ }
+ return SUCCESS;
+}
+
+static int php_cgi_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &cgi_module_entry, 1) == FAILURE) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+/* {{{ sapi_module_struct cgi_sapi_module
+ */
+static sapi_module_struct cgi_sapi_module = {
+ "fpm-fcgi", /* name */
+ "FPM/FastCGI", /* pretty name */
+
+ php_cgi_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ sapi_cgi_activate, /* activate */
+ sapi_cgi_deactivate, /* deactivate */
+
+ sapi_cgibin_ub_write, /* unbuffered write */
+ sapi_cgibin_flush, /* flush */
+ NULL, /* get uid */
+ sapi_cgibin_getenv, /* getenv */
+
+ php_error, /* error handler */
+
+ NULL, /* header handler */
+ sapi_cgi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ sapi_cgi_read_post, /* read POST data */
+ sapi_cgi_read_cookies, /* read Cookies */
+
+ sapi_cgi_register_variables, /* register server variables */
+ sapi_cgi_log_message, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+/* }}} */
+
+/* {{{ arginfo ext/standard/dl.c */
+ZEND_BEGIN_ARG_INFO(arginfo_dl, 0)
+ ZEND_ARG_INFO(0, extension_filename)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+static const zend_function_entry additional_functions[] = {
+ ZEND_FE(dl, arginfo_dl)
+ {NULL, NULL, NULL}
+};
+
+/* {{{ php_cgi_usage
+ */
+static void php_cgi_usage(char *argv0)
+{
+ char *prog;
+
+ prog = strrchr(argv0, '/');
+ if (prog) {
+ prog++;
+ } else {
+ prog = "php";
+ }
+
+ php_printf( "Usage: %s [-n] [-e] [-h] [-i] [-m] [-v] [-t] [-p <prefix>] [-g <pid>] [-c <file>] [-d foo[=bar]] [-y <file>] [-D] [-F]\n"
+ " -c <path>|<file> Look for php.ini file in this directory\n"
+ " -n No php.ini file will be used\n"
+ " -d foo[=bar] Define INI entry foo with value 'bar'\n"
+ " -e Generate extended information for debugger/profiler\n"
+ " -h This help\n"
+ " -i PHP information\n"
+ " -m Show compiled in modules\n"
+ " -v Version number\n"
+ " -p, --prefix <dir>\n"
+ " Specify alternative prefix path to FastCGI process manager (default: %s).\n"
+ " -g, --pid <file>\n"
+ " Specify the PID file location.\n"
+ " -y, --fpm-config <file>\n"
+ " Specify alternative path to FastCGI process manager config file.\n"
+ " -t, --test Test FPM configuration and exit\n"
+ " -D, --daemonize force to run in background, and ignore daemonize option from config file\n"
+ " -F, --nodaemonize\n"
+ " force to stay in foreground, and ignore daemonize option from config file\n"
+ " -R, --allow-to-run-as-root\n"
+ " Allow pool to run as root (disabled by default)\n",
+ prog, PHP_PREFIX);
+}
+/* }}} */
+
+/* {{{ is_valid_path
+ *
+ * some server configurations allow '..' to slip through in the
+ * translated path. We'll just refuse to handle such a path.
+ */
+static int is_valid_path(const char *path)
+{
+ const char *p;
+
+ if (!path) {
+ return 0;
+ }
+ p = strstr(path, "..");
+ if (p) {
+ if ((p == path || IS_SLASH(*(p-1))) &&
+ (*(p+2) == 0 || IS_SLASH(*(p+2)))
+ ) {
+ return 0;
+ }
+ while (1) {
+ p = strstr(p+1, "..");
+ if (!p) {
+ break;
+ }
+ if (IS_SLASH(*(p-1)) &&
+ (*(p+2) == 0 || IS_SLASH(*(p+2)))
+ ) {
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+/* }}} */
+
+/* {{{ init_request_info
+
+ initializes request_info structure
+
+ specificly in this section we handle proper translations
+ for:
+
+ PATH_INFO
+ derived from the portion of the URI path following
+ the script name but preceding any query data
+ may be empty
+
+ PATH_TRANSLATED
+ derived by taking any path-info component of the
+ request URI and performing any virtual-to-physical
+ translation appropriate to map it onto the server's
+ document repository structure
+
+ empty if PATH_INFO is empty
+
+ The env var PATH_TRANSLATED **IS DIFFERENT** than the
+ request_info.path_translated variable, the latter should
+ match SCRIPT_FILENAME instead.
+
+ SCRIPT_NAME
+ set to a URL path that could identify the CGI script
+ rather than the interpreter. PHP_SELF is set to this
+
+ REQUEST_URI
+ uri section following the domain:port part of a URI
+
+ SCRIPT_FILENAME
+ The virtual-to-physical translation of SCRIPT_NAME (as per
+ PATH_TRANSLATED)
+
+ These settings are documented at
+ http://cgi-spec.golux.com/
+
+
+ Based on the following URL request:
+
+ http://localhost/info.php/test?a=b
+
+ should produce, which btw is the same as if
+ we were running under mod_cgi on apache (ie. not
+ using ScriptAlias directives):
+
+ PATH_INFO=/test
+ PATH_TRANSLATED=/docroot/test
+ SCRIPT_NAME=/info.php
+ REQUEST_URI=/info.php/test?a=b
+ SCRIPT_FILENAME=/docroot/info.php
+ QUERY_STRING=a=b
+
+ but what we get is (cgi/mod_fastcgi under apache):
+
+ PATH_INFO=/info.php/test
+ PATH_TRANSLATED=/docroot/info.php/test
+ SCRIPT_NAME=/php/php-cgi (from the Action setting I suppose)
+ REQUEST_URI=/info.php/test?a=b
+ SCRIPT_FILENAME=/path/to/php/bin/php-cgi (Action setting translated)
+ QUERY_STRING=a=b
+
+ Comments in the code below refer to using the above URL in a request
+
+ */
+static void init_request_info(TSRMLS_D)
+{
+ char *env_script_filename = sapi_cgibin_getenv("SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME") - 1 TSRMLS_CC);
+ char *env_path_translated = sapi_cgibin_getenv("PATH_TRANSLATED", sizeof("PATH_TRANSLATED") - 1 TSRMLS_CC);
+ char *script_path_translated = env_script_filename;
+ char *ini;
+ int apache_was_here = 0;
+
+ /* some broken servers do not have script_filename or argv0
+ * an example, IIS configured in some ways. then they do more
+ * broken stuff and set path_translated to the cgi script location */
+ if (!script_path_translated && env_path_translated) {
+ script_path_translated = env_path_translated;
+ }
+
+ /* initialize the defaults */
+ SG(request_info).path_translated = NULL;
+ SG(request_info).request_method = NULL;
+ SG(request_info).proto_num = 1000;
+ SG(request_info).query_string = NULL;
+ SG(request_info).request_uri = NULL;
+ SG(request_info).content_type = NULL;
+ SG(request_info).content_length = 0;
+ SG(sapi_headers).http_response_code = 200;
+
+ /* script_path_translated being set is a good indication that
+ * we are running in a cgi environment, since it is always
+ * null otherwise. otherwise, the filename
+ * of the script will be retreived later via argc/argv */
+ if (script_path_translated) {
+ const char *auth;
+ char *content_length = sapi_cgibin_getenv("CONTENT_LENGTH", sizeof("CONTENT_LENGTH") - 1 TSRMLS_CC);
+ char *content_type = sapi_cgibin_getenv("CONTENT_TYPE", sizeof("CONTENT_TYPE") - 1 TSRMLS_CC);
+ char *env_path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO") - 1 TSRMLS_CC);
+ char *env_script_name = sapi_cgibin_getenv("SCRIPT_NAME", sizeof("SCRIPT_NAME") - 1 TSRMLS_CC);
+
+ /* Hack for buggy IIS that sets incorrect PATH_INFO */
+ char *env_server_software = sapi_cgibin_getenv("SERVER_SOFTWARE", sizeof("SERVER_SOFTWARE") - 1 TSRMLS_CC);
+ if (env_server_software &&
+ env_script_name &&
+ env_path_info &&
+ strncmp(env_server_software, "Microsoft-IIS", sizeof("Microsoft-IIS") - 1) == 0 &&
+ strncmp(env_path_info, env_script_name, strlen(env_script_name)) == 0
+ ) {
+ env_path_info = _sapi_cgibin_putenv("ORIG_PATH_INFO", env_path_info TSRMLS_CC);
+ env_path_info += strlen(env_script_name);
+ if (*env_path_info == 0) {
+ env_path_info = NULL;
+ }
+ env_path_info = _sapi_cgibin_putenv("PATH_INFO", env_path_info TSRMLS_CC);
+ }
+
+#define APACHE_PROXY_FCGI_PREFIX "proxy:fcgi://"
+ /* Fix proxy URLs in SCRIPT_FILENAME generated by Apache mod_proxy_fcgi:
+ * proxy:fcgi://localhost:9000/some-dir/info.php/test
+ * should be changed to:
+ * /some-dir/info.php/test
+ * See: http://bugs.php.net/bug.php?id=54152
+ * https://issues.apache.org/bugzilla/show_bug.cgi?id=50851
+ */
+ if (env_script_filename &&
+ strncasecmp(env_script_filename, APACHE_PROXY_FCGI_PREFIX, sizeof(APACHE_PROXY_FCGI_PREFIX) - 1) == 0) {
+ /* advance to first character of hostname */
+ char *p = env_script_filename + (sizeof(APACHE_PROXY_FCGI_PREFIX) - 1);
+ while (*p != '\0' && *p != '/') {
+ p++; /* move past hostname and port */
+ }
+ if (*p != '\0') {
+ /* Copy path portion in place to avoid memory leak. Note
+ * that this also affects what script_path_translated points
+ * to. */
+ memmove(env_script_filename, p, strlen(p) + 1);
+ apache_was_here = 1;
+ }
+ }
+
+ if (CGIG(fix_pathinfo)) {
+ struct stat st;
+ char *real_path = NULL;
+ char *env_redirect_url = sapi_cgibin_getenv("REDIRECT_URL", sizeof("REDIRECT_URL") - 1 TSRMLS_CC);
+ char *env_document_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1 TSRMLS_CC);
+ char *orig_path_translated = env_path_translated;
+ char *orig_path_info = env_path_info;
+ char *orig_script_name = env_script_name;
+ char *orig_script_filename = env_script_filename;
+ int script_path_translated_len;
+
+ if (!env_document_root && PG(doc_root)) {
+ env_document_root = _sapi_cgibin_putenv("DOCUMENT_ROOT", PG(doc_root) TSRMLS_CC);
+ /* fix docroot */
+ TRANSLATE_SLASHES(env_document_root);
+ }
+
+ if (env_path_translated != NULL && env_redirect_url != NULL &&
+ env_path_translated != script_path_translated &&
+ strcmp(env_path_translated, script_path_translated) != 0) {
+ /*
+ * pretty much apache specific. If we have a redirect_url
+ * then our script_filename and script_name point to the
+ * php executable
+ */
+ script_path_translated = env_path_translated;
+ /* we correct SCRIPT_NAME now in case we don't have PATH_INFO */
+ env_script_name = env_redirect_url;
+ }
+
+#ifdef __riscos__
+ /* Convert path to unix format*/
+ __riscosify_control |= __RISCOSIFY_DONT_CHECK_DIR;
+ script_path_translated = __unixify(script_path_translated, 0, NULL, 1, 0);
+#endif
+
+ /*
+ * if the file doesn't exist, try to extract PATH_INFO out
+ * of it by stat'ing back through the '/'
+ * this fixes url's like /info.php/test
+ */
+ if (script_path_translated &&
+ (script_path_translated_len = strlen(script_path_translated)) > 0 &&
+ (script_path_translated[script_path_translated_len-1] == '/' ||
+#ifdef PHP_WIN32
+ script_path_translated[script_path_translated_len-1] == '\\' ||
+#endif
+ (real_path = tsrm_realpath(script_path_translated, NULL TSRMLS_CC)) == NULL)
+ ) {
+ char *pt = estrndup(script_path_translated, script_path_translated_len);
+ int len = script_path_translated_len;
+ char *ptr;
+
+ while ((ptr = strrchr(pt, '/')) || (ptr = strrchr(pt, '\\'))) {
+ *ptr = 0;
+ if (stat(pt, &st) == 0 && S_ISREG(st.st_mode)) {
+ /*
+ * okay, we found the base script!
+ * work out how many chars we had to strip off;
+ * then we can modify PATH_INFO
+ * accordingly
+ *
+ * we now have the makings of
+ * PATH_INFO=/test
+ * SCRIPT_FILENAME=/docroot/info.php
+ *
+ * we now need to figure out what docroot is.
+ * if DOCUMENT_ROOT is set, this is easy, otherwise,
+ * we have to play the game of hide and seek to figure
+ * out what SCRIPT_NAME should be
+ */
+ int ptlen = strlen(pt);
+ int slen = len - ptlen;
+ int pilen = env_path_info ? strlen(env_path_info) : 0;
+ int tflag = 0;
+ char *path_info;
+ if (apache_was_here) {
+ /* recall that PATH_INFO won't exist */
+ path_info = script_path_translated + ptlen;
+ tflag = (slen != 0 && (!orig_path_info || strcmp(orig_path_info, path_info) != 0));
+ } else {
+ path_info = env_path_info ? env_path_info + pilen - slen : NULL;
+ tflag = (orig_path_info != path_info);
+ }
+
+ if (tflag) {
+ if (orig_path_info) {
+ char old;
+
+ _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC);
+ old = path_info[0];
+ path_info[0] = 0;
+ if (!orig_script_name ||
+ strcmp(orig_script_name, env_path_info) != 0) {
+ if (orig_script_name) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
+ }
+ SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_path_info TSRMLS_CC);
+ } else {
+ SG(request_info).request_uri = orig_script_name;
+ }
+ path_info[0] = old;
+ }
+ env_path_info = _sapi_cgibin_putenv("PATH_INFO", path_info TSRMLS_CC);
+ }
+ if (!orig_script_filename ||
+ strcmp(orig_script_filename, pt) != 0) {
+ if (orig_script_filename) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
+ }
+ script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", pt TSRMLS_CC);
+ }
+ TRANSLATE_SLASHES(pt);
+
+ /* figure out docroot
+ * SCRIPT_FILENAME minus SCRIPT_NAME
+ */
+ if (env_document_root) {
+ int l = strlen(env_document_root);
+ int path_translated_len = 0;
+ char *path_translated = NULL;
+
+ if (l && env_document_root[l - 1] == '/') {
+ --l;
+ }
+
+ /* we have docroot, so we should have:
+ * DOCUMENT_ROOT=/docroot
+ * SCRIPT_FILENAME=/docroot/info.php
+ */
+
+ /* PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO */
+ path_translated_len = l + (env_path_info ? strlen(env_path_info) : 0);
+ path_translated = (char *) emalloc(path_translated_len + 1);
+ memcpy(path_translated, env_document_root, l);
+ if (env_path_info) {
+ memcpy(path_translated + l, env_path_info, (path_translated_len - l));
+ }
+ path_translated[path_translated_len] = '\0';
+ if (orig_path_translated) {
+ _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
+ }
+ env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC);
+ efree(path_translated);
+ } else if ( env_script_name &&
+ strstr(pt, env_script_name)
+ ) {
+ /* PATH_TRANSLATED = PATH_TRANSLATED - SCRIPT_NAME + PATH_INFO */
+ int ptlen = strlen(pt) - strlen(env_script_name);
+ int path_translated_len = ptlen + (env_path_info ? strlen(env_path_info) : 0);
+ char *path_translated = NULL;
+
+ path_translated = (char *) emalloc(path_translated_len + 1);
+ memcpy(path_translated, pt, ptlen);
+ if (env_path_info) {
+ memcpy(path_translated + ptlen, env_path_info, path_translated_len - ptlen);
+ }
+ path_translated[path_translated_len] = '\0';
+ if (orig_path_translated) {
+ _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
+ }
+ env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC);
+ efree(path_translated);
+ }
+ break;
+ }
+ }
+ if (!ptr) {
+ /*
+ * if we stripped out all the '/' and still didn't find
+ * a valid path... we will fail, badly. of course we would
+ * have failed anyway... we output 'no input file' now.
+ */
+ if (orig_script_filename) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
+ }
+ script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", NULL TSRMLS_CC);
+ SG(sapi_headers).http_response_code = 404;
+ }
+ if (!SG(request_info).request_uri) {
+ if (!orig_script_name ||
+ strcmp(orig_script_name, env_script_name) != 0) {
+ if (orig_script_name) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
+ }
+ SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC);
+ } else {
+ SG(request_info).request_uri = orig_script_name;
+ }
+ }
+ if (pt) {
+ efree(pt);
+ }
+ } else {
+ /* make sure path_info/translated are empty */
+ if (!orig_script_filename ||
+ (script_path_translated != orig_script_filename &&
+ strcmp(script_path_translated, orig_script_filename) != 0)) {
+ if (orig_script_filename) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC);
+ }
+ script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", script_path_translated TSRMLS_CC);
+ }
+ if (env_redirect_url) {
+ if (orig_path_info) {
+ _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC);
+ _sapi_cgibin_putenv("PATH_INFO", NULL TSRMLS_CC);
+ }
+ if (orig_path_translated) {
+ _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC);
+ _sapi_cgibin_putenv("PATH_TRANSLATED", NULL TSRMLS_CC);
+ }
+ }
+ if (env_script_name != orig_script_name) {
+ if (orig_script_name) {
+ _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC);
+ }
+ SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC);
+ } else {
+ SG(request_info).request_uri = env_script_name;
+ }
+ free(real_path);
+ }
+ } else {
+ /* pre 4.3 behaviour, shouldn't be used but provides BC */
+ if (env_path_info) {
+ SG(request_info).request_uri = env_path_info;
+ } else {
+ SG(request_info).request_uri = env_script_name;
+ }
+ if (!CGIG(discard_path) && env_path_translated) {
+ script_path_translated = env_path_translated;
+ }
+ }
+
+ if (is_valid_path(script_path_translated)) {
+ SG(request_info).path_translated = estrdup(script_path_translated);
+ }
+
+ SG(request_info).request_method = sapi_cgibin_getenv("REQUEST_METHOD", sizeof("REQUEST_METHOD") - 1 TSRMLS_CC);
+ /* FIXME - Work out proto_num here */
+ SG(request_info).query_string = sapi_cgibin_getenv("QUERY_STRING", sizeof("QUERY_STRING") - 1 TSRMLS_CC);
+ SG(request_info).content_type = (content_type ? content_type : "" );
+ SG(request_info).content_length = (content_length ? atol(content_length) : 0);
+
+ /* The CGI RFC allows servers to pass on unvalidated Authorization data */
+ auth = sapi_cgibin_getenv("HTTP_AUTHORIZATION", sizeof("HTTP_AUTHORIZATION") - 1 TSRMLS_CC);
+ php_handle_auth_data(auth TSRMLS_CC);
+ }
+
+ /* INI stuff */
+ ini = sapi_cgibin_getenv("PHP_VALUE", sizeof("PHP_VALUE") - 1 TSRMLS_CC);
+ if (ini) {
+ int mode = ZEND_INI_USER;
+ char *tmp;
+ spprintf(&tmp, 0, "%s\n", ini);
+ zend_parse_ini_string(tmp, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t)fastcgi_ini_parser, &mode TSRMLS_CC);
+ efree(tmp);
+ }
+
+ ini = sapi_cgibin_getenv("PHP_ADMIN_VALUE", sizeof("PHP_ADMIN_VALUE") - 1 TSRMLS_CC);
+ if (ini) {
+ int mode = ZEND_INI_SYSTEM;
+ char *tmp;
+ spprintf(&tmp, 0, "%s\n", ini);
+ zend_parse_ini_string(tmp, 1, ZEND_INI_SCANNER_NORMAL, (zend_ini_parser_cb_t)fastcgi_ini_parser, &mode TSRMLS_CC);
+ efree(tmp);
+ }
+}
+/* }}} */
+
+static void fastcgi_ini_parser(zval *arg1, zval *arg2, zval *arg3, int callback_type, void *arg TSRMLS_DC) /* {{{ */
+{
+ int *mode = (int *)arg;
+ char *key;
+ char *value = NULL;
+ struct key_value_s kv;
+
+ if (!mode || !arg1) return;
+
+ if (callback_type != ZEND_INI_PARSER_ENTRY) {
+ zlog(ZLOG_ERROR, "Passing INI directive through FastCGI: only classic entries are allowed");
+ return;
+ }
+
+ key = Z_STRVAL_P(arg1);
+
+ if (!key || strlen(key) < 1) {
+ zlog(ZLOG_ERROR, "Passing INI directive through FastCGI: empty key");
+ return;
+ }
+
+ if (arg2) {
+ value = Z_STRVAL_P(arg2);
+ }
+
+ if (!value) {
+ zlog(ZLOG_ERROR, "Passing INI directive through FastCGI: empty value for key '%s'", key);
+ return;
+ }
+
+ kv.key = key;
+ kv.value = value;
+ kv.next = NULL;
+ if (fpm_php_apply_defines_ex(&kv, *mode) == -1) {
+ zlog(ZLOG_ERROR, "Passing INI directive through FastCGI: unable to set '%s'", key);
+ }
+}
+/* }}} */
+
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY("cgi.rfc2616_headers", "0", PHP_INI_ALL, OnUpdateBool, rfc2616_headers, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.nph", "0", PHP_INI_ALL, OnUpdateBool, nph, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.force_redirect", "1", PHP_INI_SYSTEM, OnUpdateBool, force_redirect, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.redirect_status_env", NULL, PHP_INI_SYSTEM, OnUpdateString, redirect_status_env, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.fix_pathinfo", "1", PHP_INI_SYSTEM, OnUpdateBool, fix_pathinfo, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("cgi.discard_path", "0", PHP_INI_SYSTEM, OnUpdateBool, discard_path, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("fastcgi.logging", "1", PHP_INI_SYSTEM, OnUpdateBool, fcgi_logging, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("fastcgi.error_header", NULL, PHP_INI_SYSTEM, OnUpdateString, error_header, php_cgi_globals_struct, php_cgi_globals)
+ STD_PHP_INI_ENTRY("fpm.config", NULL, PHP_INI_SYSTEM, OnUpdateString, fpm_config, php_cgi_globals_struct, php_cgi_globals)
+PHP_INI_END()
+
+/* {{{ php_cgi_globals_ctor
+ */
+static void php_cgi_globals_ctor(php_cgi_globals_struct *php_cgi_globals TSRMLS_DC)
+{
+ php_cgi_globals->rfc2616_headers = 0;
+ php_cgi_globals->nph = 0;
+ php_cgi_globals->force_redirect = 1;
+ php_cgi_globals->redirect_status_env = NULL;
+ php_cgi_globals->fix_pathinfo = 1;
+ php_cgi_globals->discard_path = 0;
+ php_cgi_globals->fcgi_logging = 1;
+ zend_hash_init(&php_cgi_globals->user_config_cache, 0, NULL, (dtor_func_t) user_config_cache_entry_dtor, 1);
+ php_cgi_globals->error_header = NULL;
+ php_cgi_globals->fpm_config = NULL;
+}
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+static PHP_MINIT_FUNCTION(cgi)
+{
+#ifdef ZTS
+ ts_allocate_id(&php_cgi_globals_id, sizeof(php_cgi_globals_struct), (ts_allocate_ctor) php_cgi_globals_ctor, NULL);
+#else
+ php_cgi_globals_ctor(&php_cgi_globals TSRMLS_CC);
+#endif
+ REGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+static PHP_MSHUTDOWN_FUNCTION(cgi)
+{
+ zend_hash_destroy(&CGIG(user_config_cache));
+
+ UNREGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+static PHP_MINFO_FUNCTION(cgi)
+{
+ php_info_print_table_start();
+ php_info_print_table_row(2, "php-fpm", "active");
+ php_info_print_table_end();
+
+ DISPLAY_INI_ENTRIES();
+}
+/* }}} */
+
+PHP_FUNCTION(fastcgi_finish_request) /* {{{ */
+{
+ fcgi_request *request = (fcgi_request*) SG(server_context);
+
+ if (request->fd >= 0) {
+
+ php_output_end_all(TSRMLS_C);
+ php_header(TSRMLS_C);
+
+ fcgi_flush(request, 1);
+ fcgi_close(request, 0, 0);
+ RETURN_TRUE;
+ }
+
+ RETURN_FALSE;
+
+}
+/* }}} */
+
+static const zend_function_entry cgi_fcgi_sapi_functions[] = {
+ PHP_FE(fastcgi_finish_request, NULL)
+ {NULL, NULL, NULL}
+};
+
+static zend_module_entry cgi_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "cgi-fcgi",
+ cgi_fcgi_sapi_functions,
+ PHP_MINIT(cgi),
+ PHP_MSHUTDOWN(cgi),
+ NULL,
+ NULL,
+ PHP_MINFO(cgi),
+ NO_VERSION_YET,
+ STANDARD_MODULE_PROPERTIES
+};
+
+/* {{{ main
+ */
+int main(int argc, char *argv[])
+{
+ int exit_status = FPM_EXIT_OK;
+ int cgi = 0, c, use_extended_info = 0;
+ zend_file_handle file_handle;
+
+ /* temporary locals */
+ int orig_optind = php_optind;
+ char *orig_optarg = php_optarg;
+ int ini_entries_len = 0;
+ /* end of temporary locals */
+
+#ifdef ZTS
+ void ***tsrm_ls;
+#endif
+
+ int max_requests = 500;
+ int requests = 0;
+ int fcgi_fd = 0;
+ fcgi_request request;
+ char *fpm_config = NULL;
+ char *fpm_prefix = NULL;
+ char *fpm_pid = NULL;
+ int test_conf = 0;
+ int force_daemon = -1;
+ int php_information = 0;
+ int php_allow_to_run_as_root = 0;
+
+#ifdef HAVE_SIGNAL_H
+#if defined(SIGPIPE) && defined(SIG_IGN)
+ signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
+ that sockets created via fsockopen()
+ don't kill PHP if the remote site
+ closes it. in apache|apxs mode apache
+ does that for us! thies@thieso.net
+ 20000419 */
+#endif
+#endif
+
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+ tsrm_ls = ts_resource(0);
+#endif
+
+ sapi_startup(&cgi_sapi_module);
+ cgi_sapi_module.php_ini_path_override = NULL;
+ cgi_sapi_module.php_ini_ignore_cwd = 1;
+
+ fcgi_init();
+
+#ifdef PHP_WIN32
+ _fmode = _O_BINARY; /* sets default for file streams to binary */
+ setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
+#endif
+
+ while ((c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) {
+ switch (c) {
+ case 'c':
+ if (cgi_sapi_module.php_ini_path_override) {
+ free(cgi_sapi_module.php_ini_path_override);
+ }
+ cgi_sapi_module.php_ini_path_override = strdup(php_optarg);
+ break;
+
+ case 'n':
+ cgi_sapi_module.php_ini_ignore = 1;
+ break;
+
+ case 'd': {
+ /* define ini entries on command line */
+ int len = strlen(php_optarg);
+ char *val;
+
+ if ((val = strchr(php_optarg, '='))) {
+ val++;
+ if (!isalnum(*val) && *val != '"' && *val != '\'' && *val != '\0') {
+ cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\"\"\n\0"));
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, (val - php_optarg));
+ ini_entries_len += (val - php_optarg);
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"", 1);
+ ini_entries_len++;
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, val, len - (val - php_optarg));
+ ini_entries_len += len - (val - php_optarg);
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, "\"\n\0", sizeof("\"\n\0"));
+ ini_entries_len += sizeof("\n\0\"") - 2;
+ } else {
+ cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("\n\0"));
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "\n\0", sizeof("\n\0"));
+ ini_entries_len += len + sizeof("\n\0") - 2;
+ }
+ } else {
+ cgi_sapi_module.ini_entries = realloc(cgi_sapi_module.ini_entries, ini_entries_len + len + sizeof("=1\n\0"));
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len, php_optarg, len);
+ memcpy(cgi_sapi_module.ini_entries + ini_entries_len + len, "=1\n\0", sizeof("=1\n\0"));
+ ini_entries_len += len + sizeof("=1\n\0") - 2;
+ }
+ break;
+ }
+
+ case 'y':
+ fpm_config = php_optarg;
+ break;
+
+ case 'p':
+ fpm_prefix = php_optarg;
+ break;
+
+ case 'g':
+ fpm_pid = php_optarg;
+ break;
+
+ case 'e': /* enable extended info output */
+ use_extended_info = 1;
+ break;
+
+ case 't':
+ test_conf++;
+ break;
+
+ case 'm': /* list compiled in modules */
+ cgi_sapi_module.startup(&cgi_sapi_module);
+ php_output_activate(TSRMLS_C);
+ SG(headers_sent) = 1;
+ php_printf("[PHP Modules]\n");
+ print_modules(TSRMLS_C);
+ php_printf("\n[Zend Modules]\n");
+ print_extensions(TSRMLS_C);
+ php_printf("\n");
+ php_output_end_all(TSRMLS_C);
+ php_output_deactivate(TSRMLS_C);
+ fcgi_shutdown();
+ exit_status = FPM_EXIT_OK;
+ goto out;
+
+ case 'i': /* php info & quit */
+ php_information = 1;
+ break;
+
+ case 'R': /* allow to run as root */
+ php_allow_to_run_as_root = 1;
+ break;
+
+ case 'D': /* daemonize */
+ force_daemon = 1;
+ break;
+
+ case 'F': /* nodaemonize */
+ force_daemon = 0;
+ break;
+
+ default:
+ case 'h':
+ case '?':
+ cgi_sapi_module.startup(&cgi_sapi_module);
+ php_output_activate(TSRMLS_C);
+ SG(headers_sent) = 1;
+ php_cgi_usage(argv[0]);
+ php_output_end_all(TSRMLS_C);
+ php_output_deactivate(TSRMLS_C);
+ fcgi_shutdown();
+ exit_status = (c == 'h') ? FPM_EXIT_OK : FPM_EXIT_USAGE;
+ goto out;
+
+ case 'v': /* show php version & quit */
+ cgi_sapi_module.startup(&cgi_sapi_module);
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ SG(server_context) = NULL;
+ php_module_shutdown(TSRMLS_C);
+ return FPM_EXIT_SOFTWARE;
+ }
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+
+#if ZEND_DEBUG
+ php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2013 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
+#else
+ php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2013 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
+#endif
+ php_request_shutdown((void *) 0);
+ fcgi_shutdown();
+ exit_status = FPM_EXIT_OK;
+ goto out;
+ }
+ }
+
+ if (php_information) {
+ cgi_sapi_module.phpinfo_as_text = 1;
+ cgi_sapi_module.startup(&cgi_sapi_module);
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ SG(server_context) = NULL;
+ php_module_shutdown(TSRMLS_C);
+ return FPM_EXIT_SOFTWARE;
+ }
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+ php_print_info(0xFFFFFFFF TSRMLS_CC);
+ php_request_shutdown((void *) 0);
+ fcgi_shutdown();
+ exit_status = FPM_EXIT_OK;
+ goto out;
+ }
+
+ /* No other args are permitted here as there is no interactive mode */
+ if (argc != php_optind) {
+ cgi_sapi_module.startup(&cgi_sapi_module);
+ php_output_activate(TSRMLS_C);
+ SG(headers_sent) = 1;
+ php_cgi_usage(argv[0]);
+ php_output_end_all(TSRMLS_C);
+ php_output_deactivate(TSRMLS_C);
+ fcgi_shutdown();
+ exit_status = FPM_EXIT_USAGE;
+ goto out;
+ }
+
+ php_optind = orig_optind;
+ php_optarg = orig_optarg;
+
+#ifdef ZTS
+ SG(request_info).path_translated = NULL;
+#endif
+
+ cgi_sapi_module.additional_functions = additional_functions;
+ cgi_sapi_module.executable_location = argv[0];
+
+ /* startup after we get the above ini override se we get things right */
+ if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ return FPM_EXIT_SOFTWARE;
+ }
+
+ if (use_extended_info) {
+ CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
+ }
+
+ /* check force_cgi after startup, so we have proper output */
+ if (cgi && CGIG(force_redirect)) {
+ /* Apache will generate REDIRECT_STATUS,
+ * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS.
+ * redirect.so and installation instructions available from
+ * http://www.koehntopp.de/php.
+ * -- kk@netuse.de
+ */
+ if (!getenv("REDIRECT_STATUS") &&
+ !getenv ("HTTP_REDIRECT_STATUS") &&
+ /* this is to allow a different env var to be configured
+ * in case some server does something different than above */
+ (!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env)))
+ ) {
+ zend_try {
+ SG(sapi_headers).http_response_code = 400;
+ PUTS("<b>Security Alert!</b> The PHP CGI cannot be accessed directly.\n\n\
+<p>This PHP CGI binary was compiled with force-cgi-redirect enabled. This\n\
+means that a page will only be served up if the REDIRECT_STATUS CGI variable is\n\
+set, e.g. via an Apache Action directive.</p>\n\
+<p>For more information as to <i>why</i> this behaviour exists, see the <a href=\"http://php.net/security.cgi-bin\">\
+manual page for CGI security</a>.</p>\n\
+<p>For more information about changing this behaviour or re-enabling this webserver,\n\
+consult the installation file that came with this distribution, or visit \n\
+<a href=\"http://php.net/install.windows\">the manual page</a>.</p>\n");
+ } zend_catch {
+ } zend_end_try();
+#if defined(ZTS) && !defined(PHP_DEBUG)
+ /* XXX we're crashing here in msvc6 debug builds at
+ * php_message_handler_for_zend:839 because
+ * SG(request_info).path_translated is an invalid pointer.
+ * It still happens even though I set it to null, so something
+ * weird is going on.
+ */
+ tsrm_shutdown();
+#endif
+ return FPM_EXIT_SOFTWARE;
+ }
+ }
+
+ if (0 > fpm_init(argc, argv, fpm_config ? fpm_config : CGIG(fpm_config), fpm_prefix, fpm_pid, test_conf, php_allow_to_run_as_root, force_daemon)) {
+
+ if (fpm_globals.send_config_pipe[1]) {
+ int writeval = 0;
+ zlog(ZLOG_DEBUG, "Sending \"0\" (error) to parent via fd=%d", fpm_globals.send_config_pipe[1]);
+ write(fpm_globals.send_config_pipe[1], &writeval, sizeof(writeval));
+ close(fpm_globals.send_config_pipe[1]);
+ }
+ return FPM_EXIT_CONFIG;
+ }
+
+ if (fpm_globals.send_config_pipe[1]) {
+ int writeval = 1;
+ zlog(ZLOG_DEBUG, "Sending \"1\" (OK) to parent via fd=%d", fpm_globals.send_config_pipe[1]);
+ write(fpm_globals.send_config_pipe[1], &writeval, sizeof(writeval));
+ close(fpm_globals.send_config_pipe[1]);
+ }
+ fpm_is_running = 1;
+
+ fcgi_fd = fpm_run(&max_requests);
+ parent = 0;
+
+ /* onced forked tell zlog to also send messages through sapi_cgi_log_fastcgi() */
+ zlog_set_external_logger(sapi_cgi_log_fastcgi);
+
+ /* make php call us to get _ENV vars */
+ php_php_import_environment_variables = php_import_environment_variables;
+ php_import_environment_variables = cgi_php_import_environment_variables;
+
+ /* library is already initialized, now init our request */
+ fcgi_init_request(&request, fcgi_fd);
+
+ zend_first_try {
+ while (fcgi_accept_request(&request) >= 0) {
+ request_body_fd = -1;
+ SG(server_context) = (void *) &request;
+ init_request_info(TSRMLS_C);
+ CG(interactive) = 0;
+ char *primary_script = NULL;
+
+ fpm_request_info();
+
+ /* request startup only after we've done all we can to
+ * get path_translated */
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ fcgi_finish_request(&request, 1);
+ SG(server_context) = NULL;
+ php_module_shutdown(TSRMLS_C);
+ return FPM_EXIT_SOFTWARE;
+ }
+
+ /* check if request_method has been sent.
+ * if not, it's certainly not an HTTP over fcgi request */
+ if (!SG(request_info).request_method) {
+ goto fastcgi_request_done;
+ }
+
+ if (fpm_status_handle_request(TSRMLS_C)) {
+ goto fastcgi_request_done;
+ }
+
+ /* If path_translated is NULL, terminate here with a 404 */
+ if (!SG(request_info).path_translated) {
+ zend_try {
+ zlog(ZLOG_DEBUG, "Primary script unknown");
+ SG(sapi_headers).http_response_code = 404;
+ PUTS("File not found.\n");
+ } zend_catch {
+ } zend_end_try();
+ goto fastcgi_request_done;
+ }
+
+ if (fpm_php_limit_extensions(SG(request_info).path_translated)) {
+ SG(sapi_headers).http_response_code = 403;
+ PUTS("Access denied.\n");
+ goto fastcgi_request_done;
+ }
+
+ /*
+ * have to duplicate SG(request_info).path_translated to be able to log errrors
+ * php_fopen_primary_script seems to delete SG(request_info).path_translated on failure
+ */
+ primary_script = estrdup(SG(request_info).path_translated);
+
+ /* path_translated exists, we can continue ! */
+ if (php_fopen_primary_script(&file_handle TSRMLS_CC) == FAILURE) {
+ zend_try {
+ zlog(ZLOG_ERROR, "Unable to open primary script: %s (%s)", primary_script, strerror(errno));
+ if (errno == EACCES) {
+ SG(sapi_headers).http_response_code = 403;
+ PUTS("Access denied.\n");
+ } else {
+ SG(sapi_headers).http_response_code = 404;
+ PUTS("No input file specified.\n");
+ }
+ } zend_catch {
+ } zend_end_try();
+ /* we want to serve more requests if this is fastcgi
+ * so cleanup and continue, request shutdown is
+ * handled later */
+
+ goto fastcgi_request_done;
+ }
+
+ fpm_request_executing();
+
+ php_execute_script(&file_handle TSRMLS_CC);
+
+fastcgi_request_done:
+ if (primary_script) {
+ efree(primary_script);
+ }
+
+ if (request_body_fd != -1) {
+ close(request_body_fd);
+ }
+ request_body_fd = -2;
+
+ if (EG(exit_status) == 255) {
+ if (CGIG(error_header) && *CGIG(error_header)) {
+ sapi_header_line ctr = {0};
+
+ ctr.line = CGIG(error_header);
+ ctr.line_len = strlen(CGIG(error_header));
+ sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC);
+ }
+ }
+
+ fpm_request_end(TSRMLS_C);
+ fpm_log_write(NULL TSRMLS_CC);
+
+ STR_FREE(SG(request_info).path_translated);
+ SG(request_info).path_translated = NULL;
+
+ php_request_shutdown((void *) 0);
+
+ requests++;
+ if (max_requests && (requests == max_requests)) {
+ fcgi_finish_request(&request, 1);
+ break;
+ }
+ /* end of fastcgi loop */
+ }
+ fcgi_shutdown();
+
+ if (cgi_sapi_module.php_ini_path_override) {
+ free(cgi_sapi_module.php_ini_path_override);
+ }
+ if (cgi_sapi_module.ini_entries) {
+ free(cgi_sapi_module.ini_entries);
+ }
+ } zend_catch {
+ exit_status = FPM_EXIT_SOFTWARE;
+ } zend_end_try();
+
+out:
+
+ SG(server_context) = NULL;
+ if (parent) {
+ php_module_shutdown(TSRMLS_C);
+ sapi_shutdown();
+ }
+
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+
+#if defined(PHP_WIN32) && ZEND_DEBUG && 0
+ _CrtDumpMemoryLeaks();
+#endif
+
+ return exit_status;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/fpm/fpm/fpm_php.c b/sapi/fpm/fpm/fpm_php.c
new file mode 100644
index 0000000..cd4d3ae
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_php.c
@@ -0,0 +1,297 @@
+
+ /* $Id: fpm_php.c,v 1.22.2.4 2008/12/13 03:21:18 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "php.h"
+#include "php_main.h"
+#include "php_ini.h"
+#include "ext/standard/dl.h"
+
+#include "fastcgi.h"
+
+#include "fpm.h"
+#include "fpm_php.h"
+#include "fpm_cleanup.h"
+#include "fpm_worker_pool.h"
+#include "zlog.h"
+
+static char **limit_extensions = NULL;
+
+static int fpm_php_zend_ini_alter_master(char *name, int name_length, char *new_value, int new_value_length, int mode, int stage TSRMLS_DC) /* {{{ */
+{
+ zend_ini_entry *ini_entry;
+ char *duplicate;
+
+ if (zend_hash_find(EG(ini_directives), name, name_length, (void **) &ini_entry) == FAILURE) {
+ return FAILURE;
+ }
+
+ duplicate = strdup(new_value);
+
+ if (!ini_entry->on_modify
+ || ini_entry->on_modify(ini_entry, duplicate, new_value_length,
+ ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage TSRMLS_CC) == SUCCESS) {
+ ini_entry->value = duplicate;
+ ini_entry->value_length = new_value_length;
+ ini_entry->modifiable = mode;
+ } else {
+ free(duplicate);
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+static void fpm_php_disable(char *value, int (*zend_disable)(char *, uint TSRMLS_DC) TSRMLS_DC) /* {{{ */
+{
+ char *s = 0, *e = value;
+
+ while (*e) {
+ switch (*e) {
+ case ' ':
+ case ',':
+ if (s) {
+ *e = '\0';
+ zend_disable(s, e - s TSRMLS_CC);
+ s = 0;
+ }
+ break;
+ default:
+ if (!s) {
+ s = e;
+ }
+ break;
+ }
+ e++;
+ }
+
+ if (s) {
+ zend_disable(s, e - s TSRMLS_CC);
+ }
+}
+/* }}} */
+
+int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */
+{
+ TSRMLS_FETCH();
+
+ char *name = kv->key;
+ char *value = kv->value;
+ int name_len = strlen(name);
+ int value_len = strlen(value);
+
+ if (!strcmp(name, "extension") && *value) {
+ zval zv;
+ php_dl(value, MODULE_PERSISTENT, &zv, 1 TSRMLS_CC);
+ return Z_BVAL(zv) ? 1 : -1;
+ }
+
+ if (fpm_php_zend_ini_alter_master(name, name_len+1, value, value_len, mode, PHP_INI_STAGE_ACTIVATE TSRMLS_CC) == FAILURE) {
+ return -1;
+ }
+
+ if (!strcmp(name, "disable_functions") && *value) {
+ char *v = strdup(value);
+ PG(disable_functions) = v;
+ fpm_php_disable(v, zend_disable_function TSRMLS_CC);
+ return 1;
+ }
+
+ if (!strcmp(name, "disable_classes") && *value) {
+ char *v = strdup(value);
+ PG(disable_classes) = v;
+ fpm_php_disable(v, zend_disable_class TSRMLS_CC);
+ return 1;
+ }
+
+ return 1;
+}
+/* }}} */
+
+static int fpm_php_apply_defines(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct key_value_s *kv;
+
+ for (kv = wp->config->php_values; kv; kv = kv->next) {
+ if (fpm_php_apply_defines_ex(kv, ZEND_INI_USER) == -1) {
+ zlog(ZLOG_ERROR, "Unable to set php_value '%s'", kv->key);
+ }
+ }
+
+ for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
+ if (fpm_php_apply_defines_ex(kv, ZEND_INI_SYSTEM) == -1) {
+ zlog(ZLOG_ERROR, "Unable to set php_admin_value '%s'", kv->key);
+ }
+ }
+
+ return 0;
+}
+
+static int fpm_php_set_allowed_clients(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ if (wp->listen_address_domain == FPM_AF_INET) {
+ fcgi_set_allowed_clients(wp->config->listen_allowed_clients);
+ }
+ return 0;
+}
+/* }}} */
+
+#if 0 /* Comment out this non used function. It could be used later. */
+static int fpm_php_set_fcgi_mgmt_vars(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ char max_workers[10 + 1]; /* 4294967295 */
+ int len;
+
+ len = sprintf(max_workers, "%u", (unsigned int) wp->config->pm_max_children);
+
+ fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, max_workers, len);
+ fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, max_workers, len);
+ return 0;
+}
+/* }}} */
+#endif
+
+char *fpm_php_script_filename(TSRMLS_D) /* {{{ */
+{
+ return SG(request_info).path_translated;
+}
+/* }}} */
+
+char *fpm_php_request_uri(TSRMLS_D) /* {{{ */
+{
+ return (char *) SG(request_info).request_uri;
+}
+/* }}} */
+
+char *fpm_php_request_method(TSRMLS_D) /* {{{ */
+{
+ return (char *) SG(request_info).request_method;
+}
+/* }}} */
+
+char *fpm_php_query_string(TSRMLS_D) /* {{{ */
+{
+ return SG(request_info).query_string;
+}
+/* }}} */
+
+char *fpm_php_auth_user(TSRMLS_D) /* {{{ */
+{
+ return SG(request_info).auth_user;
+}
+/* }}} */
+
+size_t fpm_php_content_length(TSRMLS_D) /* {{{ */
+{
+ return SG(request_info).content_length;
+}
+/* }}} */
+
+static void fpm_php_cleanup(int which, void *arg) /* {{{ */
+{
+ TSRMLS_FETCH();
+ php_module_shutdown(TSRMLS_C);
+ sapi_shutdown();
+}
+/* }}} */
+
+void fpm_php_soft_quit() /* {{{ */
+{
+ fcgi_set_in_shutdown(1);
+}
+/* }}} */
+
+int fpm_php_init_main() /* {{{ */
+{
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_PARENT, fpm_php_cleanup, 0)) {
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_php_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ if (0 > fpm_php_apply_defines(wp) ||
+ 0 > fpm_php_set_allowed_clients(wp)) {
+ return -1;
+ }
+
+ if (wp->limit_extensions) {
+ limit_extensions = wp->limit_extensions;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_php_limit_extensions(char *path) /* {{{ */
+{
+ char **p;
+ size_t path_len;
+
+ if (!path || !limit_extensions) {
+ return 0; /* allowed by default */
+ }
+
+ p = limit_extensions;
+ path_len = strlen(path);
+ while (p && *p) {
+ size_t ext_len = strlen(*p);
+ if (path_len > ext_len) {
+ char *path_ext = path + path_len - ext_len;
+ if (strcmp(*p, path_ext) == 0) {
+ return 0; /* allow as the extension has been found */
+ }
+ }
+ p++;
+ }
+
+
+ zlog(ZLOG_NOTICE, "Access to the script '%s' has been denied (see security.limit_extensions)", path);
+ return 1; /* extension not found: not allowed */
+}
+/* }}} */
+
+char* fpm_php_get_string_from_table(char *table, char *key TSRMLS_DC) /* {{{ */
+{
+ zval **data, **tmp;
+ char *string_key;
+ uint string_len;
+ ulong num_key;
+ if (!table || !key) {
+ return NULL;
+ }
+
+ /* inspired from ext/standard/info.c */
+
+ zend_is_auto_global(table, strlen(table) TSRMLS_CC);
+
+ /* find the table and ensure it's an array */
+ if (zend_hash_find(&EG(symbol_table), table, strlen(table) + 1, (void **) &data) == SUCCESS && Z_TYPE_PP(data) == IS_ARRAY) {
+
+ /* reset the internal pointer */
+ zend_hash_internal_pointer_reset(Z_ARRVAL_PP(data));
+
+ /* parse the array to look for our key */
+ while (zend_hash_get_current_data(Z_ARRVAL_PP(data), (void **) &tmp) == SUCCESS) {
+ /* ensure the key is a string */
+ if (zend_hash_get_current_key_ex(Z_ARRVAL_PP(data), &string_key, &string_len, &num_key, 0, NULL) == HASH_KEY_IS_STRING) {
+ /* compare to our key */
+ if (!strncmp(string_key, key, string_len)) {
+ return Z_STRVAL_PP(tmp);
+ }
+ }
+ zend_hash_move_forward(Z_ARRVAL_PP(data));
+ }
+ }
+
+ return NULL;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_php.h b/sapi/fpm/fpm/fpm_php.h
new file mode 100644
index 0000000..d605473
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_php.h
@@ -0,0 +1,50 @@
+
+ /* $Id: fpm_php.h,v 1.10.2.1 2008/11/15 00:57:24 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_PHP_H
+#define FPM_PHP_H 1
+
+#include <TSRM.h>
+
+#include "php.h"
+#include "build-defs.h" /* for PHP_ defines */
+#include "fpm/fpm_conf.h"
+
+#define FPM_PHP_INI_TO_EXPAND \
+ { \
+ "error_log", \
+ "extension_dir", \
+ "mime_magic.magicfile", \
+ "sendmail_path", \
+ "session.cookie_path", \
+ "session_pgsql.sem_file_name", \
+ "soap.wsdl_cache_dir", \
+ "uploadprogress.file.filename_template", \
+ "xdebug.output_dir", \
+ "xdebug.profiler_output_dir", \
+ "xdebug.trace_output_dir", \
+ "xmms.path", \
+ "axis2.client_home", \
+ "blenc.key_file", \
+ "coin_acceptor.device", \
+ NULL \
+ }
+
+struct fpm_worker_pool_s;
+
+int fpm_php_init_child(struct fpm_worker_pool_s *wp);
+char *fpm_php_script_filename(TSRMLS_D);
+char *fpm_php_request_uri(TSRMLS_D);
+char *fpm_php_request_method(TSRMLS_D);
+char *fpm_php_query_string(TSRMLS_D);
+char *fpm_php_auth_user(TSRMLS_D);
+size_t fpm_php_content_length(TSRMLS_D);
+void fpm_php_soft_quit();
+int fpm_php_init_main();
+int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode);
+int fpm_php_limit_extensions(char *path);
+char* fpm_php_get_string_from_table(char *table, char *key TSRMLS_DC);
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_php_trace.c b/sapi/fpm/fpm/fpm_php_trace.c
new file mode 100644
index 0000000..d95d66a
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_php_trace.c
@@ -0,0 +1,177 @@
+
+ /* $Id: fpm_php_trace.c,v 1.27.2.1 2008/11/15 00:57:24 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#if HAVE_FPM_TRACE
+
+#include "php.h"
+#include "php_main.h"
+
+#include <stdio.h>
+#include <stddef.h>
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# include <stdint.h>
+#endif
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include "fpm_trace.h"
+#include "fpm_php_trace.h"
+#include "fpm_children.h"
+#include "fpm_worker_pool.h"
+#include "fpm_process_ctl.h"
+#include "fpm_scoreboard.h"
+
+#include "zlog.h"
+
+
+#define valid_ptr(p) ((p) && 0 == ((p) & (sizeof(long) - 1)))
+
+#if SIZEOF_LONG == 4
+#define PTR_FMT "08"
+#elif SIZEOF_LONG == 8
+#define PTR_FMT "016"
+#endif
+
+
+static int fpm_php_trace_dump(struct fpm_child_s *child, FILE *slowlog TSRMLS_DC) /* {{{ */
+{
+ int callers_limit = 20;
+ pid_t pid = child->pid;
+ struct timeval tv;
+ static const int buf_size = 1024;
+ char buf[buf_size];
+ long execute_data;
+ long l;
+
+ gettimeofday(&tv, 0);
+
+ zlog_print_time(&tv, buf, buf_size);
+
+ fprintf(slowlog, "\n%s [pool %s] pid %d\n", buf, child->wp->config->name, (int) pid);
+
+ if (0 > fpm_trace_get_strz(buf, buf_size, (long) &SG(request_info).path_translated)) {
+ return -1;
+ }
+
+ fprintf(slowlog, "script_filename = %s\n", buf);
+
+ if (0 > fpm_trace_get_long((long) &EG(current_execute_data), &l)) {
+ return -1;
+ }
+
+ execute_data = l;
+
+ while (execute_data) {
+ long function;
+ uint lineno = 0;
+
+ fprintf(slowlog, "[0x%" PTR_FMT "lx] ", execute_data);
+
+ if (0 > fpm_trace_get_long(execute_data + offsetof(zend_execute_data, function_state.function), &l)) {
+ return -1;
+ }
+
+ function = l;
+
+ if (valid_ptr(function)) {
+ if (0 > fpm_trace_get_strz(buf, buf_size, function + offsetof(zend_function, common.function_name))) {
+ return -1;
+ }
+
+ fprintf(slowlog, "%s()", buf);
+ } else {
+ fprintf(slowlog, "???");
+ }
+
+ if (0 > fpm_trace_get_long(execute_data + offsetof(zend_execute_data, op_array), &l)) {
+ return -1;
+ }
+
+ *buf = '\0';
+
+ if (valid_ptr(l)) {
+ long op_array = l;
+
+ if (0 > fpm_trace_get_strz(buf, buf_size, op_array + offsetof(zend_op_array, filename))) {
+ return -1;
+ }
+ }
+
+ if (0 > fpm_trace_get_long(execute_data + offsetof(zend_execute_data, opline), &l)) {
+ return -1;
+ }
+
+ if (valid_ptr(l)) {
+ long opline = l;
+ uint *lu = (uint *) &l;
+
+ if (0 > fpm_trace_get_long(opline + offsetof(struct _zend_op, lineno), &l)) {
+ return -1;
+ }
+
+ lineno = *lu;
+ }
+
+ fprintf(slowlog, " %s:%u\n", *buf ? buf : "unknown", lineno);
+
+ if (0 > fpm_trace_get_long(execute_data + offsetof(zend_execute_data, prev_execute_data), &l)) {
+ return -1;
+ }
+
+ execute_data = l;
+
+ if (0 == --callers_limit) {
+ break;
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+void fpm_php_trace(struct fpm_child_s *child) /* {{{ */
+{
+ TSRMLS_FETCH();
+ fpm_scoreboard_update(0, 0, 0, 0, 0, 0, 1, FPM_SCOREBOARD_ACTION_SET, child->wp->scoreboard);
+ FILE *slowlog;
+
+ zlog(ZLOG_NOTICE, "about to trace %d", (int) child->pid);
+
+ slowlog = fopen(child->wp->config->slowlog, "a+");
+
+ if (!slowlog) {
+ zlog(ZLOG_SYSERROR, "unable to open slowlog (%s)", child->wp->config->slowlog);
+ goto done0;
+ }
+
+ if (0 > fpm_trace_ready(child->pid)) {
+ goto done1;
+ }
+
+ if (0 > fpm_php_trace_dump(child, slowlog TSRMLS_CC)) {
+ fprintf(slowlog, "+++ dump failed\n");
+ }
+
+ if (0 > fpm_trace_close(child->pid)) {
+ goto done1;
+ }
+
+done1:
+ fclose(slowlog);
+
+done0:
+ fpm_pctl_kill(child->pid, FPM_PCTL_CONT);
+ child->tracer = 0;
+
+ zlog(ZLOG_NOTICE, "finished trace of %d", (int) child->pid);
+}
+/* }}} */
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_php_trace.h b/sapi/fpm/fpm/fpm_php_trace.h
new file mode 100644
index 0000000..af5e456
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_php_trace.h
@@ -0,0 +1,13 @@
+
+ /* $Id: fpm_php_trace.h,v 1.2 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_PHP_TRACE_H
+#define FPM_PHP_TRACE_H 1
+
+struct fpm_child_s;
+
+void fpm_php_trace(struct fpm_child_s *);
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_process_ctl.c b/sapi/fpm/fpm/fpm_process_ctl.c
new file mode 100644
index 0000000..76ea4d3
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_process_ctl.c
@@ -0,0 +1,539 @@
+
+ /* $Id: fpm_process_ctl.c,v 1.19.2.2 2008/12/13 03:21:18 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <sys/types.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "fpm.h"
+#include "fpm_clock.h"
+#include "fpm_children.h"
+#include "fpm_signals.h"
+#include "fpm_events.h"
+#include "fpm_process_ctl.h"
+#include "fpm_cleanup.h"
+#include "fpm_request.h"
+#include "fpm_worker_pool.h"
+#include "fpm_scoreboard.h"
+#include "fpm_sockets.h"
+#include "zlog.h"
+
+
+static int fpm_state = FPM_PCTL_STATE_NORMAL;
+static int fpm_signal_sent = 0;
+
+
+static const char *fpm_state_names[] = {
+ [FPM_PCTL_STATE_NORMAL] = "normal",
+ [FPM_PCTL_STATE_RELOADING] = "reloading",
+ [FPM_PCTL_STATE_TERMINATING] = "terminating",
+ [FPM_PCTL_STATE_FINISHING] = "finishing"
+};
+
+static int saved_argc;
+static char **saved_argv;
+
+static void fpm_pctl_cleanup(int which, void *arg) /* {{{ */
+{
+ int i;
+ if (which != FPM_CLEANUP_PARENT_EXEC) {
+ for (i = 0; i < saved_argc; i++) {
+ free(saved_argv[i]);
+ }
+ free(saved_argv);
+ }
+}
+/* }}} */
+
+static struct fpm_event_s pctl_event;
+
+static void fpm_pctl_action(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_TIMEOUT);
+}
+/* }}} */
+
+static int fpm_pctl_timeout_set(int sec) /* {{{ */
+{
+ fpm_event_set_timer(&pctl_event, 0, &fpm_pctl_action, NULL);
+ fpm_event_add(&pctl_event, sec * 1000);
+ return 0;
+}
+/* }}} */
+
+static void fpm_pctl_exit() /* {{{ */
+{
+ zlog(ZLOG_NOTICE, "exiting, bye-bye!");
+
+ fpm_conf_unlink_pid();
+ fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT_MAIN);
+ exit(FPM_EXIT_OK);
+}
+/* }}} */
+
+#define optional_arg(c) (saved_argc > c ? ", \"" : ""), (saved_argc > c ? saved_argv[c] : ""), (saved_argc > c ? "\"" : "")
+
+static void fpm_pctl_exec() /* {{{ */
+{
+
+ zlog(ZLOG_NOTICE, "reloading: execvp(\"%s\", {\"%s\""
+ "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
+ "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
+ "})",
+ saved_argv[0], saved_argv[0],
+ optional_arg(1),
+ optional_arg(2),
+ optional_arg(3),
+ optional_arg(4),
+ optional_arg(5),
+ optional_arg(6),
+ optional_arg(7),
+ optional_arg(8),
+ optional_arg(9),
+ optional_arg(10)
+ );
+
+ fpm_cleanups_run(FPM_CLEANUP_PARENT_EXEC);
+ execvp(saved_argv[0], saved_argv);
+ zlog(ZLOG_SYSERROR, "failed to reload: execvp() failed");
+ exit(FPM_EXIT_SOFTWARE);
+}
+/* }}} */
+
+static void fpm_pctl_action_last() /* {{{ */
+{
+ switch (fpm_state) {
+ case FPM_PCTL_STATE_RELOADING:
+ fpm_pctl_exec();
+ break;
+
+ case FPM_PCTL_STATE_FINISHING:
+ case FPM_PCTL_STATE_TERMINATING:
+ fpm_pctl_exit();
+ break;
+ }
+}
+/* }}} */
+
+int fpm_pctl_kill(pid_t pid, int how) /* {{{ */
+{
+ int s = 0;
+
+ switch (how) {
+ case FPM_PCTL_TERM :
+ s = SIGTERM;
+ break;
+ case FPM_PCTL_STOP :
+ s = SIGSTOP;
+ break;
+ case FPM_PCTL_CONT :
+ s = SIGCONT;
+ break;
+ case FPM_PCTL_QUIT :
+ s = SIGQUIT;
+ break;
+ default :
+ break;
+ }
+ return kill(pid, s);
+}
+/* }}} */
+
+void fpm_pctl_kill_all(int signo) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ int alive_children = 0;
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ struct fpm_child_s *child;
+
+ for (child = wp->children; child; child = child->next) {
+ int res = kill(child->pid, signo);
+
+ zlog(ZLOG_DEBUG, "[pool %s] sending signal %d %s to child %d",
+ child->wp->config->name, signo,
+ fpm_signal_names[signo] ? fpm_signal_names[signo] : "", (int) child->pid);
+
+ if (res == 0) {
+ ++alive_children;
+ }
+ }
+ }
+
+ if (alive_children) {
+ zlog(ZLOG_DEBUG, "%d child(ren) still alive", alive_children);
+ }
+}
+/* }}} */
+
+static void fpm_pctl_action_next() /* {{{ */
+{
+ int sig, timeout;
+
+ if (!fpm_globals.running_children) {
+ fpm_pctl_action_last();
+ }
+
+ if (fpm_signal_sent == 0) {
+ if (fpm_state == FPM_PCTL_STATE_TERMINATING) {
+ sig = SIGTERM;
+ } else {
+ sig = SIGQUIT;
+ }
+ timeout = fpm_global_config.process_control_timeout;
+ } else {
+ if (fpm_signal_sent == SIGQUIT) {
+ sig = SIGTERM;
+ } else {
+ sig = SIGKILL;
+ }
+ timeout = 1;
+ }
+
+ fpm_pctl_kill_all(sig);
+ fpm_signal_sent = sig;
+ fpm_pctl_timeout_set(timeout);
+}
+/* }}} */
+
+void fpm_pctl(int new_state, int action) /* {{{ */
+{
+ switch (action) {
+ case FPM_PCTL_ACTION_SET :
+ if (fpm_state == new_state) { /* already in progress - just ignore duplicate signal */
+ return;
+ }
+
+ switch (fpm_state) { /* check which states can be overridden */
+ case FPM_PCTL_STATE_NORMAL :
+ /* 'normal' can be overridden by any other state */
+ break;
+ case FPM_PCTL_STATE_RELOADING :
+ /* 'reloading' can be overridden by 'finishing' */
+ if (new_state == FPM_PCTL_STATE_FINISHING) break;
+ case FPM_PCTL_STATE_FINISHING :
+ /* 'reloading' and 'finishing' can be overridden by 'terminating' */
+ if (new_state == FPM_PCTL_STATE_TERMINATING) break;
+ case FPM_PCTL_STATE_TERMINATING :
+ /* nothing can override 'terminating' state */
+ zlog(ZLOG_DEBUG, "not switching to '%s' state, because already in '%s' state",
+ fpm_state_names[new_state], fpm_state_names[fpm_state]);
+ return;
+ }
+
+ fpm_signal_sent = 0;
+ fpm_state = new_state;
+
+ zlog(ZLOG_DEBUG, "switching to '%s' state", fpm_state_names[fpm_state]);
+ /* fall down */
+
+ case FPM_PCTL_ACTION_TIMEOUT :
+ fpm_pctl_action_next();
+ break;
+ case FPM_PCTL_ACTION_LAST_CHILD_EXITED :
+ fpm_pctl_action_last();
+ break;
+
+ }
+}
+/* }}} */
+
+int fpm_pctl_can_spawn_children() /* {{{ */
+{
+ return fpm_state == FPM_PCTL_STATE_NORMAL;
+}
+/* }}} */
+
+int fpm_pctl_child_exited() /* {{{ */
+{
+ if (fpm_state == FPM_PCTL_STATE_NORMAL) {
+ return 0;
+ }
+
+ if (!fpm_globals.running_children) {
+ fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_LAST_CHILD_EXITED);
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_pctl_init_main() /* {{{ */
+{
+ int i;
+
+ saved_argc = fpm_globals.argc;
+ saved_argv = malloc(sizeof(char *) * (saved_argc + 1));
+
+ if (!saved_argv) {
+ return -1;
+ }
+
+ for (i = 0; i < saved_argc; i++) {
+ saved_argv[i] = strdup(fpm_globals.argv[i]);
+
+ if (!saved_argv[i]) {
+ return -1;
+ }
+ }
+
+ saved_argv[i] = 0;
+
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_pctl_cleanup, 0)) {
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+static void fpm_pctl_check_request_timeout(struct timeval *now) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ int terminate_timeout = wp->config->request_terminate_timeout;
+ int slowlog_timeout = wp->config->request_slowlog_timeout;
+ struct fpm_child_s *child;
+
+ if (terminate_timeout || slowlog_timeout) {
+ for (child = wp->children; child; child = child->next) {
+ fpm_request_check_timed_out(child, now, terminate_timeout, slowlog_timeout);
+ }
+ }
+ }
+}
+/* }}} */
+
+static void fpm_pctl_perform_idle_server_maintenance(struct timeval *now) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ struct fpm_child_s *child;
+ struct fpm_child_s *last_idle_child = NULL;
+ int idle = 0;
+ int active = 0;
+ int children_to_fork;
+ unsigned cur_lq = 0;
+
+ if (wp->config == NULL) continue;
+
+ for (child = wp->children; child; child = child->next) {
+ if (fpm_request_is_idle(child)) {
+ if (last_idle_child == NULL) {
+ last_idle_child = child;
+ } else {
+ if (timercmp(&child->started, &last_idle_child->started, <)) {
+ last_idle_child = child;
+ }
+ }
+ idle++;
+ } else {
+ active++;
+ }
+ }
+
+ /* update status structure for all PMs */
+ if (wp->listen_address_domain == FPM_AF_INET) {
+ if (0 > fpm_socket_get_listening_queue(wp->listening_socket, &cur_lq, NULL)) {
+ cur_lq = 0;
+#if 0
+ } else {
+ if (cur_lq > 0) {
+ if (!wp->warn_lq) {
+ zlog(ZLOG_WARNING, "[pool %s] listening queue is not empty, #%d requests are waiting to be served, consider raising pm.max_children setting (%d)", wp->config->name, cur_lq, wp->config->pm_max_children);
+ wp->warn_lq = 1;
+ }
+ } else {
+ wp->warn_lq = 0;
+ }
+#endif
+ }
+ }
+ fpm_scoreboard_update(idle, active, cur_lq, -1, -1, -1, 0, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard);
+
+ /* this is specific to PM_STYLE_ONDEMAND */
+ if (wp->config->pm == PM_STYLE_ONDEMAND) {
+ struct timeval last, now;
+
+ zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children", wp->config->name, active, idle);
+
+ if (!last_idle_child) continue;
+
+ fpm_request_last_activity(last_idle_child, &last);
+ fpm_clock_get(&now);
+ if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) {
+ last_idle_child->idle_kill = 1;
+ fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
+ }
+
+ continue;
+ }
+
+ /* the rest is only used by PM_STYLE_DYNAMIC */
+ if (wp->config->pm != PM_STYLE_DYNAMIC) continue;
+
+ zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children, %d running children. Spawning rate %d", wp->config->name, active, idle, wp->running_children, wp->idle_spawn_rate);
+
+ if (idle > wp->config->pm_max_spare_servers && last_idle_child) {
+ last_idle_child->idle_kill = 1;
+ fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
+ wp->idle_spawn_rate = 1;
+ continue;
+ }
+
+ if (idle < wp->config->pm_min_spare_servers) {
+ if (wp->running_children >= wp->config->pm_max_children) {
+ if (!wp->warn_max_children) {
+ fpm_scoreboard_update(0, 0, 0, 0, 0, 1, 0, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
+ zlog(ZLOG_WARNING, "[pool %s] server reached pm.max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
+ wp->warn_max_children = 1;
+ }
+ wp->idle_spawn_rate = 1;
+ continue;
+ }
+
+ if (wp->idle_spawn_rate >= 8) {
+ zlog(ZLOG_WARNING, "[pool %s] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning %d children, there are %d idle, and %d total children", wp->config->name, wp->idle_spawn_rate, idle, wp->running_children);
+ }
+
+ /* compute the number of idle process to spawn */
+ children_to_fork = MIN(wp->idle_spawn_rate, wp->config->pm_min_spare_servers - idle);
+
+ /* get sure it won't exceed max_children */
+ children_to_fork = MIN(children_to_fork, wp->config->pm_max_children - wp->running_children);
+ if (children_to_fork <= 0) {
+ if (!wp->warn_max_children) {
+ fpm_scoreboard_update(0, 0, 0, 0, 0, 1, 0, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
+ zlog(ZLOG_WARNING, "[pool %s] server reached pm.max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
+ wp->warn_max_children = 1;
+ }
+ wp->idle_spawn_rate = 1;
+ continue;
+ }
+ wp->warn_max_children = 0;
+
+ fpm_children_make(wp, 1, children_to_fork, 1);
+
+ /* if it's a child, stop here without creating the next event
+ * this event is reserved to the master process
+ */
+ if (fpm_globals.is_child) {
+ return;
+ }
+
+ zlog(ZLOG_DEBUG, "[pool %s] %d child(ren) have been created dynamically", wp->config->name, children_to_fork);
+
+ /* Double the spawn rate for the next iteration */
+ if (wp->idle_spawn_rate < FPM_MAX_SPAWN_RATE) {
+ wp->idle_spawn_rate *= 2;
+ }
+ continue;
+ }
+ wp->idle_spawn_rate = 1;
+ }
+}
+/* }}} */
+
+void fpm_pctl_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ static struct fpm_event_s heartbeat;
+ struct timeval now;
+
+ if (fpm_globals.parent_pid != getpid()) {
+ return; /* sanity check */
+ }
+
+ if (which == FPM_EV_TIMEOUT) {
+ fpm_clock_get(&now);
+ fpm_pctl_check_request_timeout(&now);
+ return;
+ }
+
+ /* ensure heartbeat is not lower than FPM_PCTL_MIN_HEARTBEAT */
+ fpm_globals.heartbeat = MAX(fpm_globals.heartbeat, FPM_PCTL_MIN_HEARTBEAT);
+
+ /* first call without setting to initialize the timer */
+ zlog(ZLOG_DEBUG, "heartbeat have been set up with a timeout of %dms", fpm_globals.heartbeat);
+ fpm_event_set_timer(&heartbeat, FPM_EV_PERSIST, &fpm_pctl_heartbeat, NULL);
+ fpm_event_add(&heartbeat, fpm_globals.heartbeat);
+}
+/* }}} */
+
+void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ static struct fpm_event_s heartbeat;
+ struct timeval now;
+
+ if (fpm_globals.parent_pid != getpid()) {
+ return; /* sanity check */
+ }
+
+ if (which == FPM_EV_TIMEOUT) {
+ fpm_clock_get(&now);
+ if (fpm_pctl_can_spawn_children()) {
+ fpm_pctl_perform_idle_server_maintenance(&now);
+
+ /* if it's a child, stop here without creating the next event
+ * this event is reserved to the master process
+ */
+ if (fpm_globals.is_child) {
+ return;
+ }
+ }
+ return;
+ }
+
+ /* first call without setting which to initialize the timer */
+ fpm_event_set_timer(&heartbeat, FPM_EV_PERSIST, &fpm_pctl_perform_idle_server_maintenance_heartbeat, NULL);
+ fpm_event_add(&heartbeat, FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT);
+}
+/* }}} */
+
+void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp = (struct fpm_worker_pool_s *)arg;
+ struct fpm_child_s *child;
+
+
+ if (fpm_globals.parent_pid != getpid()) {
+ /* prevent a event race condition when child process
+ * have not set up its own event loop */
+ return;
+ }
+
+ wp->socket_event_set = 0;
+
+/* zlog(ZLOG_DEBUG, "[pool %s] heartbeat running_children=%d", wp->config->name, wp->running_children);*/
+
+ if (wp->running_children >= wp->config->pm_max_children) {
+ if (!wp->warn_max_children) {
+ fpm_scoreboard_update(0, 0, 0, 0, 0, 1, 0, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
+ zlog(ZLOG_WARNING, "[pool %s] server reached max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
+ wp->warn_max_children = 1;
+ }
+
+ return;
+ }
+
+ for (child = wp->children; child; child = child->next) {
+ /* if there is at least on idle child, it will handle the connection, stop here */
+ if (fpm_request_is_idle(child)) {
+ return;
+ }
+ }
+
+ wp->warn_max_children = 0;
+ fpm_children_make(wp, 1, 1, 1);
+
+ if (fpm_globals.is_child) {
+ return;
+ }
+
+ zlog(ZLOG_DEBUG, "[pool %s] got accept without idle child available .... I forked", wp->config->name);
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_process_ctl.h b/sapi/fpm/fpm/fpm_process_ctl.h
new file mode 100644
index 0000000..86a6ef0
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_process_ctl.h
@@ -0,0 +1,53 @@
+
+ /* $Id: fpm_process_ctl.h,v 1.6 2008/07/20 21:33:10 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_PROCESS_CTL_H
+#define FPM_PROCESS_CTL_H 1
+
+#include "fpm_events.h"
+
+/* spawn max 32 children at once */
+#define FPM_MAX_SPAWN_RATE (32)
+/* 1s (in ms) heartbeat for idle server maintenance */
+#define FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT (1000)
+/* a minimum of 130ms heartbeat for pctl */
+#define FPM_PCTL_MIN_HEARTBEAT (130)
+
+
+struct fpm_child_s;
+
+void fpm_pctl(int new_state, int action);
+int fpm_pctl_can_spawn_children();
+int fpm_pctl_kill(pid_t pid, int how);
+void fpm_pctl_kill_all(int signo);
+void fpm_pctl_heartbeat(struct fpm_event_s *ev, short which, void *arg);
+void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, short which, void *arg);
+void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg);
+int fpm_pctl_child_exited();
+int fpm_pctl_init_main();
+
+
+enum {
+ FPM_PCTL_STATE_UNSPECIFIED,
+ FPM_PCTL_STATE_NORMAL,
+ FPM_PCTL_STATE_RELOADING,
+ FPM_PCTL_STATE_TERMINATING,
+ FPM_PCTL_STATE_FINISHING
+};
+
+enum {
+ FPM_PCTL_ACTION_SET,
+ FPM_PCTL_ACTION_TIMEOUT,
+ FPM_PCTL_ACTION_LAST_CHILD_EXITED
+};
+
+enum {
+ FPM_PCTL_TERM,
+ FPM_PCTL_STOP,
+ FPM_PCTL_CONT,
+ FPM_PCTL_QUIT
+};
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_request.c b/sapi/fpm/fpm/fpm_request.c
new file mode 100644
index 0000000..bf431a0
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_request.c
@@ -0,0 +1,316 @@
+
+ /* $Id: fpm_request.c,v 1.9.2.1 2008/11/15 00:57:24 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+#ifdef HAVE_TIMES
+#include <sys/times.h>
+#endif
+
+#include "fpm_config.h"
+
+#include "fpm.h"
+#include "fpm_php.h"
+#include "fpm_str.h"
+#include "fpm_clock.h"
+#include "fpm_conf.h"
+#include "fpm_trace.h"
+#include "fpm_php_trace.h"
+#include "fpm_process_ctl.h"
+#include "fpm_children.h"
+#include "fpm_scoreboard.h"
+#include "fpm_status.h"
+#include "fpm_request.h"
+#include "fpm_log.h"
+
+#include "zlog.h"
+
+static const char *requests_stages[] = {
+ [FPM_REQUEST_ACCEPTING] = "Idle",
+ [FPM_REQUEST_READING_HEADERS] = "Reading headers",
+ [FPM_REQUEST_INFO] = "Getting request informations",
+ [FPM_REQUEST_EXECUTING] = "Running",
+ [FPM_REQUEST_END] = "Ending",
+ [FPM_REQUEST_FINISHED] = "Finishing",
+};
+
+const char *fpm_request_get_stage_name(int stage) {
+ return requests_stages[stage];
+}
+
+void fpm_request_accepting() /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+ struct timeval now;
+
+ fpm_clock_get(&now);
+
+ proc = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (proc == NULL) {
+ zlog(ZLOG_WARNING, "failed to acquire proc scoreboard");
+ return;
+ }
+
+ proc->request_stage = FPM_REQUEST_ACCEPTING;
+ proc->tv = now;
+ fpm_scoreboard_proc_release(proc);
+
+ /* idle++, active-- */
+ fpm_scoreboard_update(1, -1, 0, 0, 0, 0, 0, FPM_SCOREBOARD_ACTION_INC, NULL);
+}
+/* }}} */
+
+void fpm_request_reading_headers() /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+
+ struct timeval now;
+ clock_t now_epoch;
+#ifdef HAVE_TIMES
+ struct tms cpu;
+#endif
+
+ fpm_clock_get(&now);
+ now_epoch = time(NULL);
+#ifdef HAVE_TIMES
+ times(&cpu);
+#endif
+
+ proc = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (proc == NULL) {
+ zlog(ZLOG_WARNING, "failed to acquire proc scoreboard");
+ return;
+ }
+
+ proc->request_stage = FPM_REQUEST_READING_HEADERS;
+ proc->tv = now;
+ proc->accepted = now;
+ proc->accepted_epoch = now_epoch;
+#ifdef HAVE_TIMES
+ proc->cpu_accepted = cpu;
+#endif
+ proc->requests++;
+ proc->request_uri[0] = '\0';
+ proc->request_method[0] = '\0';
+ proc->script_filename[0] = '\0';
+ proc->query_string[0] = '\0';
+ proc->query_string[0] = '\0';
+ proc->auth_user[0] = '\0';
+ proc->content_length = 0;
+ fpm_scoreboard_proc_release(proc);
+
+ /* idle--, active++, request++ */
+ fpm_scoreboard_update(-1, 1, 0, 0, 1, 0, 0, FPM_SCOREBOARD_ACTION_INC, NULL);
+}
+/* }}} */
+
+void fpm_request_info() /* {{{ */
+{
+ TSRMLS_FETCH();
+ struct fpm_scoreboard_proc_s *proc;
+ char *request_uri = fpm_php_request_uri(TSRMLS_C);
+ char *request_method = fpm_php_request_method(TSRMLS_C);
+ char *script_filename = fpm_php_script_filename(TSRMLS_C);
+ char *query_string = fpm_php_query_string(TSRMLS_C);
+ char *auth_user = fpm_php_auth_user(TSRMLS_C);
+ size_t content_length = fpm_php_content_length(TSRMLS_C);
+ struct timeval now;
+
+ fpm_clock_get(&now);
+
+ proc = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (proc == NULL) {
+ zlog(ZLOG_WARNING, "failed to acquire proc scoreboard");
+ return;
+ }
+
+ proc->request_stage = FPM_REQUEST_INFO;
+ proc->tv = now;
+
+ if (request_uri) {
+ strlcpy(proc->request_uri, request_uri, sizeof(proc->request_uri));
+ }
+
+ if (request_method) {
+ strlcpy(proc->request_method, request_method, sizeof(proc->request_method));
+ }
+
+ if (query_string) {
+ strlcpy(proc->query_string, query_string, sizeof(proc->query_string));
+ }
+
+ if (auth_user) {
+ strlcpy(proc->auth_user, auth_user, sizeof(proc->auth_user));
+ }
+
+ proc->content_length = content_length;
+
+ /* if cgi.fix_pathinfo is set to "1" and script cannot be found (404)
+ the sapi_globals.request_info.path_translated is set to NULL */
+ if (script_filename) {
+ strlcpy(proc->script_filename, script_filename, sizeof(proc->script_filename));
+ }
+
+ fpm_scoreboard_proc_release(proc);
+}
+/* }}} */
+
+void fpm_request_executing() /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+ struct timeval now;
+
+ fpm_clock_get(&now);
+
+ proc = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (proc == NULL) {
+ zlog(ZLOG_WARNING, "failed to acquire proc scoreboard");
+ return;
+ }
+
+ proc->request_stage = FPM_REQUEST_EXECUTING;
+ proc->tv = now;
+ fpm_scoreboard_proc_release(proc);
+}
+/* }}} */
+
+void fpm_request_end(TSRMLS_D) /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+ struct timeval now;
+#ifdef HAVE_TIMES
+ struct tms cpu;
+#endif
+ size_t memory = zend_memory_peak_usage(1 TSRMLS_CC);
+
+ fpm_clock_get(&now);
+#ifdef HAVE_TIMES
+ times(&cpu);
+#endif
+
+ proc = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (proc == NULL) {
+ zlog(ZLOG_WARNING, "failed to acquire proc scoreboard");
+ return;
+ }
+ proc->request_stage = FPM_REQUEST_FINISHED;
+ proc->tv = now;
+ timersub(&now, &proc->accepted, &proc->duration);
+#ifdef HAVE_TIMES
+ timersub(&proc->tv, &proc->accepted, &proc->cpu_duration);
+ proc->last_request_cpu.tms_utime = cpu.tms_utime - proc->cpu_accepted.tms_utime;
+ proc->last_request_cpu.tms_stime = cpu.tms_stime - proc->cpu_accepted.tms_stime;
+ proc->last_request_cpu.tms_cutime = cpu.tms_cutime - proc->cpu_accepted.tms_cutime;
+ proc->last_request_cpu.tms_cstime = cpu.tms_cstime - proc->cpu_accepted.tms_cstime;
+#endif
+ proc->memory = memory;
+ fpm_scoreboard_proc_release(proc);
+}
+/* }}} */
+
+void fpm_request_finished() /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+ struct timeval now;
+
+ fpm_clock_get(&now);
+
+ proc = fpm_scoreboard_proc_acquire(NULL, -1, 0);
+ if (proc == NULL) {
+ zlog(ZLOG_WARNING, "failed to acquire proc scoreboard");
+ return;
+ }
+
+ proc->request_stage = FPM_REQUEST_FINISHED;
+ proc->tv = now;
+ memset(&proc->accepted, 0, sizeof(proc->accepted));
+ proc->accepted_epoch = 0;
+ fpm_scoreboard_proc_release(proc);
+}
+/* }}} */
+
+void fpm_request_check_timed_out(struct fpm_child_s *child, struct timeval *now, int terminate_timeout, int slowlog_timeout) /* {{{ */
+{
+ struct fpm_scoreboard_proc_s proc, *proc_p;
+
+ proc_p = fpm_scoreboard_proc_acquire(child->wp->scoreboard, child->scoreboard_i, 1);
+ if (!proc_p) {
+ zlog(ZLOG_WARNING, "failed to acquire scoreboard");
+ return;
+ }
+
+ proc = *proc_p;
+ fpm_scoreboard_proc_release(proc_p);
+
+#if HAVE_FPM_TRACE
+ if (child->slow_logged.tv_sec) {
+ if (child->slow_logged.tv_sec != proc.accepted.tv_sec || child->slow_logged.tv_usec != proc.accepted.tv_usec) {
+ child->slow_logged.tv_sec = 0;
+ child->slow_logged.tv_usec = 0;
+ }
+ }
+#endif
+
+ if (proc.request_stage > FPM_REQUEST_ACCEPTING && proc.request_stage < FPM_REQUEST_END) {
+ char purified_script_filename[sizeof(proc.script_filename)];
+ struct timeval tv;
+
+ timersub(now, &proc.accepted, &tv);
+
+#if HAVE_FPM_TRACE
+ if (child->slow_logged.tv_sec == 0 && slowlog_timeout &&
+ proc.request_stage == FPM_REQUEST_EXECUTING && tv.tv_sec >= slowlog_timeout) {
+
+ str_purify_filename(purified_script_filename, proc.script_filename, sizeof(proc.script_filename));
+
+ child->slow_logged = proc.accepted;
+ child->tracer = fpm_php_trace;
+
+ fpm_trace_signal(child->pid);
+
+ zlog(ZLOG_WARNING, "[pool %s] child %d, script '%s' (request: \"%s %s\") executing too slow (%d.%06d sec), logging",
+ child->wp->config->name, (int) child->pid, purified_script_filename, proc.request_method, proc.request_uri,
+ (int) tv.tv_sec, (int) tv.tv_usec);
+ }
+ else
+#endif
+ if (terminate_timeout && tv.tv_sec >= terminate_timeout) {
+ str_purify_filename(purified_script_filename, proc.script_filename, sizeof(proc.script_filename));
+ fpm_pctl_kill(child->pid, FPM_PCTL_TERM);
+
+ zlog(ZLOG_WARNING, "[pool %s] child %d, script '%s' (request: \"%s %s\") execution timed out (%d.%06d sec), terminating",
+ child->wp->config->name, (int) child->pid, purified_script_filename, proc.request_method, proc.request_uri,
+ (int) tv.tv_sec, (int) tv.tv_usec);
+ }
+ }
+}
+/* }}} */
+
+int fpm_request_is_idle(struct fpm_child_s *child) /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+
+ /* no need in atomicity here */
+ proc = fpm_scoreboard_proc_get(child->wp->scoreboard, child->scoreboard_i);
+ if (!proc) {
+ return 0;
+ }
+
+ return proc->request_stage == FPM_REQUEST_ACCEPTING;
+}
+/* }}} */
+
+int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv) /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+
+ if (!tv) return -1;
+
+ proc = fpm_scoreboard_proc_get(child->wp->scoreboard, child->scoreboard_i);
+ if (!proc) {
+ return -1;
+ }
+
+ *tv = proc->tv;
+
+ return 1;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_request.h b/sapi/fpm/fpm/fpm_request.h
new file mode 100644
index 0000000..aebd36c
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_request.h
@@ -0,0 +1,32 @@
+
+ /* $Id: fpm_request.h,v 1.4 2008/07/20 01:47:16 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_REQUEST_H
+#define FPM_REQUEST_H 1
+
+void fpm_request_accepting(); /* hanging in accept() */
+void fpm_request_reading_headers(); /* start reading fastcgi request from very first byte */
+void fpm_request_info(); /* not a stage really but a point in the php code, where all request params have become known to sapi */
+void fpm_request_executing(); /* the script is executing */
+void fpm_request_end(TSRMLS_D); /* request ended: script response have been sent to web server */
+void fpm_request_finished(); /* request processed: cleaning current request */
+
+struct fpm_child_s;
+struct timeval;
+
+void fpm_request_check_timed_out(struct fpm_child_s *child, struct timeval *tv, int terminate_timeout, int slowlog_timeout);
+int fpm_request_is_idle(struct fpm_child_s *child);
+const char *fpm_request_get_stage_name(int stage);
+int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv);
+
+enum fpm_request_stage_e {
+ FPM_REQUEST_ACCEPTING = 1,
+ FPM_REQUEST_READING_HEADERS,
+ FPM_REQUEST_INFO,
+ FPM_REQUEST_EXECUTING,
+ FPM_REQUEST_END,
+ FPM_REQUEST_FINISHED
+};
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_scoreboard.c b/sapi/fpm/fpm/fpm_scoreboard.c
new file mode 100644
index 0000000..24463a9
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_scoreboard.c
@@ -0,0 +1,331 @@
+
+ /* $Id: fpm_status.c 312399 2011-06-23 08:03:52Z fat $ */
+ /* (c) 2009 Jerome Loyet */
+
+#include "php.h"
+#include "SAPI.h"
+#include <stdio.h>
+#include <time.h>
+
+#include "fpm_config.h"
+#include "fpm_scoreboard.h"
+#include "fpm_shm.h"
+#include "fpm_sockets.h"
+#include "fpm_worker_pool.h"
+#include "fpm_clock.h"
+#include "zlog.h"
+
+static struct fpm_scoreboard_s *fpm_scoreboard = NULL;
+static int fpm_scoreboard_i = -1;
+#ifdef HAVE_TIMES
+static float fpm_scoreboard_tick;
+#endif
+
+
+int fpm_scoreboard_init_main() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ int i;
+
+#ifdef HAVE_TIMES
+#if (defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK))
+ fpm_scoreboard_tick = sysconf(_SC_CLK_TCK);
+#else /* _SC_CLK_TCK */
+#ifdef HZ
+ fpm_scoreboard_tick = HZ;
+#else /* HZ */
+ fpm_scoreboard_tick = 100;
+#endif /* HZ */
+#endif /* _SC_CLK_TCK */
+ zlog(ZLOG_DEBUG, "got clock tick '%.0f'", fpm_scoreboard_tick);
+#endif /* HAVE_TIMES */
+
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (wp->config->pm_max_children < 1) {
+ zlog(ZLOG_ERROR, "[pool %s] Unable to create scoreboard SHM because max_client is not set", wp->config->name);
+ return -1;
+ }
+
+ if (wp->scoreboard) {
+ zlog(ZLOG_ERROR, "[pool %s] Unable to create scoreboard SHM because it already exists", wp->config->name);
+ return -1;
+ }
+
+ wp->scoreboard = fpm_shm_alloc(sizeof(struct fpm_scoreboard_s) + (wp->config->pm_max_children - 1) * sizeof(struct fpm_scoreboard_proc_s *));
+ if (!wp->scoreboard) {
+ return -1;
+ }
+ wp->scoreboard->nprocs = wp->config->pm_max_children;
+ for (i = 0; i < wp->scoreboard->nprocs; i++) {
+ wp->scoreboard->procs[i] = fpm_shm_alloc(sizeof(struct fpm_scoreboard_proc_s));
+ if (!wp->scoreboard->procs[i]) {
+ return -1;
+ }
+ memset(wp->scoreboard->procs[i], 0, sizeof(struct fpm_scoreboard_proc_s));
+ }
+
+ wp->scoreboard->pm = wp->config->pm;
+ wp->scoreboard->start_epoch = time(NULL);
+ strlcpy(wp->scoreboard->pool, wp->config->name, sizeof(wp->scoreboard->pool));
+ }
+ return 0;
+}
+/* }}} */
+
+void fpm_scoreboard_update(int idle, int active, int lq, int lq_len, int requests, int max_children_reached, int slow_rq, int action, struct fpm_scoreboard_s *scoreboard) /* {{{ */
+{
+ if (!scoreboard) {
+ scoreboard = fpm_scoreboard;
+ }
+ if (!scoreboard) {
+ zlog(ZLOG_WARNING, "Unable to update scoreboard: the SHM has not been found");
+ return;
+ }
+
+
+ fpm_spinlock(&scoreboard->lock, 0);
+ if (action == FPM_SCOREBOARD_ACTION_SET) {
+ if (idle >= 0) {
+ scoreboard->idle = idle;
+ }
+ if (active >= 0) {
+ scoreboard->active = active;
+ }
+ if (lq >= 0) {
+ scoreboard->lq = lq;
+ }
+ if (lq_len >= 0) {
+ scoreboard->lq_len = lq_len;
+ }
+#ifdef HAVE_FPM_LQ /* prevent unnecessary test */
+ if (scoreboard->lq > scoreboard->lq_max) {
+ scoreboard->lq_max = scoreboard->lq;
+ }
+#endif
+ if (requests >= 0) {
+ scoreboard->requests = requests;
+ }
+
+ if (max_children_reached >= 0) {
+ scoreboard->max_children_reached = max_children_reached;
+ }
+ if (slow_rq > 0) {
+ scoreboard->slow_rq += slow_rq;
+ }
+ } else {
+ if (scoreboard->idle + idle > 0) {
+ scoreboard->idle += idle;
+ } else {
+ scoreboard->idle = 0;
+ }
+
+ if (scoreboard->active + active > 0) {
+ scoreboard->active += active;
+ } else {
+ scoreboard->active = 0;
+ }
+
+ if (scoreboard->requests + requests > 0) {
+ scoreboard->requests += requests;
+ } else {
+ scoreboard->requests = 0;
+ }
+
+ if (scoreboard->max_children_reached + max_children_reached > 0) {
+ scoreboard->max_children_reached += max_children_reached;
+ } else {
+ scoreboard->max_children_reached = 0;
+ }
+ }
+
+ if (scoreboard->active > scoreboard->active_max) {
+ scoreboard->active_max = scoreboard->active;
+ }
+
+ fpm_unlock(scoreboard->lock);
+}
+/* }}} */
+
+struct fpm_scoreboard_s *fpm_scoreboard_get() /* {{{*/
+{
+ return fpm_scoreboard;
+}
+/* }}} */
+
+struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get(struct fpm_scoreboard_s *scoreboard, int child_index) /* {{{*/
+{
+ if (!scoreboard) {
+ scoreboard = fpm_scoreboard;
+ }
+
+ if (!scoreboard) {
+ return NULL;
+ }
+
+ if (child_index < 0) {
+ child_index = fpm_scoreboard_i;
+ }
+
+ if (child_index < 0 || child_index >= scoreboard->nprocs) {
+ return NULL;
+ }
+
+ return scoreboard->procs[child_index];
+}
+/* }}} */
+
+struct fpm_scoreboard_s *fpm_scoreboard_acquire(struct fpm_scoreboard_s *scoreboard, int nohang) /* {{{ */
+{
+ struct fpm_scoreboard_s *s;
+
+ s = scoreboard ? scoreboard : fpm_scoreboard;
+ if (!s) {
+ return NULL;
+ }
+
+ if (!fpm_spinlock(&s->lock, nohang)) {
+ return NULL;
+ }
+ return s;
+}
+/* }}} */
+
+void fpm_scoreboard_release(struct fpm_scoreboard_s *scoreboard) {
+ if (!scoreboard) {
+ return;
+ }
+
+ scoreboard->lock = 0;
+}
+
+struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_acquire(struct fpm_scoreboard_s *scoreboard, int child_index, int nohang) /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+
+ proc = fpm_scoreboard_proc_get(scoreboard, child_index);
+ if (!proc) {
+ return NULL;
+ }
+
+ if (!fpm_spinlock(&proc->lock, nohang)) {
+ return NULL;
+ }
+
+ return proc;
+}
+/* }}} */
+
+void fpm_scoreboard_proc_release(struct fpm_scoreboard_proc_s *proc) /* {{{ */
+{
+ if (!proc) {
+ return;
+ }
+
+ proc->lock = 0;
+}
+
+void fpm_scoreboard_free(struct fpm_scoreboard_s *scoreboard) /* {{{ */
+{
+ int i;
+
+ if (!scoreboard) {
+ zlog(ZLOG_ERROR, "**scoreboard is NULL");
+ return;
+ }
+
+ for (i = 0; i < scoreboard->nprocs; i++) {
+ if (!scoreboard->procs[i]) {
+ continue;
+ }
+ fpm_shm_free(scoreboard->procs[i], sizeof(struct fpm_scoreboard_proc_s));
+ }
+ fpm_shm_free(scoreboard, sizeof(struct fpm_scoreboard_s));
+}
+/* }}} */
+
+void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_index, pid_t pid) /* {{{ */
+{
+ struct fpm_scoreboard_proc_s *proc;
+ fpm_scoreboard = scoreboard;
+ fpm_scoreboard_i = child_index;
+ proc = fpm_scoreboard_proc_get(scoreboard, child_index);
+ if (!proc) {
+ return;
+ }
+ proc->pid = pid;
+ proc->start_epoch = time(NULL);
+}
+/* }}} */
+
+void fpm_scoreboard_proc_free(struct fpm_scoreboard_s *scoreboard, int child_index) /* {{{ */
+{
+ if (!scoreboard) {
+ return;
+ }
+
+ if (child_index < 0 || child_index >= scoreboard->nprocs) {
+ return;
+ }
+
+ if (scoreboard->procs[child_index] && scoreboard->procs[child_index]->used > 0) {
+ memset(scoreboard->procs[child_index], 0, sizeof(struct fpm_scoreboard_proc_s));
+ }
+
+ /* set this slot as free to avoid search on next alloc */
+ scoreboard->free_proc = child_index;
+}
+/* }}} */
+
+int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_index) /* {{{ */
+{
+ int i = -1;
+
+ if (!scoreboard || !child_index) {
+ return -1;
+ }
+
+ /* first try the slot which is supposed to be free */
+ if (scoreboard->free_proc >= 0 && scoreboard->free_proc < scoreboard->nprocs) {
+ if (scoreboard->procs[scoreboard->free_proc] && !scoreboard->procs[scoreboard->free_proc]->used) {
+ i = scoreboard->free_proc;
+ }
+ }
+
+ if (i < 0) { /* the supposed free slot is not, let's search for a free slot */
+ zlog(ZLOG_DEBUG, "[pool %s] the proc->free_slot was not free. Let's search", scoreboard->pool);
+ for (i = 0; i < scoreboard->nprocs; i++) {
+ if (scoreboard->procs[i] && !scoreboard->procs[i]->used) { /* found */
+ break;
+ }
+ }
+ }
+
+ /* no free slot */
+ if (i < 0 || i >= scoreboard->nprocs) {
+ zlog(ZLOG_ERROR, "[pool %s] no free scoreboard slot", scoreboard->pool);
+ return -1;
+ }
+
+ scoreboard->procs[i]->used = 1;
+ *child_index = i;
+
+ /* supposed next slot is free */
+ if (i + 1 >= scoreboard->nprocs) {
+ scoreboard->free_proc = 0;
+ } else {
+ scoreboard->free_proc = i + 1;
+ }
+
+ return 0;
+}
+/* }}} */
+
+#ifdef HAVE_TIMES
+float fpm_scoreboard_get_tick() /* {{{ */
+{
+ return fpm_scoreboard_tick;
+}
+/* }}} */
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_scoreboard.h b/sapi/fpm/fpm/fpm_scoreboard.h
new file mode 100644
index 0000000..f58a287
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_scoreboard.h
@@ -0,0 +1,94 @@
+
+ /* $Id: fpm_status.h 312263 2011-06-18 17:46:16Z felipe $ */
+ /* (c) 2009 Jerome Loyet */
+
+#ifndef FPM_SCOREBOARD_H
+#define FPM_SCOREBOARD_H 1
+
+#include <sys/time.h>
+#ifdef HAVE_TIMES
+#include <sys/times.h>
+#endif
+
+#include "fpm_request.h"
+#include "fpm_worker_pool.h"
+#include "fpm_atomic.h"
+
+#define FPM_SCOREBOARD_ACTION_SET 0
+#define FPM_SCOREBOARD_ACTION_INC 1
+
+struct fpm_scoreboard_proc_s {
+ union {
+ atomic_t lock;
+ char dummy[16];
+ };
+ int used;
+ time_t start_epoch;
+ pid_t pid;
+ unsigned long requests;
+ enum fpm_request_stage_e request_stage;
+ struct timeval accepted;
+ struct timeval duration;
+ time_t accepted_epoch;
+ struct timeval tv;
+ char request_uri[128];
+ char query_string[512];
+ char request_method[16];
+ size_t content_length; /* used with POST only */
+ char script_filename[256];
+ char auth_user[32];
+#ifdef HAVE_TIMES
+ struct tms cpu_accepted;
+ struct timeval cpu_duration;
+ struct tms last_request_cpu;
+ struct timeval last_request_cpu_duration;
+#endif
+ size_t memory;
+};
+
+struct fpm_scoreboard_s {
+ union {
+ atomic_t lock;
+ char dummy[16];
+ };
+ char pool[32];
+ int pm;
+ time_t start_epoch;
+ int idle;
+ int active;
+ int active_max;
+ unsigned long int requests;
+ unsigned int max_children_reached;
+ int lq;
+ int lq_max;
+ unsigned int lq_len;
+ unsigned int nprocs;
+ int free_proc;
+ unsigned long int slow_rq;
+ struct fpm_scoreboard_proc_s *procs[];
+};
+
+int fpm_scoreboard_init_main();
+int fpm_scoreboard_init_child(struct fpm_worker_pool_s *wp);
+
+void fpm_scoreboard_update(int idle, int active, int lq, int lq_len, int requests, int max_children_reached, int slow_rq, int action, struct fpm_scoreboard_s *scoreboard);
+struct fpm_scoreboard_s *fpm_scoreboard_get();
+struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_get(struct fpm_scoreboard_s *scoreboard, int child_index);
+
+struct fpm_scoreboard_s *fpm_scoreboard_acquire(struct fpm_scoreboard_s *scoreboard, int nohang);
+void fpm_scoreboard_release(struct fpm_scoreboard_s *scoreboard);
+struct fpm_scoreboard_proc_s *fpm_scoreboard_proc_acquire(struct fpm_scoreboard_s *scoreboard, int child_index, int nohang);
+void fpm_scoreboard_proc_release(struct fpm_scoreboard_proc_s *proc);
+
+void fpm_scoreboard_free(struct fpm_scoreboard_s *scoreboard);
+
+void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_index, pid_t pid);
+
+void fpm_scoreboard_proc_free(struct fpm_scoreboard_s *scoreboard, int child_index);
+int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_index);
+
+#ifdef HAVE_TIMES
+float fpm_scoreboard_get_tick();
+#endif
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_shm.c b/sapi/fpm/fpm/fpm_shm.c
new file mode 100644
index 0000000..9226adf
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_shm.c
@@ -0,0 +1,70 @@
+
+ /* $Id: fpm_shm.c,v 1.3 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin, Jerome Loyet */
+
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+
+#include "fpm_shm.h"
+#include "zlog.h"
+
+
+/* MAP_ANON is deprecated, but not in macosx */
+#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+static size_t fpm_shm_size = 0;
+
+void *fpm_shm_alloc(size_t size) /* {{{ */
+{
+ void *mem;
+
+ mem = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+
+#ifdef MAP_FAILED
+ if (mem == MAP_FAILED) {
+ zlog(ZLOG_SYSERROR, "unable to allocate %zu bytes in shared memory: %s", size, strerror(errno));
+ return NULL;
+ }
+#endif
+
+ if (!mem) {
+ zlog(ZLOG_SYSERROR, "unable to allocate %zu bytes in shared memory", size);
+ return NULL;
+ }
+
+ memset(mem, 0, size);
+ fpm_shm_size += size;
+ return mem;
+}
+/* }}} */
+
+int fpm_shm_free(void *mem, size_t size) /* {{{ */
+{
+ if (!mem) {
+ zlog(ZLOG_ERROR, "mem is NULL");
+ return 0;
+ }
+
+ if (munmap(mem, size) == -1) {
+ zlog(ZLOG_SYSERROR, "Unable to free shm");
+ return 0;
+ }
+
+ if (fpm_shm_size - size > 0) {
+ fpm_shm_size -= size;
+ } else {
+ fpm_shm_size = 0;
+ }
+
+ return 1;
+}
+/* }}} */
+
+size_t fpm_shm_get_size_allocated() /* {{{*/
+{
+ return fpm_shm_size;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_shm.h b/sapi/fpm/fpm/fpm_shm.h
new file mode 100644
index 0000000..bcb6099
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_shm.h
@@ -0,0 +1,13 @@
+
+ /* $Id: fpm_shm.h,v 1.3 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_SHM_H
+#define FPM_SHM_H 1
+
+void *fpm_shm_alloc(size_t size);
+int fpm_shm_free(void *mem, size_t size);
+size_t fpm_shm_get_size_allocated();
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_signals.c b/sapi/fpm/fpm/fpm_signals.c
new file mode 100644
index 0000000..8993a86
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_signals.c
@@ -0,0 +1,251 @@
+
+ /* $Id: fpm_signals.c,v 1.24 2008/08/26 15:09:15 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "fpm.h"
+#include "fpm_signals.h"
+#include "fpm_sockets.h"
+#include "fpm_php.h"
+#include "zlog.h"
+
+static int sp[2];
+
+const char *fpm_signal_names[NSIG + 1] = {
+#ifdef SIGHUP
+ [SIGHUP] = "SIGHUP",
+#endif
+#ifdef SIGINT
+ [SIGINT] = "SIGINT",
+#endif
+#ifdef SIGQUIT
+ [SIGQUIT] = "SIGQUIT",
+#endif
+#ifdef SIGILL
+ [SIGILL] = "SIGILL",
+#endif
+#ifdef SIGTRAP
+ [SIGTRAP] = "SIGTRAP",
+#endif
+#ifdef SIGABRT
+ [SIGABRT] = "SIGABRT",
+#endif
+#ifdef SIGEMT
+ [SIGEMT] = "SIGEMT",
+#endif
+#ifdef SIGBUS
+ [SIGBUS] = "SIGBUS",
+#endif
+#ifdef SIGFPE
+ [SIGFPE] = "SIGFPE",
+#endif
+#ifdef SIGKILL
+ [SIGKILL] = "SIGKILL",
+#endif
+#ifdef SIGUSR1
+ [SIGUSR1] = "SIGUSR1",
+#endif
+#ifdef SIGSEGV
+ [SIGSEGV] = "SIGSEGV",
+#endif
+#ifdef SIGUSR2
+ [SIGUSR2] = "SIGUSR2",
+#endif
+#ifdef SIGPIPE
+ [SIGPIPE] = "SIGPIPE",
+#endif
+#ifdef SIGALRM
+ [SIGALRM] = "SIGALRM",
+#endif
+#ifdef SIGTERM
+ [SIGTERM] = "SIGTERM",
+#endif
+#ifdef SIGCHLD
+ [SIGCHLD] = "SIGCHLD",
+#endif
+#ifdef SIGCONT
+ [SIGCONT] = "SIGCONT",
+#endif
+#ifdef SIGSTOP
+ [SIGSTOP] = "SIGSTOP",
+#endif
+#ifdef SIGTSTP
+ [SIGTSTP] = "SIGTSTP",
+#endif
+#ifdef SIGTTIN
+ [SIGTTIN] = "SIGTTIN",
+#endif
+#ifdef SIGTTOU
+ [SIGTTOU] = "SIGTTOU",
+#endif
+#ifdef SIGURG
+ [SIGURG] = "SIGURG",
+#endif
+#ifdef SIGXCPU
+ [SIGXCPU] = "SIGXCPU",
+#endif
+#ifdef SIGXFSZ
+ [SIGXFSZ] = "SIGXFSZ",
+#endif
+#ifdef SIGVTALRM
+ [SIGVTALRM] = "SIGVTALRM",
+#endif
+#ifdef SIGPROF
+ [SIGPROF] = "SIGPROF",
+#endif
+#ifdef SIGWINCH
+ [SIGWINCH] = "SIGWINCH",
+#endif
+#ifdef SIGINFO
+ [SIGINFO] = "SIGINFO",
+#endif
+#ifdef SIGIO
+ [SIGIO] = "SIGIO",
+#endif
+#ifdef SIGPWR
+ [SIGPWR] = "SIGPWR",
+#endif
+#ifdef SIGSYS
+ [SIGSYS] = "SIGSYS",
+#endif
+#ifdef SIGWAITING
+ [SIGWAITING] = "SIGWAITING",
+#endif
+#ifdef SIGLWP
+ [SIGLWP] = "SIGLWP",
+#endif
+#ifdef SIGFREEZE
+ [SIGFREEZE] = "SIGFREEZE",
+#endif
+#ifdef SIGTHAW
+ [SIGTHAW] = "SIGTHAW",
+#endif
+#ifdef SIGCANCEL
+ [SIGCANCEL] = "SIGCANCEL",
+#endif
+#ifdef SIGLOST
+ [SIGLOST] = "SIGLOST",
+#endif
+};
+
+static void sig_soft_quit(int signo) /* {{{ */
+{
+ int saved_errno = errno;
+
+ /* closing fastcgi listening socket will force fcgi_accept() exit immediately */
+ close(0);
+ socket(AF_UNIX, SOCK_STREAM, 0);
+ fpm_php_soft_quit();
+ errno = saved_errno;
+}
+/* }}} */
+
+static void sig_handler(int signo) /* {{{ */
+{
+ static const char sig_chars[NSIG + 1] = {
+ [SIGTERM] = 'T',
+ [SIGINT] = 'I',
+ [SIGUSR1] = '1',
+ [SIGUSR2] = '2',
+ [SIGQUIT] = 'Q',
+ [SIGCHLD] = 'C'
+ };
+ char s;
+ int saved_errno;
+
+ if (fpm_globals.parent_pid != getpid()) {
+ /* prevent a signal race condition when child process
+ have not set up it's own signal handler yet */
+ return;
+ }
+
+ saved_errno = errno;
+ s = sig_chars[signo];
+ write(sp[1], &s, sizeof(s));
+ errno = saved_errno;
+}
+/* }}} */
+
+int fpm_signals_init_main() /* {{{ */
+{
+ struct sigaction act;
+
+ if (0 > socketpair(AF_UNIX, SOCK_STREAM, 0, sp)) {
+ zlog(ZLOG_SYSERROR, "failed to init signals: socketpair()");
+ return -1;
+ }
+
+ if (0 > fd_set_blocked(sp[0], 0) || 0 > fd_set_blocked(sp[1], 0)) {
+ zlog(ZLOG_SYSERROR, "failed to init signals: fd_set_blocked()");
+ return -1;
+ }
+
+ if (0 > fcntl(sp[0], F_SETFD, FD_CLOEXEC) || 0 > fcntl(sp[1], F_SETFD, FD_CLOEXEC)) {
+ zlog(ZLOG_SYSERROR, "falied to init signals: fcntl(F_SETFD, FD_CLOEXEC)");
+ return -1;
+ }
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = sig_handler;
+ sigfillset(&act.sa_mask);
+
+ if (0 > sigaction(SIGTERM, &act, 0) ||
+ 0 > sigaction(SIGINT, &act, 0) ||
+ 0 > sigaction(SIGUSR1, &act, 0) ||
+ 0 > sigaction(SIGUSR2, &act, 0) ||
+ 0 > sigaction(SIGCHLD, &act, 0) ||
+ 0 > sigaction(SIGQUIT, &act, 0)) {
+
+ zlog(ZLOG_SYSERROR, "failed to init signals: sigaction()");
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_signals_init_child() /* {{{ */
+{
+ struct sigaction act, act_dfl;
+
+ memset(&act, 0, sizeof(act));
+ memset(&act_dfl, 0, sizeof(act_dfl));
+
+ act.sa_handler = &sig_soft_quit;
+ act.sa_flags |= SA_RESTART;
+
+ act_dfl.sa_handler = SIG_DFL;
+
+ close(sp[0]);
+ close(sp[1]);
+
+ if (0 > sigaction(SIGTERM, &act_dfl, 0) ||
+ 0 > sigaction(SIGINT, &act_dfl, 0) ||
+ 0 > sigaction(SIGUSR1, &act_dfl, 0) ||
+ 0 > sigaction(SIGUSR2, &act_dfl, 0) ||
+ 0 > sigaction(SIGCHLD, &act_dfl, 0) ||
+ 0 > sigaction(SIGQUIT, &act, 0)) {
+
+ zlog(ZLOG_SYSERROR, "failed to init child signals: sigaction()");
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_signals_get_fd() /* {{{ */
+{
+ return sp[0];
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_signals.h b/sapi/fpm/fpm/fpm_signals.h
new file mode 100644
index 0000000..eb80fae
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_signals.h
@@ -0,0 +1,16 @@
+
+ /* $Id: fpm_signals.h,v 1.5 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_SIGNALS_H
+#define FPM_SIGNALS_H 1
+
+#include <signal.h>
+
+int fpm_signals_init_main();
+int fpm_signals_init_child();
+int fpm_signals_get_fd();
+
+extern const char *fpm_signal_names[NSIG + 1];
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_sockets.c b/sapi/fpm/fpm/fpm_sockets.c
new file mode 100644
index 0000000..76759e7
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_sockets.c
@@ -0,0 +1,477 @@
+
+ /* $Id: fpm_sockets.c,v 1.20.2.1 2008/12/13 03:21:18 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h> /* for chmod(2) */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "zlog.h"
+#include "fpm_arrays.h"
+#include "fpm_sockets.h"
+#include "fpm_worker_pool.h"
+#include "fpm_unix.h"
+#include "fpm_str.h"
+#include "fpm_env.h"
+#include "fpm_cleanup.h"
+#include "fpm_scoreboard.h"
+
+struct listening_socket_s {
+ int refcount;
+ int sock;
+ int type;
+ char *key;
+};
+
+static struct fpm_array_s sockets_list;
+
+static int fpm_sockets_resolve_af_inet(char *node, char *service, struct sockaddr_in *addr) /* {{{ */
+{
+ struct addrinfo *res;
+ struct addrinfo hints;
+ int ret;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ ret = getaddrinfo(node, service, &hints, &res);
+
+ if (ret != 0) {
+ zlog(ZLOG_ERROR, "can't resolve hostname '%s%s%s': getaddrinfo said: %s%s%s\n",
+ node, service ? ":" : "", service ? service : "",
+ gai_strerror(ret), ret == EAI_SYSTEM ? ", system error: " : "", ret == EAI_SYSTEM ? strerror(errno) : "");
+ return -1;
+ }
+
+ *addr = *(struct sockaddr_in *) res->ai_addr;
+ freeaddrinfo(res);
+ return 0;
+}
+/* }}} */
+
+enum { FPM_GET_USE_SOCKET = 1, FPM_STORE_SOCKET = 2, FPM_STORE_USE_SOCKET = 3 };
+
+static void fpm_sockets_cleanup(int which, void *arg) /* {{{ */
+{
+ unsigned i;
+ char *env_value = 0;
+ int p = 0;
+ struct listening_socket_s *ls = sockets_list.data;
+
+ for (i = 0; i < sockets_list.used; i++, ls++) {
+ if (which != FPM_CLEANUP_PARENT_EXEC) {
+ close(ls->sock);
+ } else { /* on PARENT EXEC we want socket fds to be inherited through environment variable */
+ char fd[32];
+ sprintf(fd, "%d", ls->sock);
+ env_value = realloc(env_value, p + (p ? 1 : 0) + strlen(ls->key) + 1 + strlen(fd) + 1);
+ p += sprintf(env_value + p, "%s%s=%s", p ? "," : "", ls->key, fd);
+ }
+
+ if (which == FPM_CLEANUP_PARENT_EXIT_MAIN) {
+ if (ls->type == FPM_AF_UNIX) {
+ unlink(ls->key);
+ }
+ }
+ free(ls->key);
+ }
+
+ if (env_value) {
+ setenv("FPM_SOCKETS", env_value, 1);
+ free(env_value);
+ }
+
+ fpm_array_free(&sockets_list);
+}
+/* }}} */
+
+static int fpm_sockets_hash_op(int sock, struct sockaddr *sa, char *key, int type, int op) /* {{{ */
+{
+ if (key == NULL) {
+ switch (type) {
+ case FPM_AF_INET : {
+ struct sockaddr_in *sa_in = (struct sockaddr_in *) sa;
+ key = alloca(sizeof("xxx.xxx.xxx.xxx:ppppp"));
+ sprintf(key, "%u.%u.%u.%u:%u", IPQUAD(&sa_in->sin_addr), (unsigned int) ntohs(sa_in->sin_port));
+ break;
+ }
+
+ case FPM_AF_UNIX : {
+ struct sockaddr_un *sa_un = (struct sockaddr_un *) sa;
+ key = alloca(strlen(sa_un->sun_path) + 1);
+ strcpy(key, sa_un->sun_path);
+ break;
+ }
+
+ default :
+ return -1;
+ }
+ }
+
+ switch (op) {
+
+ case FPM_GET_USE_SOCKET :
+ {
+ unsigned i;
+ struct listening_socket_s *ls = sockets_list.data;
+
+ for (i = 0; i < sockets_list.used; i++, ls++) {
+ if (!strcmp(ls->key, key)) {
+ ++ls->refcount;
+ return ls->sock;
+ }
+ }
+ break;
+ }
+
+ case FPM_STORE_SOCKET : /* inherited socket */
+ case FPM_STORE_USE_SOCKET : /* just created */
+ {
+ struct listening_socket_s *ls;
+
+ ls = fpm_array_push(&sockets_list);
+ if (!ls) {
+ break;
+ }
+
+ if (op == FPM_STORE_SOCKET) {
+ ls->refcount = 0;
+ } else {
+ ls->refcount = 1;
+ }
+ ls->type = type;
+ ls->sock = sock;
+ ls->key = strdup(key);
+
+ return 0;
+ }
+ }
+ return -1;
+}
+/* }}} */
+
+static int fpm_sockets_new_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen) /* {{{ */
+{
+ int flags = 1;
+ int sock;
+ mode_t saved_umask;
+
+ sock = socket(sa->sa_family, SOCK_STREAM, 0);
+
+ if (0 > sock) {
+ zlog(ZLOG_SYSERROR, "failed to create new listening socket: socket()");
+ return -1;
+ }
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
+
+ if (wp->listen_address_domain == FPM_AF_UNIX) {
+ if (fpm_socket_unix_test_connect((struct sockaddr_un *)sa, socklen) == 0) {
+ zlog(ZLOG_ERROR, "An another FPM instance seems to already listen on %s", ((struct sockaddr_un *) sa)->sun_path);
+ return -1;
+ }
+ unlink( ((struct sockaddr_un *) sa)->sun_path);
+ saved_umask = umask(0777 ^ wp->socket_mode);
+ }
+
+ if (0 > bind(sock, sa, socklen)) {
+ zlog(ZLOG_SYSERROR, "unable to bind listening socket for address '%s'", wp->config->listen_address);
+ if (wp->listen_address_domain == FPM_AF_UNIX) {
+ umask(saved_umask);
+ }
+ return -1;
+ }
+
+ if (wp->listen_address_domain == FPM_AF_UNIX) {
+ char *path = ((struct sockaddr_un *) sa)->sun_path;
+
+ umask(saved_umask);
+
+ if (wp->socket_uid != -1 || wp->socket_gid != -1) {
+ if (0 > chown(path, wp->socket_uid, wp->socket_gid)) {
+ zlog(ZLOG_SYSERROR, "failed to chown() the socket '%s'", wp->config->listen_address);
+ return -1;
+ }
+ }
+ }
+
+ if (0 > listen(sock, wp->config->listen_backlog)) {
+ zlog(ZLOG_SYSERROR, "failed to listen to address '%s'", wp->config->listen_address);
+ return -1;
+ }
+
+ return sock;
+}
+/* }}} */
+
+static int fpm_sockets_get_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen) /* {{{ */
+{
+ int sock;
+
+ sock = fpm_sockets_hash_op(0, sa, 0, wp->listen_address_domain, FPM_GET_USE_SOCKET);
+ if (sock >= 0) {
+ return sock;
+ }
+
+ sock = fpm_sockets_new_listening_socket(wp, sa, socklen);
+ fpm_sockets_hash_op(sock, sa, 0, wp->listen_address_domain, FPM_STORE_USE_SOCKET);
+
+ return sock;
+}
+/* }}} */
+
+enum fpm_address_domain fpm_sockets_domain_from_address(char *address) /* {{{ */
+{
+ if (strchr(address, ':')) {
+ return FPM_AF_INET;
+ }
+
+ if (strlen(address) == strspn(address, "0123456789")) {
+ return FPM_AF_INET;
+ }
+ return FPM_AF_UNIX;
+}
+/* }}} */
+
+static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct sockaddr_in sa_in;
+ char *dup_address = strdup(wp->config->listen_address);
+ char *port_str = strchr(dup_address, ':');
+ char *addr = NULL;
+ int port = 0;
+
+ if (port_str) { /* this is host:port pair */
+ *port_str++ = '\0';
+ port = atoi(port_str);
+ addr = dup_address;
+ } else if (strlen(dup_address) == strspn(dup_address, "0123456789")) { /* this is port */
+ port = atoi(dup_address);
+ port_str = dup_address;
+ }
+
+ if (port == 0) {
+ zlog(ZLOG_ERROR, "invalid port value '%s'", port_str);
+ return -1;
+ }
+
+ memset(&sa_in, 0, sizeof(sa_in));
+
+ if (addr) {
+ sa_in.sin_addr.s_addr = inet_addr(addr);
+ if (sa_in.sin_addr.s_addr == INADDR_NONE) { /* do resolve */
+ if (0 > fpm_sockets_resolve_af_inet(addr, NULL, &sa_in)) {
+ return -1;
+ }
+ zlog(ZLOG_NOTICE, "address '%s' resolved as %u.%u.%u.%u", addr, IPQUAD(&sa_in.sin_addr));
+ }
+ } else {
+ sa_in.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+ sa_in.sin_family = AF_INET;
+ sa_in.sin_port = htons(port);
+ free(dup_address);
+ return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_in, sizeof(struct sockaddr_in));
+}
+/* }}} */
+
+static int fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct sockaddr_un sa_un;
+
+ memset(&sa_un, 0, sizeof(sa_un));
+ strlcpy(sa_un.sun_path, wp->config->listen_address, sizeof(sa_un.sun_path));
+ sa_un.sun_family = AF_UNIX;
+ return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_un, sizeof(struct sockaddr_un));
+}
+/* }}} */
+
+int fpm_sockets_init_main() /* {{{ */
+{
+ unsigned i, lq_len;
+ struct fpm_worker_pool_s *wp;
+ char *inherited = getenv("FPM_SOCKETS");
+ struct listening_socket_s *ls;
+
+ if (0 == fpm_array_init(&sockets_list, sizeof(struct listening_socket_s), 10)) {
+ return -1;
+ }
+
+ /* import inherited sockets */
+ while (inherited && *inherited) {
+ char *comma = strchr(inherited, ',');
+ int type, fd_no;
+ char *eq;
+
+ if (comma) {
+ *comma = '\0';
+ }
+
+ eq = strchr(inherited, '=');
+ if (eq) {
+ *eq = '\0';
+ fd_no = atoi(eq + 1);
+ type = fpm_sockets_domain_from_address(inherited);
+ zlog(ZLOG_NOTICE, "using inherited socket fd=%d, \"%s\"", fd_no, inherited);
+ fpm_sockets_hash_op(fd_no, 0, inherited, type, FPM_STORE_SOCKET);
+ }
+
+ if (comma) {
+ inherited = comma + 1;
+ } else {
+ inherited = 0;
+ }
+ }
+
+ /* create all required sockets */
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ switch (wp->listen_address_domain) {
+ case FPM_AF_INET :
+ wp->listening_socket = fpm_socket_af_inet_listening_socket(wp);
+ break;
+
+ case FPM_AF_UNIX :
+ if (0 > fpm_unix_resolve_socket_premissions(wp)) {
+ return -1;
+ }
+ wp->listening_socket = fpm_socket_af_unix_listening_socket(wp);
+ break;
+ }
+
+ if (wp->listening_socket == -1) {
+ return -1;
+ }
+
+ if (wp->listen_address_domain == FPM_AF_INET && fpm_socket_get_listening_queue(wp->listening_socket, NULL, &lq_len) >= 0) {
+ fpm_scoreboard_update(-1, -1, -1, (int)lq_len, -1, -1, 0, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard);
+ }
+ }
+
+ /* close unused sockets that was inherited */
+ ls = sockets_list.data;
+
+ for (i = 0; i < sockets_list.used; ) {
+ if (ls->refcount == 0) {
+ close(ls->sock);
+ if (ls->type == FPM_AF_UNIX) {
+ unlink(ls->key);
+ }
+ free(ls->key);
+ fpm_array_item_remove(&sockets_list, i);
+ } else {
+ ++i;
+ ++ls;
+ }
+ }
+
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_sockets_cleanup, 0)) {
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+#if HAVE_FPM_LQ
+
+#ifdef HAVE_LQ_TCP_INFO
+
+#include <netinet/tcp.h>
+
+int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
+{
+ struct tcp_info info;
+ socklen_t len = sizeof(info);
+
+ if (0 > getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, &len)) {
+ zlog(ZLOG_SYSERROR, "failed to retrieve TCP_INFO for socket");
+ return -1;
+ }
+
+ /* kernel >= 2.6.24 return non-zero here, that means operation is supported */
+ if (info.tcpi_sacked == 0) {
+ return -1;
+ }
+
+ if (cur_lq) {
+ *cur_lq = info.tcpi_unacked;
+ }
+
+ if (max_lq) {
+ *max_lq = info.tcpi_sacked;
+ }
+
+ return 0;
+}
+
+#endif
+
+#ifdef HAVE_LQ_SO_LISTENQ
+
+int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
+{
+ int val;
+ socklen_t len = sizeof(val);
+
+ if (cur_lq) {
+ if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLEN, &val, &len)) {
+ return -1;
+ }
+
+ *cur_lq = val;
+ }
+
+ if (max_lq) {
+ if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &val, &len)) {
+ return -1;
+ }
+
+ *max_lq = val;
+ }
+
+ return 0;
+}
+
+#endif
+
+#else
+
+int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
+{
+ return -1;
+}
+
+#endif
+
+int fpm_socket_unix_test_connect(struct sockaddr_un *sock, size_t socklen) /* {{{ */
+{
+ int fd;
+
+ if (!sock || sock->sun_family != AF_UNIX) {
+ return -1;
+ }
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ return -1;
+ }
+
+ if (connect(fd, (struct sockaddr *)sock, socklen) == -1) {
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_sockets.h b/sapi/fpm/fpm/fpm_sockets.h
new file mode 100644
index 0000000..cce5712
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_sockets.h
@@ -0,0 +1,54 @@
+
+ /* $Id: fpm_sockets.h,v 1.12 2008/08/26 15:09:15 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_MISC_H
+#define FPM_MISC_H 1
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "fpm_worker_pool.h"
+
+/*
+ On FreeBSD and OpenBSD, backlog negative values are truncated to SOMAXCONN
+*/
+#if (__FreeBSD__) || (__OpenBSD__)
+#define FPM_BACKLOG_DEFAULT -1
+#else
+#define FPM_BACKLOG_DEFAULT 128
+#endif
+
+enum fpm_address_domain fpm_sockets_domain_from_address(char *addr);
+int fpm_sockets_init_main();
+int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq);
+int fpm_socket_unix_test_connect(struct sockaddr_un *sock, size_t socklen);
+
+
+static inline int fd_set_blocked(int fd, int blocked) /* {{{ */
+{
+ int flags = fcntl(fd, F_GETFL);
+
+ if (flags < 0) {
+ return -1;
+ }
+
+ if (blocked) {
+ flags &= ~O_NONBLOCK;
+ } else {
+ flags |= O_NONBLOCK;
+ }
+ return fcntl(fd, F_SETFL, flags);
+}
+/* }}} */
+
+#define IPQUAD(sin_addr) \
+ (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[0], \
+ (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[1], \
+ (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[2], \
+ (unsigned int) ((unsigned char *) &(sin_addr)->s_addr)[3]
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_status.c b/sapi/fpm/fpm/fpm_status.c
new file mode 100644
index 0000000..2363b57
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_status.c
@@ -0,0 +1,477 @@
+
+ /* $Id$ */
+ /* (c) 2009 Jerome Loyet */
+
+#include "php.h"
+#include "SAPI.h"
+#include <stdio.h>
+
+#include "fpm_config.h"
+#include "fpm_scoreboard.h"
+#include "fpm_status.h"
+#include "fpm_clock.h"
+#include "fpm_scoreboard.h"
+#include "zlog.h"
+#include "fpm_atomic.h"
+#include "fpm_conf.h"
+#include "fpm_php.h"
+#include <ext/standard/html.h>
+
+static char *fpm_status_uri = NULL;
+static char *fpm_status_ping_uri = NULL;
+static char *fpm_status_ping_response = NULL;
+
+
+int fpm_status_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ if (!wp || !wp->config) {
+ zlog(ZLOG_ERROR, "unable to init fpm_status because conf structure is NULL");
+ return -1;
+ }
+
+ if (wp->config->pm_status_path) {
+ fpm_status_uri = strdup(wp->config->pm_status_path);
+ }
+
+ if (wp->config->ping_path) {
+ if (!wp->config->ping_response) {
+ zlog(ZLOG_ERROR, "[pool %s] ping is set (%s) but ping.response is not set.", wp->config->name, wp->config->ping_path);
+ return -1;
+ }
+ fpm_status_ping_uri = strdup(wp->config->ping_path);
+ fpm_status_ping_response = strdup(wp->config->ping_response);
+ }
+
+ return 0;
+}
+/* }}} */
+
+int fpm_status_handle_request(TSRMLS_D) /* {{{ */
+{
+ struct fpm_scoreboard_s scoreboard, *scoreboard_p;
+ struct fpm_scoreboard_proc_s proc;
+ char *buffer, *time_format, time_buffer[64];
+ time_t now_epoch;
+ int full, encode;
+ char *short_syntax, *short_post;
+ char *full_pre, *full_syntax, *full_post, *full_separator;
+
+ if (!SG(request_info).request_uri) {
+ return 0;
+ }
+
+ /* PING */
+ if (fpm_status_ping_uri && fpm_status_ping_response && !strcmp(fpm_status_ping_uri, SG(request_info).request_uri)) {
+ fpm_request_executing();
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
+ SG(sapi_headers).http_response_code = 200;
+
+ /* handle HEAD */
+ if (SG(request_info).headers_only) {
+ return 1;
+ }
+
+ PUTS(fpm_status_ping_response);
+ return 1;
+ }
+
+ /* STATUS */
+ if (fpm_status_uri && !strcmp(fpm_status_uri, SG(request_info).request_uri)) {
+ fpm_request_executing();
+
+ scoreboard_p = fpm_scoreboard_get();
+ if (!scoreboard_p) {
+ zlog(ZLOG_ERROR, "status: unable to find or access status shared memory");
+ SG(sapi_headers).http_response_code = 500;
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
+ PUTS("Internal error. Please review log file for errors.");
+ return 1;
+ }
+
+ if (!fpm_spinlock(&scoreboard_p->lock, 1)) {
+ zlog(ZLOG_NOTICE, "[pool %s] status: scoreboard already in used.", scoreboard_p->pool);
+ SG(sapi_headers).http_response_code = 503;
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
+ PUTS("Server busy. Please try again later.");
+ return 1;
+ }
+ /* copy the scoreboard not to bother other processes */
+ scoreboard = *scoreboard_p;
+ fpm_unlock(scoreboard_p->lock);
+
+ if (scoreboard.idle < 0 || scoreboard.active < 0) {
+ zlog(ZLOG_ERROR, "[pool %s] invalid status values", scoreboard.pool);
+ SG(sapi_headers).http_response_code = 500;
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
+ PUTS("Internal error. Please review log file for errors.");
+ return 1;
+ }
+
+ /* send common headers */
+ sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
+ sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
+ SG(sapi_headers).http_response_code = 200;
+
+ /* handle HEAD */
+ if (SG(request_info).headers_only) {
+ return 1;
+ }
+
+ /* full status ? */
+ full = (fpm_php_get_string_from_table("_GET", "full" TSRMLS_CC) != NULL);
+ short_syntax = short_post = NULL;
+ full_separator = full_pre = full_syntax = full_post = NULL;
+ encode = 0;
+
+ /* HTML */
+ if (fpm_php_get_string_from_table("_GET", "html" TSRMLS_CC)) {
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/html"), 1, 1 TSRMLS_CC);
+ time_format = "%d/%b/%Y:%H:%M:%S %z";
+ encode = 1;
+
+ short_syntax =
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
+ "<head><title>PHP-FPM Status Page</title></head>\n"
+ "<body>\n"
+ "<table>\n"
+ "<tr><th>pool</th><td>%s</td></tr>\n"
+ "<tr><th>process manager</th><td>%s</td></tr>\n"
+ "<tr><th>start time</th><td>%s</td></tr>\n"
+ "<tr><th>start since</th><td>%lu</td></tr>\n"
+ "<tr><th>accepted conn</th><td>%lu</td></tr>\n"
+#ifdef HAVE_FPM_LQ
+ "<tr><th>listen queue</th><td>%u</td></tr>\n"
+ "<tr><th>max listen queue</th><td>%u</td></tr>\n"
+ "<tr><th>listen queue len</th><td>%d</td></tr>\n"
+#endif
+ "<tr><th>idle processes</th><td>%d</td></tr>\n"
+ "<tr><th>active processes</th><td>%d</td></tr>\n"
+ "<tr><th>total processes</th><td>%d</td></tr>\n"
+ "<tr><th>max active processes</th><td>%d</td></tr>\n"
+ "<tr><th>max children reached</th><td>%u</td></tr>\n"
+ "<tr><th>slow requests</th><td>%lu</td></tr>\n"
+ "</table>\n";
+
+ if (!full) {
+ short_post = "</body></html>";
+ } else {
+ full_pre =
+ "<table border=\"1\">\n"
+ "<tr>"
+ "<th>pid</th>"
+ "<th>state</th>"
+ "<th>start time</th>"
+ "<th>start since</th>"
+ "<th>requests</th>"
+ "<th>request duration</th>"
+ "<th>request method</th>"
+ "<th>request uri</th>"
+ "<th>content length</th>"
+ "<th>user</th>"
+ "<th>script</th>"
+#ifdef HAVE_FPM_LQ
+ "<th>last request cpu</th>"
+#endif
+ "<th>last request memory</th>"
+ "</tr>\n";
+
+ full_syntax =
+ "<tr>"
+ "<td>%d</td>"
+ "<td>%s</td>"
+ "<td>%s</td>"
+ "<td>%lu</td>"
+ "<td>%lu</td>"
+ "<td>%lu</td>"
+ "<td>%s</td>"
+ "<td>%s%s%s</td>"
+ "<td>%zu</td>"
+ "<td>%s</td>"
+ "<td>%s</td>"
+#ifdef HAVE_FPM_LQ
+ "<td>%.2f</td>"
+#endif
+ "<td>%zu</td>"
+ "</tr>\n";
+
+ full_post = "</table></body></html>";
+ }
+
+ /* XML */
+ } else if (fpm_php_get_string_from_table("_GET", "xml" TSRMLS_CC)) {
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/xml"), 1, 1 TSRMLS_CC);
+ time_format = "%s";
+ encode = 1;
+
+ short_syntax =
+ "<?xml version=\"1.0\" ?>\n"
+ "<status>\n"
+ "<pool>%s</pool>\n"
+ "<process-manager>%s</process-manager>\n"
+ "<start-time>%s</start-time>\n"
+ "<start-since>%lu</start-since>\n"
+ "<accepted-conn>%lu</accepted-conn>\n"
+#ifdef HAVE_FPM_LQ
+ "<listen-queue>%u</listen-queue>\n"
+ "<max-listen-queue>%u</max-listen-queue>\n"
+ "<listen-queue-len>%d</listen-queue-len>\n"
+#endif
+ "<idle-processes>%d</idle-processes>\n"
+ "<active-processes>%d</active-processes>\n"
+ "<total-processes>%d</total-processes>\n"
+ "<max-active-processes>%d</max-active-processes>\n"
+ "<max-children-reached>%u</max-children-reached>\n"
+ "<slow-requests>%lu</slow-requests>\n";
+
+ if (!full) {
+ short_post = "</status>";
+ } else {
+ full_pre = "<processes>\n";
+ full_syntax =
+ "<process>"
+ "<pid>%d</pid>"
+ "<state>%s</state>"
+ "<start-time>%s</start-time>"
+ "<start-since>%lu</start-since>"
+ "<requests>%lu</requests>"
+ "<request-duration>%lu</request-duration>"
+ "<request-method>%s</request-method>"
+ "<request-uri>%s%s%s</request-uri>"
+ "<content-length>%zu</content-length>"
+ "<user>%s</user>"
+ "<script>%s</script>"
+#ifdef HAVE_FPM_LQ
+ "<last-request-cpu>%.2f</last-request-cpu>"
+#endif
+ "<last-request-memory>%zu</last-request-memory>"
+ "</process>\n"
+ ;
+ full_post = "</processes>\n</status>";
+ }
+
+ /* JSON */
+ } else if (fpm_php_get_string_from_table("_GET", "json" TSRMLS_CC)) {
+ sapi_add_header_ex(ZEND_STRL("Content-Type: application/json"), 1, 1 TSRMLS_CC);
+ time_format = "%s";
+
+ short_syntax =
+ "{"
+ "\"pool\":\"%s\","
+ "\"process manager\":\"%s\","
+ "\"start time\":%s,"
+ "\"start since\":%lu,"
+ "\"accepted conn\":%lu,"
+#ifdef HAVE_FPM_LQ
+ "\"listen queue\":%u,"
+ "\"max listen queue\":%u,"
+ "\"listen queue len\":%d,"
+#endif
+ "\"idle processes\":%d,"
+ "\"active processes\":%d,"
+ "\"total processes\":%d,"
+ "\"max active processes\":%d,"
+ "\"max children reached\":%u,"
+ "\"slow requests\":%lu";
+
+ if (!full) {
+ short_post = "}";
+ } else {
+ full_separator = ",";
+ full_pre = ", \"processes\":[";
+
+ full_syntax = "{"
+ "\"pid\":%d,"
+ "\"state\":\"%s\","
+ "\"start time\":%s,"
+ "\"start since\":%lu,"
+ "\"requests\":%lu,"
+ "\"request duration\":%lu,"
+ "\"request method\":\"%s\","
+ "\"request uri\":\"%s%s%s\","
+ "\"content length\":%zu,"
+ "\"user\":\"%s\","
+ "\"script\":\"%s\","
+#ifdef HAVE_FPM_LQ
+ "\"last request cpu\":%.2f,"
+#endif
+ "\"last request memory\":%zu"
+ "}";
+
+ full_post = "]}";
+ }
+
+ /* TEXT */
+ } else {
+ sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
+ time_format = "%d/%b/%Y:%H:%M:%S %z";
+
+ short_syntax =
+ "pool: %s\n"
+ "process manager: %s\n"
+ "start time: %s\n"
+ "start since: %lu\n"
+ "accepted conn: %lu\n"
+#ifdef HAVE_FPM_LQ
+ "listen queue: %u\n"
+ "max listen queue: %u\n"
+ "listen queue len: %d\n"
+#endif
+ "idle processes: %d\n"
+ "active processes: %d\n"
+ "total processes: %d\n"
+ "max active processes: %d\n"
+ "max children reached: %u\n"
+ "slow requests: %lu\n";
+
+ if (full) {
+ full_syntax =
+ "\n"
+ "************************\n"
+ "pid: %d\n"
+ "state: %s\n"
+ "start time: %s\n"
+ "start since: %lu\n"
+ "requests: %lu\n"
+ "request duration: %lu\n"
+ "request method: %s\n"
+ "request URI: %s%s%s\n"
+ "content length: %zu\n"
+ "user: %s\n"
+ "script: %s\n"
+#ifdef HAVE_FPM_LQ
+ "last request cpu: %.2f\n"
+#endif
+ "last request memory: %zu\n";
+ }
+ }
+
+ strftime(time_buffer, sizeof(time_buffer) - 1, time_format, localtime(&scoreboard.start_epoch));
+ now_epoch = time(NULL);
+ spprintf(&buffer, 0, short_syntax,
+ scoreboard.pool,
+ PM2STR(scoreboard.pm),
+ time_buffer,
+ now_epoch - scoreboard.start_epoch,
+ scoreboard.requests,
+#ifdef HAVE_FPM_LQ
+ scoreboard.lq,
+ scoreboard.lq_max,
+ scoreboard.lq_len,
+#endif
+ scoreboard.idle,
+ scoreboard.active,
+ scoreboard.idle + scoreboard.active,
+ scoreboard.active_max,
+ scoreboard.max_children_reached,
+ scoreboard.slow_rq);
+
+ PUTS(buffer);
+ efree(buffer);
+
+ if (short_post) {
+ PUTS(short_post);
+ }
+
+ /* no need to test the var 'full' */
+ if (full_syntax) {
+ int i, first;
+ size_t len;
+ char *query_string;
+ struct timeval duration, now;
+#ifdef HAVE_FPM_LQ
+ float cpu;
+#endif
+
+ fpm_clock_get(&now);
+
+ if (full_pre) {
+ PUTS(full_pre);
+ }
+
+ first = 1;
+ for (i=0; i<scoreboard_p->nprocs; i++) {
+ if (!scoreboard_p->procs[i] || !scoreboard_p->procs[i]->used) {
+ continue;
+ }
+ proc = *scoreboard_p->procs[i];
+
+ if (first) {
+ first = 0;
+ } else {
+ if (full_separator) {
+ PUTS(full_separator);
+ }
+ }
+
+ query_string = NULL;
+ len = 0;
+ if (proc.query_string[0] != '\0') {
+ if (!encode) {
+ query_string = proc.query_string;
+ } else {
+ query_string = php_escape_html_entities_ex((unsigned char *)proc.query_string, strlen(proc.query_string), &len, 1, ENT_HTML_IGNORE_ERRORS & ENT_COMPAT, NULL, 1 TSRMLS_CC);
+ }
+ }
+
+#ifdef HAVE_FPM_LQ
+ /* prevent NaN */
+ if (proc.cpu_duration.tv_sec == 0 && proc.cpu_duration.tv_usec == 0) {
+ cpu = 0.;
+ } else {
+ cpu = (proc.last_request_cpu.tms_utime + proc.last_request_cpu.tms_stime + proc.last_request_cpu.tms_cutime + proc.last_request_cpu.tms_cstime) / fpm_scoreboard_get_tick() / (proc.cpu_duration.tv_sec + proc.cpu_duration.tv_usec / 1000000.) * 100.;
+ }
+#endif
+
+ if (proc.request_stage == FPM_REQUEST_ACCEPTING) {
+ duration = proc.duration;
+ } else {
+ timersub(&now, &proc.accepted, &duration);
+ }
+ strftime(time_buffer, sizeof(time_buffer) - 1, time_format, localtime(&proc.start_epoch));
+ spprintf(&buffer, 0, full_syntax,
+ proc.pid,
+ fpm_request_get_stage_name(proc.request_stage),
+ time_buffer,
+ now_epoch - proc.start_epoch,
+ proc.requests,
+ duration.tv_sec * 1000000UL + duration.tv_usec,
+ proc.request_method[0] != '\0' ? proc.request_method : "-",
+ proc.request_uri[0] != '\0' ? proc.request_uri : "-",
+ query_string ? "?" : "",
+ query_string ? query_string : "",
+ proc.content_length,
+ proc.auth_user[0] != '\0' ? proc.auth_user : "-",
+ proc.script_filename[0] != '\0' ? proc.script_filename : "-",
+#ifdef HAVE_FPM_LQ
+ proc.request_stage == FPM_REQUEST_ACCEPTING ? cpu : 0.,
+#endif
+ proc.request_stage == FPM_REQUEST_ACCEPTING ? proc.memory : 0);
+ PUTS(buffer);
+ efree(buffer);
+
+ if (len > 0 && query_string) {
+ efree(query_string);
+ }
+ }
+
+ if (full_post) {
+ PUTS(full_post);
+ }
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_status.h b/sapi/fpm/fpm/fpm_status.h
new file mode 100644
index 0000000..8f3daf9
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_status.h
@@ -0,0 +1,35 @@
+
+ /* $Id$ */
+ /* (c) 2009 Jerome Loyet */
+
+#ifndef FPM_STATUS_H
+#define FPM_STATUS_H 1
+#include "fpm_worker_pool.h"
+#include "fpm_shm.h"
+
+#define FPM_STATUS_BUFFER_SIZE 512
+
+struct fpm_status_s {
+ int pm;
+ int idle;
+ int active;
+ int total;
+ unsigned cur_lq;
+ int max_lq;
+ unsigned long int accepted_conn;
+ unsigned int max_children_reached;
+ struct timeval last_update;
+};
+
+int fpm_status_init_child(struct fpm_worker_pool_s *wp);
+void fpm_status_update_activity(struct fpm_shm_s *shm, int idle, int active, int total, unsigned cur_lq, int max_lq, int clear_last_update);
+void fpm_status_update_accepted_conn(struct fpm_shm_s *shm, unsigned long int accepted_conn);
+void fpm_status_increment_accepted_conn(struct fpm_shm_s *shm);
+void fpm_status_set_pm(struct fpm_shm_s *shm, int pm);
+void fpm_status_update_max_children_reached(struct fpm_shm_s *shm, unsigned int max_children_reached);
+void fpm_status_increment_max_children_reached(struct fpm_shm_s *shm);
+int fpm_status_handle_request(TSRMLS_D);
+
+extern struct fpm_shm_s *fpm_status_shm;
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_stdio.c b/sapi/fpm/fpm/fpm_stdio.c
new file mode 100644
index 0000000..6a587d0
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_stdio.c
@@ -0,0 +1,301 @@
+
+ /* $Id: fpm_stdio.c,v 1.22.2.2 2008/12/13 03:32:24 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "php_syslog.h"
+
+#include "fpm.h"
+#include "fpm_children.h"
+#include "fpm_events.h"
+#include "fpm_sockets.h"
+#include "fpm_stdio.h"
+#include "zlog.h"
+
+static int fd_stdout[2];
+static int fd_stderr[2];
+
+int fpm_stdio_init_main() /* {{{ */
+{
+ int fd = open("/dev/null", O_RDWR);
+
+ if (0 > fd) {
+ zlog(ZLOG_SYSERROR, "failed to init stdio: open(\"/dev/null\")");
+ return -1;
+ }
+
+ if (0 > dup2(fd, STDIN_FILENO) || 0 > dup2(fd, STDOUT_FILENO)) {
+ zlog(ZLOG_SYSERROR, "failed to init stdio: dup2()");
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+/* }}} */
+
+int fpm_stdio_init_final() /* {{{ */
+{
+ if (fpm_global_config.daemonize) {
+ /* prevent duping if logging to syslog */
+ if (fpm_globals.error_log_fd > 0 && fpm_globals.error_log_fd != STDERR_FILENO) {
+
+ /* there might be messages to stderr from other parts of the code, we need to log them all */
+ if (0 > dup2(fpm_globals.error_log_fd, STDERR_FILENO)) {
+ zlog(ZLOG_SYSERROR, "failed to init stdio: dup2()");
+ return -1;
+ }
+ }
+ }
+ zlog_set_launched();
+ return 0;
+}
+/* }}} */
+
+int fpm_stdio_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+#ifdef HAVE_SYSLOG_H
+ if (fpm_globals.error_log_fd == ZLOG_SYSLOG) {
+ closelog(); /* ensure to close syslog not to interrupt with PHP syslog code */
+ } else
+#endif
+ if (fpm_globals.error_log_fd > 0) {
+ close(fpm_globals.error_log_fd);
+ }
+ fpm_globals.error_log_fd = -1;
+ zlog_set_fd(-1);
+
+ if (wp->listening_socket != STDIN_FILENO) {
+ if (0 > dup2(wp->listening_socket, STDIN_FILENO)) {
+ zlog(ZLOG_SYSERROR, "failed to init child stdio: dup2()");
+ return -1;
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ static const int max_buf_size = 1024;
+ int fd = ev->fd;
+ char buf[max_buf_size];
+ struct fpm_child_s *child;
+ int is_stdout;
+ struct fpm_event_s *event;
+ int fifo_in = 1, fifo_out = 1;
+ int is_last_message = 0;
+ int in_buf = 0;
+ int res;
+
+ if (!arg) {
+ return;
+ }
+ child = (struct fpm_child_s *)arg;
+ is_stdout = (fd == child->fd_stdout);
+ if (is_stdout) {
+ event = &child->ev_stdout;
+ } else {
+ event = &child->ev_stderr;
+ }
+
+ while (fifo_in || fifo_out) {
+ if (fifo_in) {
+ res = read(fd, buf + in_buf, max_buf_size - 1 - in_buf);
+ if (res <= 0) { /* no data */
+ fifo_in = 0;
+ if (res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+ /* just no more data ready */
+ } else { /* error or pipe is closed */
+
+ if (res < 0) { /* error */
+ zlog(ZLOG_SYSERROR, "unable to read what child say");
+ }
+
+ fpm_event_del(event);
+ is_last_message = 1;
+
+ if (is_stdout) {
+ close(child->fd_stdout);
+ child->fd_stdout = -1;
+ } else {
+ close(child->fd_stderr);
+ child->fd_stderr = -1;
+ }
+ }
+ } else {
+ in_buf += res;
+ }
+ }
+
+ if (fifo_out) {
+ if (in_buf == 0) {
+ fifo_out = 0;
+ } else {
+ char *nl;
+ int should_print = 0;
+ buf[in_buf] = '\0';
+
+ /* FIXME: there might be binary data */
+
+ /* we should print if no more space in the buffer */
+ if (in_buf == max_buf_size - 1) {
+ should_print = 1;
+ }
+
+ /* we should print if no more data to come */
+ if (!fifo_in) {
+ should_print = 1;
+ }
+
+ nl = strchr(buf, '\n');
+ if (nl || should_print) {
+
+ if (nl) {
+ *nl = '\0';
+ }
+
+ zlog(ZLOG_WARNING, "[pool %s] child %d said into %s: \"%s\"%s", child->wp->config->name,
+ (int) child->pid, is_stdout ? "stdout" : "stderr", buf, is_last_message ? ", pipe is closed" : "");
+
+ if (nl) {
+ int out_buf = 1 + nl - buf;
+ memmove(buf, buf + out_buf, in_buf - out_buf);
+ in_buf -= out_buf;
+ } else {
+ in_buf = 0;
+ }
+ }
+ }
+ }
+ }
+}
+/* }}} */
+
+int fpm_stdio_prepare_pipes(struct fpm_child_s *child) /* {{{ */
+{
+ if (0 == child->wp->config->catch_workers_output) { /* not required */
+ return 0;
+ }
+
+ if (0 > pipe(fd_stdout)) {
+ zlog(ZLOG_SYSERROR, "failed to prepare the stdout pipe");
+ return -1;
+ }
+
+ if (0 > pipe(fd_stderr)) {
+ zlog(ZLOG_SYSERROR, "failed to prepare the stderr pipe");
+ close(fd_stdout[0]);
+ close(fd_stdout[1]);
+ return -1;
+ }
+
+ if (0 > fd_set_blocked(fd_stdout[0], 0) || 0 > fd_set_blocked(fd_stderr[0], 0)) {
+ zlog(ZLOG_SYSERROR, "failed to unblock pipes");
+ close(fd_stdout[0]);
+ close(fd_stdout[1]);
+ close(fd_stderr[0]);
+ close(fd_stderr[1]);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_stdio_parent_use_pipes(struct fpm_child_s *child) /* {{{ */
+{
+ if (0 == child->wp->config->catch_workers_output) { /* not required */
+ return 0;
+ }
+
+ close(fd_stdout[1]);
+ close(fd_stderr[1]);
+
+ child->fd_stdout = fd_stdout[0];
+ child->fd_stderr = fd_stderr[0];
+
+ fpm_event_set(&child->ev_stdout, child->fd_stdout, FPM_EV_READ, fpm_stdio_child_said, child);
+ fpm_event_add(&child->ev_stdout, 0);
+
+ fpm_event_set(&child->ev_stderr, child->fd_stderr, FPM_EV_READ, fpm_stdio_child_said, child);
+ fpm_event_add(&child->ev_stderr, 0);
+ return 0;
+}
+/* }}} */
+
+int fpm_stdio_discard_pipes(struct fpm_child_s *child) /* {{{ */
+{
+ if (0 == child->wp->config->catch_workers_output) { /* not required */
+ return 0;
+ }
+
+ close(fd_stdout[1]);
+ close(fd_stderr[1]);
+
+ close(fd_stdout[0]);
+ close(fd_stderr[0]);
+ return 0;
+}
+/* }}} */
+
+void fpm_stdio_child_use_pipes(struct fpm_child_s *child) /* {{{ */
+{
+ if (child->wp->config->catch_workers_output) {
+ dup2(fd_stdout[1], STDOUT_FILENO);
+ dup2(fd_stderr[1], STDERR_FILENO);
+ close(fd_stdout[0]); close(fd_stdout[1]);
+ close(fd_stderr[0]); close(fd_stderr[1]);
+ } else {
+ /* stdout of parent is always /dev/null */
+ dup2(STDOUT_FILENO, STDERR_FILENO);
+ }
+}
+/* }}} */
+
+int fpm_stdio_open_error_log(int reopen) /* {{{ */
+{
+ int fd;
+
+#ifdef HAVE_SYSLOG_H
+ if (!strcasecmp(fpm_global_config.error_log, "syslog")) {
+ openlog(fpm_global_config.syslog_ident, LOG_PID | LOG_CONS, fpm_global_config.syslog_facility);
+ fpm_globals.error_log_fd = ZLOG_SYSLOG;
+ if (fpm_global_config.daemonize) {
+ zlog_set_fd(fpm_globals.error_log_fd);
+ }
+ return 0;
+ }
+#endif
+
+ fd = open(fpm_global_config.error_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
+ if (0 > fd) {
+ zlog(ZLOG_SYSERROR, "failed to open error_log (%s)", fpm_global_config.error_log);
+ return -1;
+ }
+
+ if (reopen) {
+ if (fpm_global_config.daemonize) {
+ dup2(fd, STDERR_FILENO);
+ }
+
+ dup2(fd, fpm_globals.error_log_fd);
+ close(fd);
+ fd = fpm_globals.error_log_fd; /* for FD_CLOSEXEC to work */
+ } else {
+ fpm_globals.error_log_fd = fd;
+ if (fpm_global_config.daemonize) {
+ zlog_set_fd(fpm_globals.error_log_fd);
+ }
+ }
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_stdio.h b/sapi/fpm/fpm/fpm_stdio.h
new file mode 100644
index 0000000..d3d61e4
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_stdio.h
@@ -0,0 +1,20 @@
+
+ /* $Id: fpm_stdio.h,v 1.9 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_STDIO_H
+#define FPM_STDIO_H 1
+
+#include "fpm_worker_pool.h"
+
+int fpm_stdio_init_main();
+int fpm_stdio_init_final();
+int fpm_stdio_init_child(struct fpm_worker_pool_s *wp);
+int fpm_stdio_prepare_pipes(struct fpm_child_s *child);
+void fpm_stdio_child_use_pipes(struct fpm_child_s *child);
+int fpm_stdio_parent_use_pipes(struct fpm_child_s *child);
+int fpm_stdio_discard_pipes(struct fpm_child_s *child);
+int fpm_stdio_open_error_log(int reopen);
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_str.h b/sapi/fpm/fpm/fpm_str.h
new file mode 100644
index 0000000..65db545
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_str.h
@@ -0,0 +1,29 @@
+
+ /* $Id: fpm_str.h,v 1.3 2008/05/24 17:38:47 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_STR_H
+#define FPM_STR_H 1
+
+static inline char *str_purify_filename(char *dst, char *src, size_t size) /* {{{ */
+{
+ char *d, *end;
+
+ d = dst;
+ end = dst + size - 1;
+
+ for (; d < end && *src; ++d, ++src) {
+ if (* (unsigned char *) src < ' ' || * (unsigned char *) src > '\x7f') {
+ *d = '.';
+ } else {
+ *d = *src;
+ }
+ }
+
+ *d = '\0';
+
+ return d;
+}
+/* }}} */
+
+#endif
diff --git a/sapi/fpm/fpm/fpm_trace.c b/sapi/fpm/fpm/fpm_trace.c
new file mode 100644
index 0000000..366af5a
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_trace.c
@@ -0,0 +1,41 @@
+
+ /* $Id: fpm_trace.c,v 1.1 2008/07/20 20:59:00 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <sys/types.h>
+
+#include "fpm_trace.h"
+
+int fpm_trace_get_strz(char *buf, size_t sz, long addr) /* {{{ */
+{
+ int i;
+ long l;
+ char *lc = (char *) &l;
+
+ if (0 > fpm_trace_get_long(addr, &l)) {
+ return -1;
+ }
+
+ i = l % SIZEOF_LONG;
+ l -= i;
+ for (addr = l; ; addr += SIZEOF_LONG) {
+ if (0 > fpm_trace_get_long(addr, &l)) {
+ return -1;
+ }
+ for ( ; i < SIZEOF_LONG; i++) {
+ --sz;
+ if (sz && lc[i]) {
+ *buf++ = lc[i];
+ continue;
+ }
+ *buf = '\0';
+ return 0;
+ }
+ i = 0;
+ }
+}
+/* }}} */
+
+
diff --git a/sapi/fpm/fpm/fpm_trace.h b/sapi/fpm/fpm/fpm_trace.h
new file mode 100644
index 0000000..b421172
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_trace.h
@@ -0,0 +1,17 @@
+
+ /* $Id: fpm_trace.h,v 1.3 2008/07/20 22:43:39 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_TRACE_H
+#define FPM_TRACE_H 1
+
+#include <unistd.h>
+
+int fpm_trace_signal(pid_t pid);
+int fpm_trace_ready(pid_t pid);
+int fpm_trace_close(pid_t pid);
+int fpm_trace_get_long(long addr, long *data);
+int fpm_trace_get_strz(char *buf, size_t sz, long addr);
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_trace_mach.c b/sapi/fpm/fpm/fpm_trace_mach.c
new file mode 100644
index 0000000..3b85e6a
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_trace_mach.c
@@ -0,0 +1,99 @@
+
+ /* $Id: fpm_trace_mach.c,v 1.4 2008/08/26 15:09:15 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+
+#include <unistd.h>
+
+#include "fpm_trace.h"
+#include "fpm_process_ctl.h"
+#include "fpm_unix.h"
+#include "zlog.h"
+
+
+static mach_port_name_t target;
+static vm_offset_t target_page_base;
+static vm_offset_t local_page;
+static mach_msg_type_number_t local_size;
+
+static void fpm_mach_vm_deallocate() /* {{{ */
+{
+ if (local_page) {
+ mach_vm_deallocate(mach_task_self(), local_page, local_size);
+ target_page_base = 0;
+ local_page = 0;
+ local_size = 0;
+ }
+}
+/* }}} */
+
+static int fpm_mach_vm_read_page(vm_offset_t page) /* {{{ */
+{
+ kern_return_t kr;
+
+ kr = mach_vm_read(target, page, fpm_pagesize, &local_page, &local_size);
+ if (kr != KERN_SUCCESS) {
+ zlog(ZLOG_ERROR, "failed to read vm page: mach_vm_read(): %s (%d)", mach_error_string(kr), kr);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_signal(pid_t pid) /* {{{ */
+{
+ if (0 > fpm_pctl_kill(pid, FPM_PCTL_STOP)) {
+ zlog(ZLOG_SYSERROR, "failed to send SIGSTOP to %d", pid);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_ready(pid_t pid) /* {{{ */
+{
+ kern_return_t kr;
+
+ kr = task_for_pid(mach_task_self(), pid, &target);
+ if (kr != KERN_SUCCESS) {
+ char *msg = "";
+
+ if (kr == KERN_FAILURE) {
+ msg = " It seems that master process does not have enough privileges to trace processes.";
+ }
+ zlog(ZLOG_ERROR, "task_for_pid() failed: %s (%d)%s", mach_error_string(kr), kr, msg);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_close(pid_t pid) /* {{{ */
+{
+ fpm_mach_vm_deallocate();
+ target = 0;
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_get_long(long addr, long *data) /* {{{ */
+{
+ size_t offset = ((uintptr_t) (addr) % fpm_pagesize);
+ vm_offset_t base = (uintptr_t) (addr) - offset;
+
+ if (base != target_page_base) {
+ fpm_mach_vm_deallocate();
+ if (0 > fpm_mach_vm_read_page(base)) {
+ return -1;
+ }
+ }
+ *data = * (long *) (local_page + offset);
+ return 0;
+}
+/* }}} */
+
+
diff --git a/sapi/fpm/fpm/fpm_trace_pread.c b/sapi/fpm/fpm/fpm_trace_pread.c
new file mode 100644
index 0000000..6a61557
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_trace_pread.c
@@ -0,0 +1,67 @@
+
+ /* $Id: fpm_trace_pread.c,v 1.7 2008/08/26 15:09:15 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#define _GNU_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include "fpm_config.h"
+
+#include <unistd.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# include <stdint.h>
+#endif
+
+#include "fpm_trace.h"
+#include "fpm_process_ctl.h"
+#include "zlog.h"
+
+static int mem_file = -1;
+
+int fpm_trace_signal(pid_t pid) /* {{{ */
+{
+ if (0 > fpm_pctl_kill(pid, FPM_PCTL_STOP)) {
+ zlog(ZLOG_SYSERROR, "failed to send SIGSTOP to %d", pid);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_ready(pid_t pid) /* {{{ */
+{
+ char buf[128];
+
+ sprintf(buf, "/proc/%d/" PROC_MEM_FILE, (int) pid);
+ mem_file = open(buf, O_RDONLY);
+ if (0 > mem_file) {
+ zlog(ZLOG_SYSERROR, "failed to open %s", buf);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_close(pid_t pid) /* {{{ */
+{
+ close(mem_file);
+ mem_file = -1;
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_get_long(long addr, long *data) /* {{{ */
+{
+ if (sizeof(*data) != pread(mem_file, (void *) data, sizeof(*data), (uintptr_t) addr)) {
+ zlog(ZLOG_SYSERROR, "pread() failed");
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_trace_ptrace.c b/sapi/fpm/fpm/fpm_trace_ptrace.c
new file mode 100644
index 0000000..838c618
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_trace_ptrace.c
@@ -0,0 +1,82 @@
+
+ /* $Id: fpm_trace_ptrace.c,v 1.7 2008/09/18 23:34:11 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <sys/wait.h>
+#include <sys/ptrace.h>
+#include <unistd.h>
+#include <errno.h>
+
+#if defined(PT_ATTACH) && !defined(PTRACE_ATTACH)
+#define PTRACE_ATTACH PT_ATTACH
+#endif
+
+#if defined(PT_DETACH) && !defined(PTRACE_DETACH)
+#define PTRACE_DETACH PT_DETACH
+#endif
+
+#if defined(PT_READ_D) && !defined(PTRACE_PEEKDATA)
+#define PTRACE_PEEKDATA PT_READ_D
+#endif
+
+#include "fpm_trace.h"
+#include "zlog.h"
+
+static pid_t traced_pid;
+
+int fpm_trace_signal(pid_t pid) /* {{{ */
+{
+ if (0 > ptrace(PTRACE_ATTACH, pid, 0, 0)) {
+ zlog(ZLOG_SYSERROR, "failed to ptrace(ATTACH) child %d", pid);
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_ready(pid_t pid) /* {{{ */
+{
+ traced_pid = pid;
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_close(pid_t pid) /* {{{ */
+{
+ if (0 > ptrace(PTRACE_DETACH, pid, (void *) 1, 0)) {
+ zlog(ZLOG_SYSERROR, "failed to ptrace(DETACH) child %d", pid);
+ return -1;
+ }
+ traced_pid = 0;
+ return 0;
+}
+/* }}} */
+
+int fpm_trace_get_long(long addr, long *data) /* {{{ */
+{
+#ifdef PT_IO
+ struct ptrace_io_desc ptio = {
+ .piod_op = PIOD_READ_D,
+ .piod_offs = (void *) addr,
+ .piod_addr = (void *) data,
+ .piod_len = sizeof(long)
+ };
+
+ if (0 > ptrace(PT_IO, traced_pid, (void *) &ptio, 0)) {
+ zlog(ZLOG_SYSERROR, "failed to ptrace(PT_IO) pid %d", traced_pid);
+ return -1;
+ }
+#else
+ errno = 0;
+ *data = ptrace(PTRACE_PEEKDATA, traced_pid, (void *) addr, 0);
+ if (errno) {
+ zlog(ZLOG_SYSERROR, "failed to ptrace(PEEKDATA) pid %d", traced_pid);
+ return -1;
+ }
+#endif
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c
new file mode 100644
index 0000000..48249e8
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_unix.c
@@ -0,0 +1,368 @@
+
+ /* $Id: fpm_unix.c,v 1.25.2.1 2008/12/13 03:18:23 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef HAVE_PRCTL
+#include <sys/prctl.h>
+#endif
+
+#include "fpm.h"
+#include "fpm_conf.h"
+#include "fpm_cleanup.h"
+#include "fpm_clock.h"
+#include "fpm_stdio.h"
+#include "fpm_unix.h"
+#include "fpm_signals.h"
+#include "zlog.h"
+
+size_t fpm_pagesize;
+
+int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct fpm_worker_pool_config_s *c = wp->config;
+
+ /* uninitialized */
+ wp->socket_uid = -1;
+ wp->socket_gid = -1;
+ wp->socket_mode = 0666;
+
+ if (!c) {
+ return 0;
+ }
+
+ if (c->listen_owner && *c->listen_owner) {
+ struct passwd *pwd;
+
+ pwd = getpwnam(c->listen_owner);
+ if (!pwd) {
+ zlog(ZLOG_SYSERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, c->listen_owner);
+ return -1;
+ }
+
+ wp->socket_uid = pwd->pw_uid;
+ wp->socket_gid = pwd->pw_gid;
+ }
+
+ if (c->listen_group && *c->listen_group) {
+ struct group *grp;
+
+ grp = getgrnam(c->listen_group);
+ if (!grp) {
+ zlog(ZLOG_SYSERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, c->listen_group);
+ return -1;
+ }
+ wp->socket_gid = grp->gr_gid;
+ }
+
+ if (c->listen_mode && *c->listen_mode) {
+ wp->socket_mode = strtoul(c->listen_mode, 0, 8);
+ }
+ return 0;
+}
+/* }}} */
+
+static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ struct passwd *pwd;
+ int is_root = !geteuid();
+
+ if (is_root) {
+ if (wp->config->user && *wp->config->user) {
+ if (strlen(wp->config->user) == strspn(wp->config->user, "0123456789")) {
+ wp->set_uid = strtoul(wp->config->user, 0, 10);
+ } else {
+ struct passwd *pwd;
+
+ pwd = getpwnam(wp->config->user);
+ if (!pwd) {
+ zlog(ZLOG_ERROR, "[pool %s] cannot get uid for user '%s'", wp->config->name, wp->config->user);
+ return -1;
+ }
+
+ wp->set_uid = pwd->pw_uid;
+ wp->set_gid = pwd->pw_gid;
+
+ wp->user = strdup(pwd->pw_name);
+ wp->home = strdup(pwd->pw_dir);
+ }
+ }
+
+ if (wp->config->group && *wp->config->group) {
+ if (strlen(wp->config->group) == strspn(wp->config->group, "0123456789")) {
+ wp->set_gid = strtoul(wp->config->group, 0, 10);
+ } else {
+ struct group *grp;
+
+ grp = getgrnam(wp->config->group);
+ if (!grp) {
+ zlog(ZLOG_ERROR, "[pool %s] cannot get gid for group '%s'", wp->config->name, wp->config->group);
+ return -1;
+ }
+ wp->set_gid = grp->gr_gid;
+ }
+ }
+
+ if (!fpm_globals.run_as_root) {
+ if (wp->set_uid == 0 || wp->set_gid == 0) {
+ zlog(ZLOG_ERROR, "[pool %s] please specify user and group other than root", wp->config->name);
+ return -1;
+ }
+ }
+ } else { /* not root */
+ if (wp->config->user && *wp->config->user) {
+ zlog(ZLOG_NOTICE, "[pool %s] 'user' directive is ignored when FPM is not running as root", wp->config->name);
+ }
+ if (wp->config->group && *wp->config->group) {
+ zlog(ZLOG_NOTICE, "[pool %s] 'group' directive is ignored when FPM is not running as root", wp->config->name);
+ }
+ if (wp->config->chroot && *wp->config->chroot) {
+ zlog(ZLOG_NOTICE, "[pool %s] 'chroot' directive is ignored when FPM is not running as root", wp->config->name);
+ }
+ if (wp->config->process_priority != 64) {
+ zlog(ZLOG_NOTICE, "[pool %s] 'process.priority' directive is ignored when FPM is not running as root", wp->config->name);
+ }
+
+ /* set up HOME and USER anyway */
+ pwd = getpwuid(getuid());
+ if (pwd) {
+ wp->user = strdup(pwd->pw_name);
+ wp->home = strdup(pwd->pw_dir);
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_unix_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
+{
+ int is_root = !geteuid();
+ int made_chroot = 0;
+
+ if (wp->config->rlimit_files) {
+ struct rlimit r;
+
+ r.rlim_max = r.rlim_cur = (rlim_t) wp->config->rlimit_files;
+
+ if (0 > setrlimit(RLIMIT_NOFILE, &r)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to set rlimit_files for this pool. Please check your system limits or decrease rlimit_files. setrlimit(RLIMIT_NOFILE, %d)", wp->config->name, wp->config->rlimit_files);
+ }
+ }
+
+ if (wp->config->rlimit_core) {
+ struct rlimit r;
+
+ r.rlim_max = r.rlim_cur = wp->config->rlimit_core == -1 ? (rlim_t) RLIM_INFINITY : (rlim_t) wp->config->rlimit_core;
+
+ if (0 > setrlimit(RLIMIT_CORE, &r)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_core. setrlimit(RLIMIT_CORE, %d)", wp->config->name, wp->config->rlimit_core);
+ }
+ }
+
+ if (is_root && wp->config->chroot && *wp->config->chroot) {
+ if (0 > chroot(wp->config->chroot)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to chroot(%s)", wp->config->name, wp->config->chroot);
+ return -1;
+ }
+ made_chroot = 1;
+ }
+
+ if (wp->config->chdir && *wp->config->chdir) {
+ if (0 > chdir(wp->config->chdir)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to chdir(%s)", wp->config->name, wp->config->chdir);
+ return -1;
+ }
+ } else if (made_chroot) {
+ chdir("/");
+ }
+
+ if (is_root) {
+
+ if (wp->config->process_priority != 64) {
+ if (setpriority(PRIO_PROCESS, 0, wp->config->process_priority) < 0) {
+ zlog(ZLOG_SYSERROR, "[pool %s] Unable to set priority for this new process", wp->config->name);
+ return -1;
+ }
+ }
+
+ if (wp->set_gid) {
+ if (0 > setgid(wp->set_gid)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to setgid(%d)", wp->config->name, wp->set_gid);
+ return -1;
+ }
+ }
+ if (wp->set_uid) {
+ if (0 > initgroups(wp->config->user, wp->set_gid)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to initgroups(%s, %d)", wp->config->name, wp->config->user, wp->set_gid);
+ return -1;
+ }
+ if (0 > setuid(wp->set_uid)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to setuid(%d)", wp->config->name, wp->set_uid);
+ return -1;
+ }
+ }
+ }
+
+#ifdef HAVE_PRCTL
+ if (0 > prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)) {
+ zlog(ZLOG_SYSERROR, "[pool %s] failed to prctl(PR_SET_DUMPABLE)", wp->config->name);
+ }
+#endif
+
+ if (0 > fpm_clock_init()) {
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_unix_init_main() /* {{{ */
+{
+ struct fpm_worker_pool_s *wp;
+ int is_root = !geteuid();
+
+ if (fpm_global_config.rlimit_files) {
+ struct rlimit r;
+
+ r.rlim_max = r.rlim_cur = (rlim_t) fpm_global_config.rlimit_files;
+
+ if (0 > setrlimit(RLIMIT_NOFILE, &r)) {
+ zlog(ZLOG_SYSERROR, "failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_files. setrlimit(RLIMIT_NOFILE, %d)", fpm_global_config.rlimit_files);
+ return -1;
+ }
+ }
+
+ if (fpm_global_config.rlimit_core) {
+ struct rlimit r;
+
+ r.rlim_max = r.rlim_cur = fpm_global_config.rlimit_core == -1 ? (rlim_t) RLIM_INFINITY : (rlim_t) fpm_global_config.rlimit_core;
+
+ if (0 > setrlimit(RLIMIT_CORE, &r)) {
+ zlog(ZLOG_SYSERROR, "failed to set rlimit_core for this pool. Please check your system limits or decrease rlimit_core. setrlimit(RLIMIT_CORE, %d)", fpm_global_config.rlimit_core);
+ return -1;
+ }
+ }
+
+ fpm_pagesize = getpagesize();
+ if (fpm_global_config.daemonize) {
+ /*
+ * If daemonize, the calling process will die soon
+ * and the master process continues to initialize itself.
+ *
+ * The parent process has then to wait for the master
+ * process to initialize to return a consistent exit
+ * value. For this pupose, the master process will
+ * send \"1\" into the pipe if everything went well
+ * and \"0\" otherwise.
+ */
+
+
+ struct timeval tv;
+ fd_set rfds;
+ int ret;
+
+ if (pipe(fpm_globals.send_config_pipe) == -1) {
+ zlog(ZLOG_SYSERROR, "failed to create pipe");
+ return -1;
+ }
+
+ /* then fork */
+ pid_t pid = fork();
+ switch (pid) {
+
+ case -1 : /* error */
+ zlog(ZLOG_SYSERROR, "failed to daemonize");
+ return -1;
+
+ case 0 : /* children */
+ close(fpm_globals.send_config_pipe[0]); /* close the read side of the pipe */
+ break;
+
+ default : /* parent */
+ close(fpm_globals.send_config_pipe[1]); /* close the write side of the pipe */
+
+ /*
+ * wait for 10s before exiting with error
+ * the child is supposed to send 1 or 0 into the pipe to tell the parent
+ * how it goes for it
+ */
+ FD_ZERO(&rfds);
+ FD_SET(fpm_globals.send_config_pipe[0], &rfds);
+
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+
+ zlog(ZLOG_DEBUG, "The calling process is waiting for the master process to ping via fd=%d", fpm_globals.send_config_pipe[0]);
+ ret = select(fpm_globals.send_config_pipe[0] + 1, &rfds, NULL, NULL, &tv);
+ if (ret == -1) {
+ zlog(ZLOG_SYSERROR, "failed to select");
+ exit(FPM_EXIT_SOFTWARE);
+ }
+ if (ret) { /* data available */
+ int readval;
+ ret = read(fpm_globals.send_config_pipe[0], &readval, sizeof(readval));
+ if (ret == -1) {
+ zlog(ZLOG_SYSERROR, "failed to read from pipe");
+ exit(FPM_EXIT_SOFTWARE);
+ }
+
+ if (ret == 0) {
+ zlog(ZLOG_ERROR, "no data have been read from pipe");
+ exit(FPM_EXIT_SOFTWARE);
+ } else {
+ if (readval == 1) {
+ zlog(ZLOG_DEBUG, "I received a valid acknoledge from the master process, I can exit without error");
+ fpm_cleanups_run(FPM_CLEANUP_PARENT_EXIT);
+ exit(FPM_EXIT_OK);
+ } else {
+ zlog(ZLOG_DEBUG, "The master process returned an error !");
+ exit(FPM_EXIT_SOFTWARE);
+ }
+ }
+ } else { /* no date sent ! */
+ zlog(ZLOG_ERROR, "the master process didn't send back its status (via the pipe to the calling process)");
+ exit(FPM_EXIT_SOFTWARE);
+ }
+ exit(FPM_EXIT_SOFTWARE);
+ }
+ }
+
+ /* continue as a child */
+ setsid();
+ if (0 > fpm_clock_init()) {
+ return -1;
+ }
+
+ if (fpm_global_config.process_priority != 64) {
+ if (is_root) {
+ if (setpriority(PRIO_PROCESS, 0, fpm_global_config.process_priority) < 0) {
+ zlog(ZLOG_SYSERROR, "Unable to set priority for the master process");
+ return -1;
+ }
+ } else {
+ zlog(ZLOG_NOTICE, "'process.priority' directive is ignored when FPM is not running as root");
+ }
+ }
+
+ fpm_globals.parent_pid = getpid();
+ for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
+ if (0 > fpm_unix_conf_wp(wp)) {
+ return -1;
+ }
+ }
+
+ zlog_set_level(fpm_globals.log_level);
+ return 0;
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/fpm_unix.h b/sapi/fpm/fpm/fpm_unix.h
new file mode 100644
index 0000000..3451db1
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_unix.h
@@ -0,0 +1,17 @@
+
+ /* $Id: fpm_unix.h,v 1.8 2008/05/25 13:21:13 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_UNIX_H
+#define FPM_UNIX_H 1
+
+#include "fpm_worker_pool.h"
+
+int fpm_unix_resolve_socket_premissions(struct fpm_worker_pool_s *wp);
+int fpm_unix_init_child(struct fpm_worker_pool_s *wp);
+int fpm_unix_init_main();
+
+extern size_t fpm_pagesize;
+
+#endif
+
diff --git a/sapi/fpm/fpm/fpm_worker_pool.c b/sapi/fpm/fpm/fpm_worker_pool.c
new file mode 100644
index 0000000..123f989
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_worker_pool.c
@@ -0,0 +1,65 @@
+
+ /* $Id: fpm_worker_pool.c,v 1.15.2.1 2008/12/13 03:21:18 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "fpm.h"
+#include "fpm_worker_pool.h"
+#include "fpm_cleanup.h"
+#include "fpm_children.h"
+#include "fpm_shm.h"
+#include "fpm_scoreboard.h"
+#include "fpm_conf.h"
+
+struct fpm_worker_pool_s *fpm_worker_all_pools;
+
+static void fpm_worker_pool_cleanup(int which, void *arg) /* {{{ */
+{
+ struct fpm_worker_pool_s *wp, *wp_next;
+
+ for (wp = fpm_worker_all_pools; wp; wp = wp_next) {
+ wp_next = wp->next;
+ fpm_worker_pool_config_free(wp->config);
+ fpm_children_free(wp->children);
+ if ((which & FPM_CLEANUP_CHILD) == 0 && fpm_globals.parent_pid == getpid()) {
+ fpm_scoreboard_free(wp->scoreboard);
+ }
+ free(wp->config);
+ free(wp->user);
+ free(wp->home);
+ free(wp);
+ }
+ fpm_worker_all_pools = NULL;
+}
+/* }}} */
+
+struct fpm_worker_pool_s *fpm_worker_pool_alloc() /* {{{ */
+{
+ struct fpm_worker_pool_s *ret;
+
+ ret = malloc(sizeof(struct fpm_worker_pool_s));
+ if (!ret) {
+ return 0;
+ }
+
+ memset(ret, 0, sizeof(struct fpm_worker_pool_s));
+
+ ret->idle_spawn_rate = 1;
+ ret->log_fd = -1;
+ return ret;
+}
+/* }}} */
+
+int fpm_worker_pool_init_main() /* {{{ */
+{
+ if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_worker_pool_cleanup, 0)) {
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_worker_pool.h b/sapi/fpm/fpm/fpm_worker_pool.h
new file mode 100644
index 0000000..6688e6d
--- /dev/null
+++ b/sapi/fpm/fpm/fpm_worker_pool.h
@@ -0,0 +1,53 @@
+
+ /* $Id: fpm_worker_pool.h,v 1.13 2008/08/26 15:09:15 anight Exp $ */
+ /* (c) 2007,2008 Andrei Nigmatulin */
+
+#ifndef FPM_WORKER_POOL_H
+#define FPM_WORKER_POOL_H 1
+
+#include "fpm_conf.h"
+#include "fpm_shm.h"
+
+struct fpm_worker_pool_s;
+struct fpm_child_s;
+struct fpm_child_stat_s;
+struct fpm_shm_s;
+
+enum fpm_address_domain {
+ FPM_AF_UNIX = 1,
+ FPM_AF_INET = 2
+};
+
+struct fpm_worker_pool_s {
+ struct fpm_worker_pool_s *next;
+ struct fpm_worker_pool_config_s *config;
+ char *user, *home; /* for setting env USER and HOME */
+ enum fpm_address_domain listen_address_domain;
+ int listening_socket;
+ int set_uid, set_gid; /* config uid and gid */
+ int socket_uid, socket_gid, socket_mode;
+
+ /* runtime */
+ struct fpm_child_s *children;
+ int running_children;
+ int idle_spawn_rate;
+ int warn_max_children;
+#if 0
+ int warn_lq;
+#endif
+ struct fpm_scoreboard_s *scoreboard;
+ int log_fd;
+ char **limit_extensions;
+
+ /* for ondemand PM */
+ struct fpm_event_s *ondemand_event;
+ int socket_event_set;
+};
+
+struct fpm_worker_pool_s *fpm_worker_pool_alloc();
+int fpm_worker_pool_init_main();
+
+extern struct fpm_worker_pool_s *fpm_worker_all_pools;
+
+#endif
+
diff --git a/sapi/fpm/fpm/zlog.c b/sapi/fpm/fpm/zlog.c
new file mode 100644
index 0000000..80db9d8
--- /dev/null
+++ b/sapi/fpm/fpm/zlog.c
@@ -0,0 +1,199 @@
+
+ /* $Id: zlog.c,v 1.7 2008/05/22 21:08:32 anight Exp $ */
+ /* (c) 2004-2007 Andrei Nigmatulin */
+
+#include "fpm_config.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include "php_syslog.h"
+
+#include "zlog.h"
+#include "fpm.h"
+
+#define MAX_LINE_LENGTH 1024
+
+static int zlog_fd = -1;
+static int zlog_level = ZLOG_NOTICE;
+static int launched = 0;
+static void (*external_logger)(int, char *, size_t) = NULL;
+
+static const char *level_names[] = {
+ [ZLOG_DEBUG] = "DEBUG",
+ [ZLOG_NOTICE] = "NOTICE",
+ [ZLOG_WARNING] = "WARNING",
+ [ZLOG_ERROR] = "ERROR",
+ [ZLOG_ALERT] = "ALERT",
+};
+
+#ifdef HAVE_SYSLOG_H
+const int syslog_priorities[] = {
+ [ZLOG_DEBUG] = LOG_DEBUG,
+ [ZLOG_NOTICE] = LOG_NOTICE,
+ [ZLOG_WARNING] = LOG_WARNING,
+ [ZLOG_ERROR] = LOG_ERR,
+ [ZLOG_ALERT] = LOG_ALERT,
+};
+#endif
+
+void zlog_set_external_logger(void (*logger)(int, char *, size_t)) /* {{{ */
+{
+ external_logger = logger;
+}
+/* }}} */
+
+const char *zlog_get_level_name(int log_level) /* {{{ */
+{
+ if (log_level < 0) {
+ log_level = zlog_level;
+ } else if (log_level < ZLOG_DEBUG || log_level > ZLOG_ALERT) {
+ return "unknown value";
+ }
+
+ return level_names[log_level];
+}
+/* }}} */
+
+void zlog_set_launched(void) {
+ launched = 1;
+}
+
+size_t zlog_print_time(struct timeval *tv, char *timebuf, size_t timebuf_len) /* {{{ */
+{
+ struct tm t;
+ size_t len;
+
+ len = strftime(timebuf, timebuf_len, "[%d-%b-%Y %H:%M:%S", localtime_r((const time_t *) &tv->tv_sec, &t));
+ if (zlog_level == ZLOG_DEBUG) {
+ len += snprintf(timebuf + len, timebuf_len - len, ".%06d", (int) tv->tv_usec);
+ }
+ len += snprintf(timebuf + len, timebuf_len - len, "] ");
+ return len;
+}
+/* }}} */
+
+int zlog_set_fd(int new_fd) /* {{{ */
+{
+ int old_fd = zlog_fd;
+
+ zlog_fd = new_fd;
+ return old_fd;
+}
+/* }}} */
+
+int zlog_set_level(int new_value) /* {{{ */
+{
+ int old_value = zlog_level;
+
+ if (new_value < ZLOG_DEBUG || new_value > ZLOG_ALERT) return old_value;
+
+ zlog_level = new_value;
+ return old_value;
+}
+/* }}} */
+
+void zlog_ex(const char *function, int line, int flags, const char *fmt, ...) /* {{{ */
+{
+ struct timeval tv;
+ char buf[MAX_LINE_LENGTH];
+ const size_t buf_size = MAX_LINE_LENGTH;
+ va_list args;
+ size_t len = 0;
+ int truncated = 0;
+ int saved_errno;
+
+ if (external_logger) {
+ va_start(args, fmt);
+ len = vsnprintf(buf, buf_size, fmt, args);
+ va_end(args);
+ if (len >= buf_size) {
+ memcpy(buf + buf_size - sizeof("..."), "...", sizeof("...") - 1);
+ len = buf_size - 1;
+ }
+ external_logger(flags & ZLOG_LEVEL_MASK, buf, len);
+ len = 0;
+ memset(buf, '\0', buf_size);
+ }
+
+ if ((flags & ZLOG_LEVEL_MASK) < zlog_level) {
+ return;
+ }
+
+ saved_errno = errno;
+#ifdef HAVE_SYSLOG_H
+ if (zlog_fd == ZLOG_SYSLOG /* && !fpm_globals.is_child */) {
+ len = 0;
+ if (zlog_level == ZLOG_DEBUG) {
+ len += snprintf(buf, buf_size, "[%s] %s(), line %d: ", level_names[flags & ZLOG_LEVEL_MASK], function, line);
+ } else {
+ len += snprintf(buf, buf_size, "[%s] ", level_names[flags & ZLOG_LEVEL_MASK]);
+ }
+ } else
+#endif
+ {
+ if (!fpm_globals.is_child) {
+ gettimeofday(&tv, 0);
+ len = zlog_print_time(&tv, buf, buf_size);
+ }
+ if (zlog_level == ZLOG_DEBUG) {
+ if (!fpm_globals.is_child) {
+ len += snprintf(buf + len, buf_size - len, "%s: pid %d, %s(), line %d: ", level_names[flags & ZLOG_LEVEL_MASK], getpid(), function, line);
+ } else {
+ len += snprintf(buf + len, buf_size - len, "%s: %s(), line %d: ", level_names[flags & ZLOG_LEVEL_MASK], function, line);
+ }
+ } else {
+ len += snprintf(buf + len, buf_size - len, "%s: ", level_names[flags & ZLOG_LEVEL_MASK]);
+ }
+ }
+
+ if (len > buf_size - 1) {
+ truncated = 1;
+ }
+
+ if (!truncated) {
+ va_start(args, fmt);
+ len += vsnprintf(buf + len, buf_size - len, fmt, args);
+ va_end(args);
+ if (len >= buf_size) {
+ truncated = 1;
+ }
+ }
+
+ if (!truncated) {
+ if (flags & ZLOG_HAVE_ERRNO) {
+ len += snprintf(buf + len, buf_size - len, ": %s (%d)", strerror(saved_errno), saved_errno);
+ if (len >= buf_size) {
+ truncated = 1;
+ }
+ }
+ }
+
+ if (truncated) {
+ memcpy(buf + buf_size - sizeof("..."), "...", sizeof("...") - 1);
+ len = buf_size - 1;
+ }
+
+#ifdef HAVE_SYSLOG_H
+ if (zlog_fd == ZLOG_SYSLOG) {
+ buf[len] = '\0';
+ php_syslog(syslog_priorities[zlog_level], "%s", buf);
+ buf[len++] = '\n';
+ } else
+#endif
+ {
+ buf[len++] = '\n';
+ write(zlog_fd > -1 ? zlog_fd : STDERR_FILENO, buf, len);
+ }
+
+ if (zlog_fd != STDERR_FILENO && zlog_fd != -1 && !launched && (flags & ZLOG_LEVEL_MASK) >= ZLOG_NOTICE) {
+ write(STDERR_FILENO, buf, len);
+ }
+}
+/* }}} */
+
diff --git a/sapi/fpm/fpm/zlog.h b/sapi/fpm/fpm/zlog.h
new file mode 100644
index 0000000..1945922
--- /dev/null
+++ b/sapi/fpm/fpm/zlog.h
@@ -0,0 +1,45 @@
+
+ /* $Id: zlog.h,v 1.7 2008/05/22 21:08:32 anight Exp $ */
+ /* (c) 2004-2007 Andrei Nigmatulin */
+
+#ifndef ZLOG_H
+#define ZLOG_H 1
+
+#define zlog(flags,...) zlog_ex(__func__, __LINE__, flags, __VA_ARGS__)
+
+struct timeval;
+
+void zlog_set_external_logger(void (*logger)(int, char *, size_t));
+int zlog_set_fd(int new_fd);
+int zlog_set_level(int new_value);
+const char *zlog_get_level_name(int log_level);
+void zlog_set_launched(void);
+
+size_t zlog_print_time(struct timeval *tv, char *timebuf, size_t timebuf_len);
+
+void zlog_ex(const char *function, int line, int flags, const char *fmt, ...)
+ __attribute__ ((format(printf,4,5)));
+
+#ifdef HAVE_SYSLOG_H
+extern const int syslog_priorities[];
+#endif
+
+enum {
+ ZLOG_DEBUG = 1,
+ ZLOG_NOTICE = 2,
+ ZLOG_WARNING = 3,
+ ZLOG_ERROR = 4,
+ ZLOG_ALERT = 5,
+};
+
+#define ZLOG_LEVEL_MASK 7
+
+#define ZLOG_HAVE_ERRNO 0x100
+
+#define ZLOG_SYSERROR (ZLOG_ERROR | ZLOG_HAVE_ERRNO)
+
+#ifdef HAVE_SYSLOG_H
+#define ZLOG_SYSLOG -2
+#endif
+
+#endif
diff --git a/sapi/fpm/init.d.php-fpm.in b/sapi/fpm/init.d.php-fpm.in
new file mode 100644
index 0000000..49cce79
--- /dev/null
+++ b/sapi/fpm/init.d.php-fpm.in
@@ -0,0 +1,138 @@
+#! /bin/sh
+
+### BEGIN INIT INFO
+# Provides: php-fpm
+# Required-Start: $remote_fs $network
+# Required-Stop: $remote_fs $network
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: starts php-fpm
+# Description: starts the PHP FastCGI Process Manager daemon
+### END INIT INFO
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+php_fpm_BIN=@sbindir@/php-fpm
+php_fpm_CONF=@sysconfdir@/php-fpm.conf
+php_fpm_PID=@localstatedir@/run/php-fpm.pid
+
+
+php_opts="--fpm-config $php_fpm_CONF --pid $php_fpm_PID"
+
+
+wait_for_pid () {
+ try=0
+
+ while test $try -lt 35 ; do
+
+ case "$1" in
+ 'created')
+ if [ -f "$2" ] ; then
+ try=''
+ break
+ fi
+ ;;
+
+ 'removed')
+ if [ ! -f "$2" ] ; then
+ try=''
+ break
+ fi
+ ;;
+ esac
+
+ echo -n .
+ try=`expr $try + 1`
+ sleep 1
+
+ done
+
+}
+
+case "$1" in
+ start)
+ echo -n "Starting php-fpm "
+
+ $php_fpm_BIN --daemonize $php_opts
+
+ if [ "$?" != 0 ] ; then
+ echo " failed"
+ exit 1
+ fi
+
+ wait_for_pid created $php_fpm_PID
+
+ if [ -n "$try" ] ; then
+ echo " failed"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ stop)
+ echo -n "Gracefully shutting down php-fpm "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -QUIT `cat $php_fpm_PID`
+
+ wait_for_pid removed $php_fpm_PID
+
+ if [ -n "$try" ] ; then
+ echo " failed. Use force-quit"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ force-quit)
+ echo -n "Terminating php-fpm "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -TERM `cat $php_fpm_PID`
+
+ wait_for_pid removed $php_fpm_PID
+
+ if [ -n "$try" ] ; then
+ echo " failed"
+ exit 1
+ else
+ echo " done"
+ fi
+ ;;
+
+ restart)
+ $0 stop
+ $0 start
+ ;;
+
+ reload)
+
+ echo -n "Reload service php-fpm "
+
+ if [ ! -r $php_fpm_PID ] ; then
+ echo "warning, no pid file found - php-fpm is not running ?"
+ exit 1
+ fi
+
+ kill -USR2 `cat $php_fpm_PID`
+
+ echo " done"
+ ;;
+
+ *)
+ echo "Usage: $0 {start|stop|force-quit|restart|reload}"
+ exit 1
+ ;;
+
+esac
diff --git a/sapi/fpm/php-fpm.8.in b/sapi/fpm/php-fpm.8.in
new file mode 100644
index 0000000..a4e7e74
--- /dev/null
+++ b/sapi/fpm/php-fpm.8.in
@@ -0,0 +1,220 @@
+.TH PHP-FPM 8 "2009" "The PHP Group" "Scripting Language"
+.SH NAME
+.TP 15
+php-fpm \- PHP FastCGI Process Manager 'PHP-FPM'
+.SH SYNOPSIS
+.B php-fpm
+[options]
+.LP
+.SH DESCRIPTION
+\fBPHP\fP is a widely\-used general\-purpose scripting language that is especially suited for
+Web development and can be embedded into HTML. This is a variant of PHP that will run in the background as a daemon, listening for CGI requests. Output is logged to @php_fpm_localstatedir@/log/php-fpm.log.
+.LP
+Most options are set in the configuration file. The configuration file is @php_fpm_sysconfdir@/php-fpm.conf. By default, php-fpm will respond to CGI requests listening on localhost http port 9000. Therefore php-fpm expects your webserver to forward all requests for '.php' files to port 9000 and you should edit your webserver configuration file appropriately.
+.SH OPTIONS
+.TP 15
+.B \-C
+Do not chdir to the script's directory
+.TP
+.PD 0
+.B \-\-php\-ini \fIpath\fP|\fIfile\fP
+.TP
+.PD 1
+.B \-c \fIpath\fP|\fIfile\fP
+Look for
+.B php.ini
+file in the directory
+.IR path
+or use the specified
+.IR file
+.TP
+.PD 0
+.B \-\-no\-php\-ini
+.TP
+.PD 1
+.B \-n
+No
+.B php.ini
+file will be used
+.TP
+.PD 0
+.B \-\-define \fIfoo\fP[=\fIbar\fP]
+.TP
+.PD 1
+.B \-d \fIfoo\fP[=\fIbar\fP]
+Define INI entry
+.IR foo
+with value
+.IR bar
+.TP
+.B \-e
+Generate extended information for debugger/profiler
+.TP
+.PD 0
+.B \-\-help
+.TP
+.PD 1
+.B \-h
+This help
+.TP
+.PD 0
+.B \-\-info
+.TP
+.PD 1
+.B \-i
+PHP information and configuration
+.TP
+.PD 0
+.B \-\-modules
+.TP
+.PD 1
+.B \-m
+Show compiled in modules
+.TP
+.PD 0
+.B \-\-version
+.TP
+.PD 1
+.B \-v
+Version number
+.B \-\-prefix \fIpath\fP
+.TP
+.PD 1
+.B \-p
+Specify alternative prefix path (the default is @php_fpm_prefix@)
+.TP
+.PD 0
+.B \-\-fpm\-config \fIfile\fP
+.TP
+.PD 1
+.B \-y
+Specify alternative path to FastCGI process manager configuration file (the default is @php_fpm_sysconfdir@/php-fpm.conf)
+.TP
+.PD 0
+.B \-\-test
+.TP
+.PD 1
+.B \-t
+Test FPM configuration file and exit
+If called twice (-tt), the configuration is dumped before exiting.
+.TP
+.PD 0
+.B \-\-daemonize
+.TP
+.PD 1
+.B \-D
+Force to run in background and ignore daemonize option from configuration file.
+.TP
+.PD 0
+.B \-\-nodaemonize
+.TP
+.PD 1
+.B \-F
+Force to stay in foreground and ignore daemonize option from configuration file.
+.TP
+.PD 0
+.B \-\-zend\-extension \fIfile\fP
+.TP
+.PD 1
+.B \-z \fIfile\fP
+Load Zend extension
+.IR file
+.SH FILES
+.TP 15
+.B php-fpm.conf
+The configuration file for the php-fpm daemon.
+.TP
+.B php.ini
+The standard php configuration file.
+.SH EXAMPLES
+For any unix systems which use init.d for their main process manager, you should use the init script provided to start and stop the php-fpm daemon.
+.P
+.PD 1
+.RS
+sudo /etc/init.d/php-fpm start
+.RE
+.TP
+For any unix systems which use systemd for their main process manager, you should use the unit file provided to start and stop the php-fpm daemon.
+.P
+.PD 1
+.RS
+sudo systemctl start php-fpm.service
+.RE
+.TP
+If your installation has no appropriate init script, launch php-fpm with no arguments. It will launch as a daemon (background process) by default. The file @php_fpm_localstatedir@/run/php-fpm.pid determines whether php-fpm is already up and running. Once started, php-fpm then responds to several POSIX signals:
+.P
+.PD 0
+.RS
+.B SIGINT,SIGTERM \fPimmediate termination
+.TP
+.B SIGQUIT \fPgraceful stop
+.TP
+.B SIGUSR1 \fPre-open log file
+.TP
+.B SIGUSR2 \fPgraceful reload of all workers + reload of fpm conf/binary
+.RE
+.PD 1
+.P
+.SH TIPS
+The PHP-FPM CGI daemon will work well with most popular webservers, including Apache2, lighttpd and nginx.
+.PD 1
+.P
+.SH SEE ALSO
+The PHP-FPM website:
+.PD 0
+.P
+.B http://php-fpm.org
+.PD 1
+.P
+For a more or less complete description of PHP look here:
+.PD 0
+.P
+.B http://www.php.net/manual/
+.PD 1
+.P
+A nice introduction to PHP by Stig Bakken can be found here:
+.PD 0
+.P
+.B http://www.zend.com/zend/art/intro.php
+.PD 1
+.SH BUGS
+You can view the list of known bugs or report any new bug you
+found at:
+.PD 0
+.P
+.B http://bugs.php.net
+.PD 1
+.SH AUTHORS
+PHP-FPM SAPI was written by Andrei Nigmatulin. The mailing-lists are highload-php-en (English) and highload-php-ru (Russian).
+.P
+The PHP Group: Thies C. Arntzen, Stig Bakken, Andi Gutmans, Rasmus Lerdorf, Sam Ruby, Sascha Schumann, Zeev Suraski, Jim Winstead, Andrei Zmievski.
+.P
+A List of active developers can be found here:
+.PD 0
+.P
+.B http://www.php.net/credits.php
+.PD 1
+.P
+And last but not least PHP was developed with the help of a huge amount of
+contributors all around the world.
+.SH VERSION INFORMATION
+This manpage describes \fBphp-fpm\fP, version @PHP_VERSION@.
+.SH COPYRIGHT
+Copyright \(co 1997\-2009 The PHP Group
+.PD 0
+.P
+Copyright (c) 2007-2009, Andrei Nigmatulin
+.PD 1
+.LP
+This source file is subject to version 3.01 of the PHP license,
+that is bundled with this package in the file LICENSE, and is
+available through the world-wide-web at the following url:
+.PD 0
+.P
+.B http://www.php.net/license/3_01.txt
+.PD 1
+.P
+If you did not receive a copy of the PHP license and are unable to
+obtain it through the world-wide-web, please send a note to
+.B license@php.net
+so we can mail you a copy immediately.
diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in
new file mode 100644
index 0000000..a63dec7
--- /dev/null
+++ b/sapi/fpm/php-fpm.conf.in
@@ -0,0 +1,510 @@
+;;;;;;;;;;;;;;;;;;;;;
+; FPM Configuration ;
+;;;;;;;;;;;;;;;;;;;;;
+
+; All relative paths in this configuration file are relative to PHP's install
+; prefix (@prefix@). This prefix can be dynamicaly changed by using the
+; '-p' argument from the command line.
+
+; Include one or more files. If glob(3) exists, it is used to include a bunch of
+; files from a glob(3) pattern. This directive can be used everywhere in the
+; file.
+; Relative path can also be used. They will be prefixed by:
+; - the global prefix if it's been set (-p arguement)
+; - @prefix@ otherwise
+;include=etc/fpm.d/*.conf
+
+;;;;;;;;;;;;;;;;;;
+; Global Options ;
+;;;;;;;;;;;;;;;;;;
+
+[global]
+; Pid file
+; Note: the default prefix is @EXPANDED_LOCALSTATEDIR@
+; Default Value: none
+;pid = run/php-fpm.pid
+
+; Error log file
+; If it's set to "syslog", log is sent to syslogd instead of being written
+; in a local file.
+; Note: the default prefix is @EXPANDED_LOCALSTATEDIR@
+; Default Value: log/php-fpm.log
+;error_log = log/php-fpm.log
+
+; syslog_facility is used to specify what type of program is logging the
+; message. This lets syslogd specify that messages from different facilities
+; will be handled differently.
+; See syslog(3) for possible values (ex daemon equiv LOG_DAEMON)
+; Default Value: daemon
+;syslog.facility = daemon
+
+; syslog_ident is prepended to every message. If you have multiple FPM
+; instances running on the same server, you can change the default value
+; which must suit common needs.
+; Default Value: php-fpm
+;syslog.ident = php-fpm
+
+; Log level
+; Possible Values: alert, error, warning, notice, debug
+; Default Value: notice
+;log_level = notice
+
+; If this number of child processes exit with SIGSEGV or SIGBUS within the time
+; interval set by emergency_restart_interval then FPM will restart. A value
+; of '0' means 'Off'.
+; Default Value: 0
+;emergency_restart_threshold = 0
+
+; Interval of time used by emergency_restart_interval to determine when
+; a graceful restart will be initiated. This can be useful to work around
+; accidental corruptions in an accelerator's shared memory.
+; Available Units: s(econds), m(inutes), h(ours), or d(ays)
+; Default Unit: seconds
+; Default Value: 0
+;emergency_restart_interval = 0
+
+; Time limit for child processes to wait for a reaction on signals from master.
+; Available units: s(econds), m(inutes), h(ours), or d(ays)
+; Default Unit: seconds
+; Default Value: 0
+;process_control_timeout = 0
+
+; The maximum number of processes FPM will fork. This has been design to control
+; the global number of processes when using dynamic PM within a lot of pools.
+; Use it with caution.
+; Note: A value of 0 indicates no limit
+; Default Value: 0
+; process.max = 128
+
+; Specify the nice(2) priority to apply to the master process (only if set)
+; The value can vary from -19 (highest priority) to 20 (lower priority)
+; Note: - It will only work if the FPM master process is launched as root
+; - The pool process will inherit the master process priority
+; unless it specified otherwise
+; Default Value: no set
+; process.priority = -19
+
+; Send FPM to background. Set to 'no' to keep FPM in foreground for debugging.
+; Default Value: yes
+;daemonize = yes
+
+; Set open file descriptor rlimit for the master process.
+; Default Value: system defined value
+;rlimit_files = 1024
+
+; Set max core size rlimit for the master process.
+; Possible Values: 'unlimited' or an integer greater or equal to 0
+; Default Value: system defined value
+;rlimit_core = 0
+
+; Specify the event mechanism FPM will use. The following is available:
+; - select (any POSIX os)
+; - poll (any POSIX os)
+; - epoll (linux >= 2.5.44)
+; - kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0)
+; - /dev/poll (Solaris >= 7)
+; - port (Solaris >= 10)
+; Default Value: not set (auto detection)
+; events.mechanism = epoll
+
+;;;;;;;;;;;;;;;;;;;;
+; Pool Definitions ;
+;;;;;;;;;;;;;;;;;;;;
+
+; Multiple pools of child processes may be started with different listening
+; ports and different management options. The name of the pool will be
+; used in logs and stats. There is no limitation on the number of pools which
+; FPM can handle. Your system will tell you anyway :)
+
+; Start a new pool named 'www'.
+; the variable $pool can we used in any directive and will be replaced by the
+; pool name ('www' here)
+[www]
+
+; Per pool prefix
+; It only applies on the following directives:
+; - 'slowlog'
+; - 'listen' (unixsocket)
+; - 'chroot'
+; - 'chdir'
+; - 'php_values'
+; - 'php_admin_values'
+; When not set, the global prefix (or @php_fpm_prefix@) applies instead.
+; Note: This directive can also be relative to the global prefix.
+; Default Value: none
+;prefix = /path/to/pools/$pool
+
+; Unix user/group of processes
+; Note: The user is mandatory. If the group is not set, the default user's group
+; will be used.
+user = @php_fpm_user@
+group = @php_fpm_group@
+
+; The address on which to accept FastCGI requests.
+; Valid syntaxes are:
+; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific address on
+; a specific port;
+; 'port' - to listen on a TCP socket to all addresses on a
+; specific port;
+; '/path/to/unix/socket' - to listen on a unix socket.
+; Note: This value is mandatory.
+listen = 127.0.0.1:9000
+
+; Set listen(2) backlog.
+; Default Value: 128 (-1 on FreeBSD and OpenBSD)
+;listen.backlog = 128
+
+; Set permissions for unix socket, if one is used. In Linux, read/write
+; permissions must be set in order to allow connections from a web server. Many
+; BSD-derived systems allow connections regardless of permissions.
+; Default Values: user and group are set as the running user
+; mode is set to 0666
+;listen.owner = @php_fpm_user@
+;listen.group = @php_fpm_group@
+;listen.mode = 0666
+
+; List of ipv4 addresses of FastCGI clients which are allowed to connect.
+; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original
+; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address
+; must be separated by a comma. If this value is left blank, connections will be
+; accepted from any ip address.
+; Default Value: any
+;listen.allowed_clients = 127.0.0.1
+
+; Specify the nice(2) priority to apply to the pool processes (only if set)
+; The value can vary from -19 (highest priority) to 20 (lower priority)
+; Note: - It will only work if the FPM master process is launched as root
+; - The pool processes will inherit the master process priority
+; unless it specified otherwise
+; Default Value: no set
+; priority = -19
+
+; Choose how the process manager will control the number of child processes.
+; Possible Values:
+; static - a fixed number (pm.max_children) of child processes;
+; dynamic - the number of child processes are set dynamically based on the
+; following directives. With this process management, there will be
+; always at least 1 children.
+; pm.max_children - the maximum number of children that can
+; be alive at the same time.
+; pm.start_servers - the number of children created on startup.
+; pm.min_spare_servers - the minimum number of children in 'idle'
+; state (waiting to process). If the number
+; of 'idle' processes is less than this
+; number then some children will be created.
+; pm.max_spare_servers - the maximum number of children in 'idle'
+; state (waiting to process). If the number
+; of 'idle' processes is greater than this
+; number then some children will be killed.
+; ondemand - no children are created at startup. Children will be forked when
+; new requests will connect. The following parameter are used:
+; pm.max_children - the maximum number of children that
+; can be alive at the same time.
+; pm.process_idle_timeout - The number of seconds after which
+; an idle process will be killed.
+; Note: This value is mandatory.
+pm = dynamic
+
+; The number of child processes to be created when pm is set to 'static' and the
+; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
+; This value sets the limit on the number of simultaneous requests that will be
+; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
+; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
+; CGI. The below defaults are based on a server without much resources. Don't
+; forget to tweak pm.* to fit your needs.
+; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
+; Note: This value is mandatory.
+pm.max_children = 5
+
+; The number of child processes created on startup.
+; Note: Used only when pm is set to 'dynamic'
+; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
+pm.start_servers = 2
+
+; The desired minimum number of idle server processes.
+; Note: Used only when pm is set to 'dynamic'
+; Note: Mandatory when pm is set to 'dynamic'
+pm.min_spare_servers = 1
+
+; The desired maximum number of idle server processes.
+; Note: Used only when pm is set to 'dynamic'
+; Note: Mandatory when pm is set to 'dynamic'
+pm.max_spare_servers = 3
+
+; The number of seconds after which an idle process will be killed.
+; Note: Used only when pm is set to 'ondemand'
+; Default Value: 10s
+;pm.process_idle_timeout = 10s;
+
+; The number of requests each child process should execute before respawning.
+; This can be useful to work around memory leaks in 3rd party libraries. For
+; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
+; Default Value: 0
+;pm.max_requests = 500
+
+; The URI to view the FPM status page. If this value is not set, no URI will be
+; recognized as a status page. It shows the following informations:
+; pool - the name of the pool;
+; process manager - static, dynamic or ondemand;
+; start time - the date and time FPM has started;
+; start since - number of seconds since FPM has started;
+; accepted conn - the number of request accepted by the pool;
+; listen queue - the number of request in the queue of pending
+; connections (see backlog in listen(2));
+; max listen queue - the maximum number of requests in the queue
+; of pending connections since FPM has started;
+; listen queue len - the size of the socket queue of pending connections;
+; idle processes - the number of idle processes;
+; active processes - the number of active processes;
+; total processes - the number of idle + active processes;
+; max active processes - the maximum number of active processes since FPM
+; has started;
+; max children reached - number of times, the process limit has been reached,
+; when pm tries to start more children (works only for
+; pm 'dynamic' and 'ondemand');
+; Value are updated in real time.
+; Example output:
+; pool: www
+; process manager: static
+; start time: 01/Jul/2011:17:53:49 +0200
+; start since: 62636
+; accepted conn: 190460
+; listen queue: 0
+; max listen queue: 1
+; listen queue len: 42
+; idle processes: 4
+; active processes: 11
+; total processes: 15
+; max active processes: 12
+; max children reached: 0
+;
+; By default the status page output is formatted as text/plain. Passing either
+; 'html', 'xml' or 'json' in the query string will return the corresponding
+; output syntax. Example:
+; http://www.foo.bar/status
+; http://www.foo.bar/status?json
+; http://www.foo.bar/status?html
+; http://www.foo.bar/status?xml
+;
+; By default the status page only outputs short status. Passing 'full' in the
+; query string will also return status for each pool process.
+; Example:
+; http://www.foo.bar/status?full
+; http://www.foo.bar/status?json&full
+; http://www.foo.bar/status?html&full
+; http://www.foo.bar/status?xml&full
+; The Full status returns for each process:
+; pid - the PID of the process;
+; state - the state of the process (Idle, Running, ...);
+; start time - the date and time the process has started;
+; start since - the number of seconds since the process has started;
+; requests - the number of requests the process has served;
+; request duration - the duration in µs of the requests;
+; request method - the request method (GET, POST, ...);
+; request URI - the request URI with the query string;
+; content length - the content length of the request (only with POST);
+; user - the user (PHP_AUTH_USER) (or '-' if not set);
+; script - the main script called (or '-' if not set);
+; last request cpu - the %cpu the last request consumed
+; it's always 0 if the process is not in Idle state
+; because CPU calculation is done when the request
+; processing has terminated;
+; last request memory - the max amount of memory the last request consumed
+; it's always 0 if the process is not in Idle state
+; because memory calculation is done when the request
+; processing has terminated;
+; If the process is in Idle state, then informations are related to the
+; last request the process has served. Otherwise informations are related to
+; the current request being served.
+; Example output:
+; ************************
+; pid: 31330
+; state: Running
+; start time: 01/Jul/2011:17:53:49 +0200
+; start since: 63087
+; requests: 12808
+; request duration: 1250261
+; request method: GET
+; request URI: /test_mem.php?N=10000
+; content length: 0
+; user: -
+; script: /home/fat/web/docs/php/test_mem.php
+; last request cpu: 0.00
+; last request memory: 0
+;
+; Note: There is a real-time FPM status monitoring sample web page available
+; It's available in: @EXPANDED_DATADIR@/fpm/status.html
+;
+; Note: The value must start with a leading slash (/). The value can be
+; anything, but it may not be a good idea to use the .php extension or it
+; may conflict with a real PHP file.
+; Default Value: not set
+;pm.status_path = /status
+
+; The ping URI to call the monitoring page of FPM. If this value is not set, no
+; URI will be recognized as a ping page. This could be used to test from outside
+; that FPM is alive and responding, or to
+; - create a graph of FPM availability (rrd or such);
+; - remove a server from a group if it is not responding (load balancing);
+; - trigger alerts for the operating team (24/7).
+; Note: The value must start with a leading slash (/). The value can be
+; anything, but it may not be a good idea to use the .php extension or it
+; may conflict with a real PHP file.
+; Default Value: not set
+;ping.path = /ping
+
+; This directive may be used to customize the response of a ping request. The
+; response is formatted as text/plain with a 200 response code.
+; Default Value: pong
+;ping.response = pong
+
+; The access log file
+; Default: not set
+;access.log = log/$pool.access.log
+
+; The access log format.
+; The following syntax is allowed
+; %%: the '%' character
+; %C: %CPU used by the request
+; it can accept the following format:
+; - %{user}C for user CPU only
+; - %{system}C for system CPU only
+; - %{total}C for user + system CPU (default)
+; %d: time taken to serve the request
+; it can accept the following format:
+; - %{seconds}d (default)
+; - %{miliseconds}d
+; - %{mili}d
+; - %{microseconds}d
+; - %{micro}d
+; %e: an environment variable (same as $_ENV or $_SERVER)
+; it must be associated with embraces to specify the name of the env
+; variable. Some exemples:
+; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e
+; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e
+; %f: script filename
+; %l: content-length of the request (for POST request only)
+; %m: request method
+; %M: peak of memory allocated by PHP
+; it can accept the following format:
+; - %{bytes}M (default)
+; - %{kilobytes}M
+; - %{kilo}M
+; - %{megabytes}M
+; - %{mega}M
+; %n: pool name
+; %o: ouput header
+; it must be associated with embraces to specify the name of the header:
+; - %{Content-Type}o
+; - %{X-Powered-By}o
+; - %{Transfert-Encoding}o
+; - ....
+; %p: PID of the child that serviced the request
+; %P: PID of the parent of the child that serviced the request
+; %q: the query string
+; %Q: the '?' character if query string exists
+; %r: the request URI (without the query string, see %q and %Q)
+; %R: remote IP address
+; %s: status (response code)
+; %t: server time the request was received
+; it can accept a strftime(3) format:
+; %d/%b/%Y:%H:%M:%S %z (default)
+; %T: time the log has been written (the request has finished)
+; it can accept a strftime(3) format:
+; %d/%b/%Y:%H:%M:%S %z (default)
+; %u: remote user
+;
+; Default: "%R - %u %t \"%m %r\" %s"
+;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"
+
+; The log file for slow requests
+; Default Value: not set
+; Note: slowlog is mandatory if request_slowlog_timeout is set
+;slowlog = log/$pool.log.slow
+
+; The timeout for serving a single request after which a PHP backtrace will be
+; dumped to the 'slowlog' file. A value of '0s' means 'off'.
+; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
+; Default Value: 0
+;request_slowlog_timeout = 0
+
+; The timeout for serving a single request after which the worker process will
+; be killed. This option should be used when the 'max_execution_time' ini option
+; does not stop script execution for some reason. A value of '0' means 'off'.
+; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
+; Default Value: 0
+;request_terminate_timeout = 0
+
+; Set open file descriptor rlimit.
+; Default Value: system defined value
+;rlimit_files = 1024
+
+; Set max core size rlimit.
+; Possible Values: 'unlimited' or an integer greater or equal to 0
+; Default Value: system defined value
+;rlimit_core = 0
+
+; Chroot to this directory at the start. This value must be defined as an
+; absolute path. When this value is not set, chroot is not used.
+; Note: you can prefix with '$prefix' to chroot to the pool prefix or one
+; of its subdirectories. If the pool prefix is not set, the global prefix
+; will be used instead.
+; Note: chrooting is a great security feature and should be used whenever
+; possible. However, all PHP paths will be relative to the chroot
+; (error_log, sessions.save_path, ...).
+; Default Value: not set
+;chroot =
+
+; Chdir to this directory at the start.
+; Note: relative path can be used.
+; Default Value: current directory or / when chroot
+;chdir = /var/www
+
+; Redirect worker stdout and stderr into main error log. If not set, stdout and
+; stderr will be redirected to /dev/null according to FastCGI specs.
+; Note: on highloaded environement, this can cause some delay in the page
+; process time (several ms).
+; Default Value: no
+;catch_workers_output = yes
+
+; Limits the extensions of the main script FPM will allow to parse. This can
+; prevent configuration mistakes on the web server side. You should only limit
+; FPM to .php extensions to prevent malicious users to use other extensions to
+; exectute php code.
+; Note: set an empty value to allow all extensions.
+; Default Value: .php
+;security.limit_extensions = .php .php3 .php4 .php5
+
+; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from
+; the current environment.
+; Default Value: clean env
+;env[HOSTNAME] = $HOSTNAME
+;env[PATH] = /usr/local/bin:/usr/bin:/bin
+;env[TMP] = /tmp
+;env[TMPDIR] = /tmp
+;env[TEMP] = /tmp
+
+; Additional php.ini defines, specific to this pool of workers. These settings
+; overwrite the values previously defined in the php.ini. The directives are the
+; same as the PHP SAPI:
+; php_value/php_flag - you can set classic ini defines which can
+; be overwritten from PHP call 'ini_set'.
+; php_admin_value/php_admin_flag - these directives won't be overwritten by
+; PHP call 'ini_set'
+; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no.
+
+; Defining 'extension' will load the corresponding shared extension from
+; extension_dir. Defining 'disable_functions' or 'disable_classes' will not
+; overwrite previously defined php.ini values, but will append the new value
+; instead.
+
+; Note: path INI options can be relative and will be expanded with the prefix
+; (pool, global or @prefix@)
+
+; Default Value: nothing is defined by default except the values in php.ini and
+; specified at startup with the -d argument
+;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
+;php_flag[display_errors] = off
+;php_admin_value[error_log] = /var/log/fpm-php.www.log
+;php_admin_flag[log_errors] = on
+;php_admin_value[memory_limit] = 32M
diff --git a/sapi/fpm/php-fpm.service.in b/sapi/fpm/php-fpm.service.in
new file mode 100644
index 0000000..396a88d
--- /dev/null
+++ b/sapi/fpm/php-fpm.service.in
@@ -0,0 +1,12 @@
+[Unit]
+Description=The PHP FastCGI Process Manager
+After=syslog.target network.target
+
+[Service]
+PIDFile=@localstatedir@/run/php-fpm.pid
+ExecStart=@sbindir@/php-fpm --nodaemonize --fpm-config @sysconfdir@/php-fpm.conf
+ExecReload=/bin/kill -USR2 $MAINPID
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/sapi/fpm/status.html.in b/sapi/fpm/status.html.in
new file mode 100644
index 0000000..86492d7
--- /dev/null
+++ b/sapi/fpm/status.html.in
@@ -0,0 +1,459 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+ $Id$
+ (c) 2011 Jerome Loyet
+ The PHP License, version 3.01
+ This is sample real-time status page for FPM. You can change it to better feet your needs.
+-->
+ <head>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+ <style type="text/css">
+ body {background-color: #ffffff; color: #000000;}
+ body, td, th, h1, h2 {font-family: sans-serif;}
+ pre {margin: 0px; font-family: monospace;}
+ a:link {color: #000099; text-decoration: none; background-color: #ffffff;}
+ a:hover {text-decoration: underline;}
+ table {border-collapse: collapse;}
+ .center {text-align: center;}
+ .center table { margin-left: auto; margin-right: auto; text-align: left;}
+ .center th { text-align: center !important; }
+ td, th { border: 1px solid #000000; font-size: 75%; vertical-align: baseline;}
+ h1 {font-size: 150%;}
+ h2 {font-size: 125%;}
+ .p {text-align: left;}
+ .e {background-color: #ccccff; font-weight: bold; color: #000000;}
+ .h {background-color: #9999cc; font-weight: bold; color: #000000;}
+
+ .v {background-color: #cccccc; color: #000000;}
+ .w {background-color: #ccccff; color: #000000;}
+
+ .h th {
+ cursor: pointer;
+ }
+ img {float: right; border: 0px;}
+ hr {width: 600px; background-color: #cccccc; border: 0px; height: 1px; color: #000000;}
+ </style>
+ <title>PHP-FPM status page</title>
+ <meta name="ROBOTS" content="NOINDEX,NOFOLLOW,NOARCHIVE" /></head>
+ <body>
+ <div class="center">
+ <table border="0" cellpadding="3" width="95%">
+ <tr class="h">
+ <td>
+ <a href="http://www.php.net/"><img border="0" src="https://static.php.net/www.php.net/images/php.gif" alt="PHP Logo" /></a><h1 class="p">PHP-FPM real-time status page</h1>
+ </td>
+ </tr>
+ </table>
+ <br />
+ <table border="0" cellpadding="3" width="95%">
+ <tr><td class="e">Status URL</td><td class="v"><input type="text" id="url" size="45" /></td></tr>
+ <tr><td class="e">Ajax status</td><td class="v" id="status"></td></tr>
+ <tr><td class="e">Refresh Rate</td><td class="v"><input type="text" id="rate" value="1" /></td></tr>
+ <tr>
+ <td class="e">Actions</td>
+ <td class="v">
+ <button onclick="javascript:refresh();">Manual Refresh</button>
+ <button id="play" onclick="javascript:playpause();">Play</button>
+ </td>
+ </tr>
+ </table>
+ <h1>Pool Status</h1>
+ <table border="0" cellpadding="3" width="95%" id="short">
+ <tr style="display: none;"><td>&nbsp;</td></tr>
+ </table>
+ <h1>Active Processes status</h1>
+ <table border="0" cellpadding="3" width="95%" id="active">
+ <tr class="h"><th>PID&darr;</th><th>Start Time</th><th>Start Since</th><th>Requests Served</th><th>Request Duration</th><th>Request method</th><th>Request URI</th><th>Content Length</th><th>User</th><th>Script</th></tr>
+ </table>
+ <h1>Idle Processes status</h1>
+ <table border="0" cellpadding="3" width="95%" id="idle">
+ <tr class="h"><th>PID&darr;</th><th>Start Time</th><th>Start Since</th><th>Requests Served</th><th>Request Duration</th><th>Request method</th><th>Request URI</th><th>Content Length</th><th>User</th><th>Script</th><th>Last Request %CPU</th><th>Last Request Memory</th></tr>
+ </table>
+ </div>
+ <p>
+ <a href="http://validator.w3.org/check?uri=referer">
+ <img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0 Transitional" height="31" width="88" />
+ </a>
+ </p>
+ <script type="text/javascript">
+<!--
+ var xhr_object = null;
+ var doc_url = document.getElementById("url");
+ var doc_rate = document.getElementById("rate");
+ var doc_status = document.getElementById("status");
+ var doc_play = document.getElementById("play");
+ var doc_short = document.getElementById("short");
+ var doc_active = document.getElementById("active");
+ var doc_idle = document.getElementById("idle");
+ var rate = 0;
+ var play=0;
+ var delay = 1000;
+ var order_active_index = 0;
+ var order_active_reverse = 0;
+ var order_idle_index = 0;
+ var order_idle_reverse = 0;
+ var sort_index;
+ var sort_order;
+
+ doc_url.value = location.protocol + '//' + location.host + "/status?json&full";
+
+ ths = document.getElementsByTagName("th");
+ for (var i=0; i<ths.length; i++) {
+ var th = ths[i];
+ if (th.parentNode.className == "h") {
+ th.onclick = function() { order(this); return false; };
+ }
+ }
+
+ xhr_object = create_ajax();
+
+ function create_ajax() {
+ if (window.XMLHttpRequest) {
+ return new XMLHttpRequest();
+ }
+ var names = [
+ "Msxml2.XMLHTTP.6.0",
+ "Msxml2.XMLHTTP.3.0",
+ "Msxml2.XMLHTTP",
+ "Microsoft.XMLHTTP"
+ ];
+ for(var i in names)
+ {
+ try {
+ return new ActiveXObject(names[i]);
+ } catch(e){}
+ }
+ alert("Browser not compatible ...");
+ }
+
+ function order(cell) {
+ var table;
+
+ if (cell.constructor != HTMLTableCellElement && cell.constructor != HTMLTableHeaderCellElement) {
+ return;
+ }
+
+ table = cell.parentNode.parentNode.parentNode;
+
+ if (table == doc_active) {
+ if (order_active_index == cell.cellIndex) {
+ if (order_active_reverse == 0) {
+ cell.innerHTML = cell.innerHTML.replace(/.$/, "&uarr;");
+ order_active_reverse = 1;
+ } else {
+ cell.innerHTML = cell.innerHTML.replace(/.$/, "&darr;");
+ order_active_reverse = 0;
+ }
+ } else {
+ var c = doc_active.rows[0].cells[order_active_index];
+ c.innerHTML = c.innerHTML.replace(/.$/, "");
+ cell.innerHTML = cell.innerHTML.replace(/$/, order_active_reverse == 0 ? "&darr;" : "&uarr;");
+ order_active_index = cell.cellIndex;
+ }
+ reorder(table, order_active_index, order_active_reverse);
+ return;
+ }
+
+ if (table == doc_idle) {
+ if (order_idle_index == cell.cellIndex) {
+ if (order_idle_reverse == 0) {
+ cell.innerHTML = cell.innerHTML.replace(/.$/, "&uarr;");
+ order_idle_reverse = 1;
+ } else {
+ cell.innerHTML = cell.innerHTML.replace(/.$/, "&darr;");
+ order_idle_reverse = 0;
+ }
+ } else {
+ var c = doc_idle.rows[0].cells[order_idle_index];
+ c.innerHTML = c.innerHTML.replace(/.$/, "");
+ cell.innerHTML = cell.innerHTML.replace(/$/, order_idle_reverse == 0 ? "&darr;" : "&uarr;");
+ order_idle_index = cell.cellIndex;
+ }
+ reorder(table, order_idle_index, order_idle_reverse);
+ return;
+ }
+ }
+
+ function reorder(table, index, order) {
+ var rows = [];
+ while (table.rows.length > 1) {
+ rows.push(table.rows[1]);
+ table.deleteRow(1);
+ }
+ sort_index = index;
+ sort_order = order;
+ rows.sort(sort_table);
+ for (var i in rows) {
+ table.appendChild(rows[i]);
+ }
+ var odd = 1;
+ for (var i=1; i<table.rows.length; i++) {
+ table.rows[i].className = odd++ % 2 == 0 ? "v" : "w";
+ }
+ return;
+ }
+
+ function sort_table(a, b) {
+ if (a.cells[0].tagName == "TH") return -1;
+ if (b.cells[0].tagName == "TH") return 1;
+
+ if (a.cells[sort_index].__search_t == 0) { /* integer */
+ if (!sort_order) return a.cells[sort_index].__search_v - b.cells[sort_index].__search_v;
+ return b.cells[sort_index].__search_v - a.cells[sort_index].__search_v;;
+ }
+
+ /* string */
+ if (!sort_order) return a.cells[sort_index].__search_v.localeCompare(b.cells[sort_index].__search_v);
+ else return b.cells[sort_index].__search_v.localeCompare(a.cells[sort_index].__search_v);
+ }
+
+ function playpause() {
+ rate = 0;
+ if (play) {
+ play = 0;
+ doc_play.innerHTML = "Play";
+ doc_rate.disabled = false;
+ } else {
+ delay = parseInt(doc_rate.value);
+ if (!delay || delay < 1) {
+ doc_status.innerHTML = "Not valid 'refresh' value";
+ return;
+ }
+ play = 1;
+ doc_rate.disabled = true;
+ doc_play.innerHTML = "Pause";
+ setTimeout("callback()", delay * 1000);
+ }
+ }
+
+ function refresh() {
+ if (xhr_object == null) return;
+ if (xhr_object.readyState > 0 && xhr_object.readyState < 4) {
+ return; /* request is running */
+ }
+ xhr_object.open("GET", doc_url.value, true);
+ xhr_object.onreadystatechange = function() {
+ switch(xhr_object.readyState) {
+ case 0:
+ doc_status.innerHTML = "uninitialized";
+ break;
+ case 1:
+ doc_status.innerHTML = "loading ...";
+ break;
+ case 2:
+ doc_status.innerHTML = "loaded";
+ break;
+ case 3:
+ doc_status.innerHTML = "interactive";
+ break;
+ case 4:
+ doc_status.innerHTML = "complete";
+ if (xhr_object.status == 200) {
+ fpm_status(xhr_object.responseText);
+ } else {
+ doc_status.innerHTML = "Error " + xhr_object.status;
+ }
+ break;
+ }
+ }
+ xhr_object.send();
+ }
+
+ function callback() {
+ if (!play) return;
+ refresh();
+ setTimeout("callback()", delay * 1000);
+ }
+
+ function fpm_status(txt) {
+ var json = null;
+
+ while (doc_short.rows.length > 0) {
+ doc_short.deleteRow(0);
+ }
+
+ while (doc_active.rows.length > 1) {
+ doc_active.deleteRow(1);
+ }
+
+ while (doc_idle.rows.length > 1) {
+ doc_idle.deleteRow(1);
+ }
+
+ try {
+ json = JSON.parse(txt);
+ } catch (e) {
+ doc_status.innerHTML = "Error while parsing json: '" + e + "': <br /><pre>" + txt + "</pre>";
+ return;
+ }
+
+ for (var key in json) {
+ if (key == "processes") continue;
+ if (key == "state") continue;
+ var row = doc_short.insertRow(doc_short.rows.length);
+ var value = json[key];
+ if (key == "start time") {
+ value = new Date(value * 1000).toLocaleString();
+ }
+ if (key == "start since") {
+ value = time_s(value);
+ }
+ var cell = row.insertCell(row.cells.length);
+ cell.className = "e";
+ cell.innerHTML = key;
+
+ cell = row.insertCell(row.cells.length);
+ cell.className = "v";
+ cell.innerHTML = value;
+ }
+
+ if (json.processes) {
+ process_full(json.processes, doc_active, "Idle", 0, 0);
+ reorder(doc_active, order_active_index, order_active_reverse);
+
+ process_full(json.processes, doc_idle, "Idle", 1, 1);
+ reorder(doc_idle, order_idle_index, order_idle_reverse);
+ }
+ }
+
+ function process_full(processes, table, state, equal, cpumem) {
+ var odd = 1;
+
+ for (var i in processes) {
+ var proc = processes[i];
+ if ((equal && proc.state == state) || (!equal && proc.state != state)) {
+ var c = odd++ % 2 == 0 ? "v" : "w";
+ var row = table.insertRow(-1);
+ row.className = c;
+ row.insertCell(-1).innerHTML = proc.pid;
+ row.cells[row.cells.length - 1].__search_v = proc.pid;
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = date(proc['start time'] * 1000);;
+ row.cells[row.cells.length - 1].__search_v = proc['start time'];
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = time_s(proc['start since']);
+ row.cells[row.cells.length - 1].__search_v = proc['start since'];
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = proc.requests;
+ row.cells[row.cells.length - 1].__search_v = proc.requests;
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = time_u(proc['request duration']);
+ row.cells[row.cells.length - 1].__search_v = proc['request duration'];
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = proc['request method'];
+ row.cells[row.cells.length - 1].__search_v = proc['request method'];
+ row.cells[row.cells.length - 1].__search_t = 1;
+
+ row.insertCell(-1).innerHTML = proc['request uri'];
+ row.cells[row.cells.length - 1].__search_v = proc['request uri'];
+ row.cells[row.cells.length - 1].__search_t = 1;
+
+ row.insertCell(-1).innerHTML = proc['content length'];
+ row.cells[row.cells.length - 1].__search_v = proc['content length'];
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = proc.user;
+ row.cells[row.cells.length - 1].__search_v = proc.user;
+ row.cells[row.cells.length - 1].__search_t = 1;
+
+ row.insertCell(-1).innerHTML = proc.script;
+ row.cells[row.cells.length - 1].__search_v = proc.script;
+ row.cells[row.cells.length - 1].__search_t = 1;
+
+ if (cpumem) {
+ row.insertCell(-1).innerHTML = cpu(proc['last request cpu']);
+ row.cells[row.cells.length - 1].__search_v = proc['last request cpu'];
+ row.cells[row.cells.length - 1].__search_t = 0;
+
+ row.insertCell(-1).innerHTML = memory(proc['last request memory']);
+ row.cells[row.cells.length - 1].__search_v = proc['last request memory'];
+ row.cells[row.cells.length - 1].__search_t = 0;
+ }
+ }
+ }
+ }
+
+ function date(d) {
+ var t = new Date(d);
+ var r = "";
+
+ r += (t.getDate() < 10 ? '0' : '') + t.getDate();
+ r += '/';
+ r += (t.getMonth() + 1 < 10 ? '0' : '') + (t.getMonth() + 1);
+ r += '/';
+ r += t.getFullYear();
+ r += ' ';
+ r += (t.getHours() < 10 ? '0' : '') + t.getHours();
+ r += ':';
+ r += (t.getMinutes() < 10 ? '0' : '') + t.getMinutes();
+ r += ':';
+ r += (t.getSeconds() < 10 ? '0' : '') + t.getSeconds();
+
+
+ return r;
+ }
+
+ function cpu(c) {
+ if (c == 0) return 0;
+ return c + "%";
+ }
+
+ function memory(mem) {
+ if (mem == 0) return 0;
+ if (mem < 1024) {
+ return mem + "B";
+ }
+ if (mem < 1024 * 1024) {
+ return mem/1024 + "KB";
+ }
+ if (mem < 1024*1024*1024) {
+ return mem/1024/1024 + "MB";
+ }
+ }
+
+ function time_s(t) {
+ var r = "";
+ if (t < 60) {
+ return t + 's';
+ }
+
+ r = (t % 60) + 's';
+ t = Math.floor(t / 60);
+ if (t < 60) {
+ return t + 'm ' + r;
+ }
+
+ r = (t % 60) + 'm ' + r;
+ t = Math.floor(t/60);
+
+ if (t < 24) {
+ return t + 'h ' + r;
+ }
+
+ return Math.floor(t/24) + 'd ' + (t % 24) + 'h ' + t;
+ }
+
+ function time_u(t) {
+ var r = "";
+ if (t < 1000) {
+ return t + '&micro;s'
+ }
+
+ r = (t % 1000) + '&micro;s';
+ t = Math.floor(t / 1000);
+ if (t < 1000) {
+ return t + 'ms ' + r;
+ }
+
+ return time_s(Math.floor(t/1000)) + ' ' + (t%1000) + 'ms ' + r;
+ }
+-->
+ </script>
+ </body>
+</html>
diff --git a/sapi/isapi/CREDITS b/sapi/isapi/CREDITS
new file mode 100644
index 0000000..11c6fdc
--- /dev/null
+++ b/sapi/isapi/CREDITS
@@ -0,0 +1,2 @@
+ISAPI
+Andi Gutmans, Zeev Suraski
diff --git a/sapi/isapi/config.m4 b/sapi/isapi/config.m4
new file mode 100644
index 0000000..7c7dcf0
--- /dev/null
+++ b/sapi/isapi/config.m4
@@ -0,0 +1,24 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(isapi, for Zeus ISAPI support,
+[ --with-isapi[=DIR] Build PHP as an ISAPI module for use with Zeus], no, no)
+
+if test "$PHP_ISAPI" != "no"; then
+ if test "$PHP_ISAPI" = "yes"; then
+ ZEUSPATH=/usr/local/zeus # the default
+ else
+ ZEUSPATH=$PHP_ISAPI
+ fi
+ test -f "$ZEUSPATH/web/include/httpext.h" || AC_MSG_ERROR(Unable to find httpext.h in $ZEUSPATH/web/include)
+ PHP_BUILD_THREAD_SAFE
+ AC_DEFINE(WITH_ZEUS, 1, [ ])
+ PHP_ADD_INCLUDE($ZEUSPATH/web/include)
+ PHP_SELECT_SAPI(isapi, shared, php5isapi.c)
+ INSTALL_IT="\$(SHELL) \$(srcdir)/install-sh -m 0755 $SAPI_SHARED \$(INSTALL_ROOT)$ZEUSPATH/web/bin/"
+fi
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/isapi/config.w32 b/sapi/isapi/config.w32
new file mode 100644
index 0000000..8012352
--- /dev/null
+++ b/sapi/isapi/config.w32
@@ -0,0 +1,13 @@
+// vim:ft=javascript
+// $Id$
+
+ARG_ENABLE('isapi', 'Build ISAPI version of PHP', 'no');
+
+if (PHP_ISAPI == "yes") {
+ if (PHP_ZTS == "no") {
+ WARNING("ISAPI module requires an --enable-zts build of PHP");
+ } else {
+ SAPI('isapi', 'php5isapi.c', 'php' + PHP_VERSION + 'isapi.dll', '/D PHP5ISAPI_EXPORTS');
+ ADD_FLAG('LDFLAGS_ISAPI', '/DEF:sapi\\isapi\\php5isapi.def');
+ }
+}
diff --git a/sapi/isapi/php.sym b/sapi/isapi/php.sym
new file mode 100644
index 0000000..34b50b8
--- /dev/null
+++ b/sapi/isapi/php.sym
@@ -0,0 +1,5 @@
+GetFilterVersion
+HttpFilterProc
+GetExtensionVersion
+HttpExtensionProc
+ZSLMain
diff --git a/sapi/isapi/php5isapi.c b/sapi/isapi/php5isapi.c
new file mode 100644
index 0000000..002ad2a
--- /dev/null
+++ b/sapi/isapi/php5isapi.c
@@ -0,0 +1,973 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Zeev Suraski <zeev@zend.com> |
+ | Ben Mansell <ben@zeus.com> (Zeus Support) |
+ +----------------------------------------------------------------------+
+ */
+/* $Id$ */
+
+#include "php.h"
+#include <httpext.h>
+#include <httpfilt.h>
+#include <httpext.h>
+#include "php_main.h"
+#include "SAPI.h"
+#include "php_globals.h"
+#include "ext/standard/info.h"
+#include "php_variables.h"
+#include "php_ini.h"
+
+#ifdef PHP_WIN32
+# include <process.h>
+#else
+# define __try
+# define __except(val)
+# define __declspec(foo)
+#endif
+
+
+#ifdef WITH_ZEUS
+# include "httpext.h"
+# include <errno.h>
+# define GetLastError() errno
+#endif
+
+#ifdef PHP_WIN32
+#define PHP_ENABLE_SEH
+#endif
+
+/*
+uncomment the following lines to turn off
+exception trapping when running under a debugger
+
+#ifdef _DEBUG
+#undef PHP_ENABLE_SEH
+#endif
+*/
+
+#define MAX_STATUS_LENGTH sizeof("xxxx LONGEST POSSIBLE STATUS DESCRIPTION")
+#define ISAPI_SERVER_VAR_BUF_SIZE 1024
+#define ISAPI_POST_DATA_BUF 1024
+
+static zend_bool bFilterLoaded=0;
+static zend_bool bTerminateThreadsOnError=0;
+
+static char *isapi_special_server_variable_names[] = {
+ "ALL_HTTP",
+ "HTTPS",
+#ifndef WITH_ZEUS
+ "SCRIPT_NAME",
+#endif
+ NULL
+};
+
+#define NUM_SPECIAL_VARS (sizeof(isapi_special_server_variable_names)/sizeof(char *))
+#define SPECIAL_VAR_ALL_HTTP 0
+#define SPECIAL_VAR_HTTPS 1
+#define SPECIAL_VAR_PHP_SELF 2
+
+static char *isapi_server_variable_names[] = {
+ "AUTH_PASSWORD",
+ "AUTH_TYPE",
+ "AUTH_USER",
+ "CONTENT_LENGTH",
+ "CONTENT_TYPE",
+ "PATH_TRANSLATED",
+ "QUERY_STRING",
+ "REMOTE_ADDR",
+ "REMOTE_HOST",
+ "REMOTE_USER",
+ "REQUEST_METHOD",
+ "SERVER_NAME",
+ "SERVER_PORT",
+ "SERVER_PROTOCOL",
+ "SERVER_SOFTWARE",
+#ifndef WITH_ZEUS
+ "APPL_MD_PATH",
+ "APPL_PHYSICAL_PATH",
+ "INSTANCE_ID",
+ "INSTANCE_META_PATH",
+ "LOGON_USER",
+ "REQUEST_URI",
+ "URL",
+#else
+ "DOCUMENT_ROOT",
+#endif
+ NULL
+};
+
+
+static char *isapi_secure_server_variable_names[] = {
+ "CERT_COOKIE",
+ "CERT_FLAGS",
+ "CERT_ISSUER",
+ "CERT_KEYSIZE",
+ "CERT_SECRETKEYSIZE",
+ "CERT_SERIALNUMBER",
+ "CERT_SERVER_ISSUER",
+ "CERT_SERVER_SUBJECT",
+ "CERT_SUBJECT",
+ "HTTPS_KEYSIZE",
+ "HTTPS_SECRETKEYSIZE",
+ "HTTPS_SERVER_ISSUER",
+ "HTTPS_SERVER_SUBJECT",
+ "SERVER_PORT_SECURE",
+#ifdef WITH_ZEUS
+ "SSL_CLIENT_CN",
+ "SSL_CLIENT_EMAIL",
+ "SSL_CLIENT_OU",
+ "SSL_CLIENT_O",
+ "SSL_CLIENT_L",
+ "SSL_CLIENT_ST",
+ "SSL_CLIENT_C",
+ "SSL_CLIENT_I_CN",
+ "SSL_CLIENT_I_EMAIL",
+ "SSL_CLIENT_I_OU",
+ "SSL_CLIENT_I_O",
+ "SSL_CLIENT_I_L",
+ "SSL_CLIENT_I_ST",
+ "SSL_CLIENT_I_C",
+#endif
+ NULL
+};
+
+
+static void php_info_isapi(ZEND_MODULE_INFO_FUNC_ARGS)
+{
+ char **p;
+ char variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ DWORD variable_len;
+ char **all_variables[] = {
+ isapi_server_variable_names,
+ isapi_special_server_variable_names,
+ isapi_secure_server_variable_names,
+ NULL
+ };
+ char ***server_variable_names;
+ LPEXTENSION_CONTROL_BLOCK lpECB;
+
+ lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+
+ php_info_print_table_start();
+ php_info_print_table_header(2, "Server Variable", "Value");
+ server_variable_names = all_variables;
+ while (*server_variable_names) {
+ p = *server_variable_names;
+ while (*p) {
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len)
+ && variable_buf[0]) {
+ php_info_print_table_row(2, *p, variable_buf);
+ } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ char *tmp_variable_buf;
+
+ tmp_variable_buf = (char *) emalloc(variable_len);
+ if (lpECB->GetServerVariable(lpECB->ConnID, *p, tmp_variable_buf, &variable_len)
+ && variable_buf[0]) {
+ php_info_print_table_row(2, *p, tmp_variable_buf);
+ }
+ efree(tmp_variable_buf);
+ }
+ p++;
+ }
+ server_variable_names++;
+ }
+ php_info_print_table_end();
+}
+
+
+static zend_module_entry php_isapi_module = {
+ STANDARD_MODULE_HEADER,
+ "ISAPI",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ php_info_isapi,
+ NULL,
+ STANDARD_MODULE_PROPERTIES
+};
+
+
+static int sapi_isapi_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ DWORD num_bytes = str_length;
+ LPEXTENSION_CONTROL_BLOCK ecb;
+
+ ecb = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+ if (ecb->WriteClient(ecb->ConnID, (char *) str, &num_bytes, HSE_IO_SYNC) == FALSE) {
+ php_handle_aborted_connection();
+ }
+ return num_bytes;
+}
+
+
+static int sapi_isapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ return SAPI_HEADER_ADD;
+}
+
+
+
+static void accumulate_header_length(sapi_header_struct *sapi_header, uint *total_length TSRMLS_DC)
+{
+ *total_length += sapi_header->header_len+2;
+}
+
+
+static void concat_header(sapi_header_struct *sapi_header, char **combined_headers_ptr TSRMLS_DC)
+{
+ memcpy(*combined_headers_ptr, sapi_header->header, sapi_header->header_len);
+ *combined_headers_ptr += sapi_header->header_len;
+ **combined_headers_ptr = '\r';
+ (*combined_headers_ptr)++;
+ **combined_headers_ptr = '\n';
+ (*combined_headers_ptr)++;
+}
+
+
+static int sapi_isapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ uint total_length = 2; /* account for the trailing \r\n */
+ char *combined_headers, *combined_headers_ptr;
+ LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+ HSE_SEND_HEADER_EX_INFO header_info;
+ sapi_header_struct default_content_type;
+ char *status_buf = NULL;
+
+ /* Obtain headers length */
+ if (SG(sapi_headers).send_default_content_type) {
+ sapi_get_default_content_type_header(&default_content_type TSRMLS_CC);
+ accumulate_header_length(&default_content_type, (void *) &total_length TSRMLS_CC);
+ }
+ zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) accumulate_header_length, (void *) &total_length TSRMLS_CC);
+
+ /* Generate headers */
+ combined_headers = (char *) emalloc(total_length+1);
+ combined_headers_ptr = combined_headers;
+ if (SG(sapi_headers).send_default_content_type) {
+ concat_header(&default_content_type, (void *) &combined_headers_ptr TSRMLS_CC);
+ sapi_free_header(&default_content_type); /* we no longer need it */
+ }
+ zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) concat_header, (void *) &combined_headers_ptr TSRMLS_CC);
+ *combined_headers_ptr++ = '\r';
+ *combined_headers_ptr++ = '\n';
+ *combined_headers_ptr = 0;
+
+ switch (SG(sapi_headers).http_response_code) {
+ case 200:
+ header_info.pszStatus = "200 OK";
+ break;
+ case 302:
+ header_info.pszStatus = "302 Moved Temporarily";
+ break;
+ case 401:
+ header_info.pszStatus = "401 Authorization Required";
+ break;
+ default: {
+ const char *sline = SG(sapi_headers).http_status_line;
+ int sline_len;
+
+ /* httpd requires that r->status_line is set to the first digit of
+ * the status-code: */
+ if (sline && ((sline_len = strlen(sline)) > 12) && strncmp(sline, "HTTP/1.", 7) == 0 && sline[8] == ' ') {
+ if ((sline_len - 9) > MAX_STATUS_LENGTH) {
+ status_buf = estrndup(sline + 9, MAX_STATUS_LENGTH);
+ } else {
+ status_buf = estrndup(sline + 9, sline_len - 9);
+ }
+ } else {
+ status_buf = emalloc(MAX_STATUS_LENGTH + 1);
+ snprintf(status_buf, MAX_STATUS_LENGTH, "%d Undescribed", SG(sapi_headers).http_response_code);
+ }
+ header_info.pszStatus = status_buf;
+ break;
+ }
+ }
+ header_info.cchStatus = strlen(header_info.pszStatus);
+ header_info.pszHeader = combined_headers;
+ header_info.cchHeader = total_length;
+ header_info.fKeepConn = FALSE;
+ lpECB->dwHttpStatusCode = SG(sapi_headers).http_response_code;
+
+ lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &header_info, NULL, NULL);
+
+ efree(combined_headers);
+ if (status_buf) {
+ efree(status_buf);
+ }
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+
+static int php_isapi_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &php_isapi_module, 1)==FAILURE) {
+ return FAILURE;
+ } else {
+ bTerminateThreadsOnError = (zend_bool) INI_INT("isapi.terminate_threads_on_error");
+ return SUCCESS;
+ }
+}
+
+
+static int sapi_isapi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+ DWORD read_from_buf=0;
+ DWORD read_from_input=0;
+ DWORD total_read=0;
+
+ if ((DWORD) SG(read_post_bytes) < lpECB->cbAvailable) {
+ read_from_buf = MIN(lpECB->cbAvailable-SG(read_post_bytes), count_bytes);
+ memcpy(buffer, lpECB->lpbData+SG(read_post_bytes), read_from_buf);
+ total_read += read_from_buf;
+ }
+ if (read_from_buf<count_bytes
+ && (SG(read_post_bytes)+read_from_buf) < lpECB->cbTotalBytes) {
+ DWORD cbRead=0, cbSize;
+
+ read_from_input = MIN(count_bytes-read_from_buf, lpECB->cbTotalBytes-SG(read_post_bytes)-read_from_buf);
+ while (cbRead < read_from_input) {
+ cbSize = read_from_input - cbRead;
+ if (!lpECB->ReadClient(lpECB->ConnID, buffer+read_from_buf+cbRead, &cbSize) || cbSize==0) {
+ break;
+ }
+ cbRead += cbSize;
+ }
+ total_read += cbRead;
+ }
+ return total_read;
+}
+
+
+static char *sapi_isapi_read_cookies(TSRMLS_D)
+{
+ LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+ char variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+
+ if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_COOKIE", variable_buf, &variable_len)) {
+ return estrndup(variable_buf, variable_len);
+ } else if (GetLastError()==ERROR_INSUFFICIENT_BUFFER) {
+ char *tmp_variable_buf = (char *) emalloc(variable_len+1);
+
+ if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_COOKIE", tmp_variable_buf, &variable_len)) {
+ tmp_variable_buf[variable_len] = 0;
+ return tmp_variable_buf;
+ } else {
+ efree(tmp_variable_buf);
+ }
+ }
+ return STR_EMPTY_ALLOC();
+}
+
+
+#ifdef WITH_ZEUS
+
+static void sapi_isapi_register_zeus_ssl_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
+{
+ char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ char static_cons_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ /*
+ * We need to construct the /C=.../ST=...
+ * DN's for SSL_CLIENT_DN and SSL_CLIENT_I_DN
+ */
+ strcpy( static_cons_buf, "/C=" );
+ if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
+ strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE);
+ }
+ strlcat( static_cons_buf, "/ST=", ISAPI_SERVER_VAR_BUF_SIZE);
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
+ strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
+ }
+ php_register_variable( "SSL_CLIENT_DN", static_cons_buf, track_vars_array TSRMLS_CC );
+
+ strcpy( static_cons_buf, "/C=" );
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_C", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
+ strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
+ }
+ strlcat( static_cons_buf, "/ST=", ISAPI_SERVER_VAR_BUF_SIZE);
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if( lpECB->GetServerVariable( lpECB->ConnID, "SSL_CLIENT_I_ST", static_variable_buf, &variable_len ) && static_variable_buf[0] ) {
+ strlcat( static_cons_buf, static_variable_buf, ISAPI_SERVER_VAR_BUF_SIZE );
+ }
+ php_register_variable( "SSL_CLIENT_I_DN", static_cons_buf, track_vars_array TSRMLS_CC );
+}
+
+static void sapi_isapi_register_zeus_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
+{
+ char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ DWORD pathinfo_len = 0;
+ char *strtok_buf = NULL;
+
+ /* Get SCRIPT_NAME, we use this to work out which bit of the URL
+ * belongs in PHP's version of PATH_INFO
+ */
+ lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len);
+
+ /* Adjust Zeus' version of PATH_INFO, set PHP_SELF,
+ * and generate REQUEST_URI
+ */
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+
+ /* PHP_SELF is just PATH_INFO */
+ php_register_variable( "PHP_SELF", static_variable_buf, track_vars_array TSRMLS_CC );
+
+ /* Chop off filename to get just the 'real' PATH_INFO' */
+ pathinfo_len = variable_len - scriptname_len;
+ php_register_variable( "PATH_INFO", static_variable_buf + scriptname_len - 1, track_vars_array TSRMLS_CC );
+ /* append query string to give url... extra byte for '?' */
+ if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) {
+ /* append query string only if it is present... */
+ if ( strlen(lpECB->lpszQueryString) ) {
+ static_variable_buf[ variable_len - 1 ] = '?';
+ strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString );
+ }
+ php_register_variable( "URL", static_variable_buf, track_vars_array TSRMLS_CC );
+ php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+ }
+
+ /* Get and adjust PATH_TRANSLATED to what PHP wants */
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ static_variable_buf[ variable_len - pathinfo_len - 1 ] = '\0';
+ php_register_variable( "PATH_TRANSLATED", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+
+ /* Bring in the AUTHENTICATION stuff as needed */
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_USER", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ php_register_variable( "PHP_AUTH_USER", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_PASSWORD", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ php_register_variable( "PHP_AUTH_PW", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "AUTH_TYPE", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ php_register_variable( "AUTH_TYPE", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+
+ /* And now, for the SSL variables (if applicable) */
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "CERT_COOKIE", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ sapi_isapi_register_zeus_ssl_variables( lpECB, track_vars_array TSRMLS_CC );
+ }
+ /* Copy some of the variables we need to meet Apache specs */
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "SERVER_SOFTWARE", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ php_register_variable( "SERVER_SIGNATURE", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+}
+#else
+
+static void sapi_isapi_register_iis_variables(LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array TSRMLS_DC)
+{
+ char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ char path_info_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ DWORD scriptname_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ DWORD pathinfo_len = 0;
+ HSE_URL_MAPEX_INFO humi;
+
+ /* Get SCRIPT_NAME, we use this to work out which bit of the URL
+ * belongs in PHP's version of PATH_INFO. SCRIPT_NAME also becomes PHP_SELF.
+ */
+ lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &scriptname_len);
+ php_register_variable("SCRIPT_FILENAME", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
+
+ /* Adjust IIS' version of PATH_INFO, set PHP_SELF,
+ * and generate REQUEST_URI
+ * Get and adjust PATH_TRANSLATED to what PHP wants
+ */
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_INFO", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+
+ /* Chop off filename to get just the 'real' PATH_INFO' */
+ php_register_variable( "ORIG_PATH_INFO", static_variable_buf, track_vars_array TSRMLS_CC );
+ pathinfo_len = variable_len - scriptname_len;
+ strncpy(path_info_buf, static_variable_buf + scriptname_len - 1, sizeof(path_info_buf)-1);
+ php_register_variable( "PATH_INFO", path_info_buf, track_vars_array TSRMLS_CC );
+ /* append query string to give url... extra byte for '?' */
+ if ( strlen(lpECB->lpszQueryString) + variable_len + 1 < ISAPI_SERVER_VAR_BUF_SIZE ) {
+ /* append query string only if it is present... */
+ if ( strlen(lpECB->lpszQueryString) ) {
+ static_variable_buf[ variable_len - 1 ] = '?';
+ strcpy( static_variable_buf + variable_len, lpECB->lpszQueryString );
+ }
+ php_register_variable( "URL", static_variable_buf, track_vars_array TSRMLS_CC );
+ php_register_variable( "REQUEST_URI", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if ( lpECB->GetServerVariable(lpECB->ConnID, "PATH_TRANSLATED", static_variable_buf, &variable_len) && static_variable_buf[0] ) {
+ php_register_variable( "ORIG_PATH_TRANSLATED", static_variable_buf, track_vars_array TSRMLS_CC );
+ }
+ if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, path_info_buf, &pathinfo_len, (LPDWORD) &humi)) {
+ /* Remove trailing \ */
+ if (humi.lpszPath[variable_len-2] == '\\') {
+ humi.lpszPath[variable_len-2] = 0;
+ }
+ php_register_variable("PATH_TRANSLATED", humi.lpszPath, track_vars_array TSRMLS_CC);
+ }
+ }
+
+ static_variable_buf[0] = '/';
+ static_variable_buf[1] = 0;
+ variable_len = 2;
+ if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) {
+ /* Remove trailing \ */
+ if (humi.lpszPath[variable_len-2] == '\\') {
+ humi.lpszPath[variable_len-2] = 0;
+ }
+ php_register_variable("DOCUMENT_ROOT", humi.lpszPath, track_vars_array TSRMLS_CC);
+ }
+
+ if (!SG(request_info).auth_user || !SG(request_info).auth_password ||
+ !SG(request_info).auth_user[0] || !SG(request_info).auth_password[0]) {
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if (lpECB->GetServerVariable(lpECB->ConnID, "HTTP_AUTHORIZATION", static_variable_buf, &variable_len)
+ && static_variable_buf[0]) {
+ php_handle_auth_data(static_variable_buf TSRMLS_CC);
+ }
+ }
+
+ if (SG(request_info).auth_user) {
+ php_register_variable("PHP_AUTH_USER", SG(request_info).auth_user, track_vars_array TSRMLS_CC );
+ }
+ if (SG(request_info).auth_password) {
+ php_register_variable("PHP_AUTH_PW", SG(request_info).auth_password, track_vars_array TSRMLS_CC );
+ }
+}
+#endif
+
+static void sapi_isapi_register_server_variables2(char **server_variables, LPEXTENSION_CONTROL_BLOCK lpECB, zval *track_vars_array, char **recorded_values TSRMLS_DC)
+{
+ char **p=server_variables;
+ DWORD variable_len;
+ char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+ char *variable_buf;
+
+ while (*p) {
+ variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ if (lpECB->GetServerVariable(lpECB->ConnID, *p, static_variable_buf, &variable_len)
+ && static_variable_buf[0]) {
+ php_register_variable(*p, static_variable_buf, track_vars_array TSRMLS_CC);
+ if (recorded_values) {
+ recorded_values[p-server_variables] = estrndup(static_variable_buf, variable_len);
+ }
+ } else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+ variable_buf = (char *) emalloc(variable_len+1);
+ if (lpECB->GetServerVariable(lpECB->ConnID, *p, variable_buf, &variable_len)
+ && variable_buf[0]) {
+ php_register_variable(*p, variable_buf, track_vars_array TSRMLS_CC);
+ }
+ if (recorded_values) {
+ recorded_values[p-server_variables] = variable_buf;
+ } else {
+ efree(variable_buf);
+ }
+ } else { /* for compatibility with Apache SAPIs */
+ php_register_variable(*p, "", track_vars_array TSRMLS_CC);
+ }
+ p++;
+ }
+}
+
+
+static void sapi_isapi_register_server_variables(zval *track_vars_array TSRMLS_DC)
+{
+ DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ char *variable;
+ char *strtok_buf = NULL;
+ char *isapi_special_server_variables[NUM_SPECIAL_VARS];
+ LPEXTENSION_CONTROL_BLOCK lpECB;
+
+ lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+
+ /* Register the special ISAPI variables */
+ memset(isapi_special_server_variables, 0, sizeof(isapi_special_server_variables));
+ sapi_isapi_register_server_variables2(isapi_special_server_variable_names, lpECB, track_vars_array, isapi_special_server_variables TSRMLS_CC);
+ if (SG(request_info).cookie_data) {
+ php_register_variable("HTTP_COOKIE", SG(request_info).cookie_data, track_vars_array TSRMLS_CC);
+ }
+
+ /* Register the standard ISAPI variables */
+ sapi_isapi_register_server_variables2(isapi_server_variable_names, lpECB, track_vars_array, NULL TSRMLS_CC);
+
+ if (isapi_special_server_variables[SPECIAL_VAR_HTTPS]
+ && (atoi(isapi_special_server_variables[SPECIAL_VAR_HTTPS])
+ || !strcasecmp(isapi_special_server_variables[SPECIAL_VAR_HTTPS], "on"))
+ ) {
+ /* Register SSL ISAPI variables */
+ sapi_isapi_register_server_variables2(isapi_secure_server_variable_names, lpECB, track_vars_array, NULL TSRMLS_CC);
+ }
+
+ if (isapi_special_server_variables[SPECIAL_VAR_HTTPS]) {
+ efree(isapi_special_server_variables[SPECIAL_VAR_HTTPS]);
+ }
+
+
+#ifdef WITH_ZEUS
+ sapi_isapi_register_zeus_variables(lpECB, track_vars_array TSRMLS_CC);
+#else
+ sapi_isapi_register_iis_variables(lpECB, track_vars_array TSRMLS_CC);
+#endif
+
+ /* PHP_SELF support */
+ if (isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]) {
+ php_register_variable("PHP_SELF", isapi_special_server_variables[SPECIAL_VAR_PHP_SELF], track_vars_array TSRMLS_CC);
+ efree(isapi_special_server_variables[SPECIAL_VAR_PHP_SELF]);
+ }
+
+ if (isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP]) {
+ /* Register the internal bits of ALL_HTTP */
+ variable = php_strtok_r(isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP], "\r\n", &strtok_buf);
+ while (variable) {
+ char *colon = strchr(variable, ':');
+
+ if (colon) {
+ char *value = colon+1;
+
+ while (*value==' ') {
+ value++;
+ }
+ *colon = 0;
+ php_register_variable(variable, value, track_vars_array TSRMLS_CC);
+ *colon = ':';
+ }
+ variable = php_strtok_r(NULL, "\r\n", &strtok_buf);
+ }
+ efree(isapi_special_server_variables[SPECIAL_VAR_ALL_HTTP]);
+ }
+}
+
+
+static sapi_module_struct isapi_sapi_module = {
+ "isapi", /* name */
+ "ISAPI", /* pretty name */
+
+ php_isapi_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ sapi_isapi_ub_write, /* unbuffered write */
+ NULL, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ sapi_isapi_header_handler, /* header handler */
+ sapi_isapi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ sapi_isapi_read_post, /* read POST data */
+ sapi_isapi_read_cookies, /* read Cookies */
+
+ sapi_isapi_register_server_variables, /* register server variables */
+ NULL, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+
+BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pFilterVersion)
+{
+ bFilterLoaded = 1;
+ pFilterVersion->dwFilterVersion = HTTP_FILTER_REVISION;
+ strcpy(pFilterVersion->lpszFilterDesc, isapi_sapi_module.pretty_name);
+ pFilterVersion->dwFlags= (SF_NOTIFY_AUTHENTICATION | SF_NOTIFY_PREPROC_HEADERS);
+ return TRUE;
+}
+
+
+DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification)
+{
+ TSRMLS_FETCH();
+
+ switch (notificationType) {
+ case SF_NOTIFY_PREPROC_HEADERS:
+ SG(request_info).auth_user = NULL;
+ SG(request_info).auth_password = NULL;
+ SG(request_info).auth_digest = NULL;
+ break;
+ case SF_NOTIFY_AUTHENTICATION: {
+ char *auth_user = ((HTTP_FILTER_AUTHENT *) pvNotification)->pszUser;
+ char *auth_password = ((HTTP_FILTER_AUTHENT *) pvNotification)->pszPassword;
+
+ if (auth_user && auth_user[0]) {
+ SG(request_info).auth_user = estrdup(auth_user);
+ }
+ if (auth_password && auth_password[0]) {
+ SG(request_info).auth_password = estrdup(auth_password);
+ }
+ return SF_STATUS_REQ_HANDLED_NOTIFICATION;
+ }
+ break;
+ }
+ return SF_STATUS_REQ_NEXT_NOTIFICATION;
+}
+
+
+static void init_request_info(LPEXTENSION_CONTROL_BLOCK lpECB TSRMLS_DC)
+{
+ DWORD variable_len = ISAPI_SERVER_VAR_BUF_SIZE;
+ char static_variable_buf[ISAPI_SERVER_VAR_BUF_SIZE];
+#ifndef WITH_ZEUS
+ HSE_URL_MAPEX_INFO humi;
+#endif
+
+ SG(request_info).request_method = lpECB->lpszMethod;
+ SG(request_info).query_string = lpECB->lpszQueryString;
+ SG(request_info).request_uri = lpECB->lpszPathInfo;
+ SG(request_info).content_type = lpECB->lpszContentType;
+ SG(request_info).content_length = lpECB->cbTotalBytes;
+ SG(sapi_headers).http_response_code = 200; /* I think dwHttpStatusCode is invalid at this stage -RL */
+ if (!bFilterLoaded) { /* we don't have valid ISAPI Filter information */
+ SG(request_info).auth_user = SG(request_info).auth_password = SG(request_info).auth_digest = NULL;
+ }
+
+#ifdef WITH_ZEUS
+ /* PATH_TRANSLATED can contain extra PATH_INFO stuff after the
+ * file being loaded, so we must use SCRIPT_FILENAME instead
+ */
+ if(lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_FILENAME", static_variable_buf, &variable_len)) {
+ SG(request_info).path_translated = estrdup(static_variable_buf);
+ } else
+#else
+ /* happily, IIS gives us SCRIPT_NAME which is correct (without PATH_INFO stuff)
+ so we can just map that to the physical path and we have our filename */
+
+ lpECB->GetServerVariable(lpECB->ConnID, "SCRIPT_NAME", static_variable_buf, &variable_len);
+ if (lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_MAP_URL_TO_PATH_EX, static_variable_buf, &variable_len, (LPDWORD) &humi)) {
+ SG(request_info).path_translated = estrdup(humi.lpszPath);
+ } else
+#endif
+ /* if mapping fails, default to what the server tells us */
+ SG(request_info).path_translated = estrdup(lpECB->lpszPathTranslated);
+
+ /* some server configurations allow '..' to slip through in the
+ translated path. We'll just refuse to handle such a path. */
+ if (strstr(SG(request_info).path_translated,"..")) {
+ SG(sapi_headers).http_response_code = 404;
+ efree(SG(request_info).path_translated);
+ SG(request_info).path_translated = NULL;
+ }
+}
+
+
+static void php_isapi_report_exception(char *message, int message_len TSRMLS_DC)
+{
+ if (!SG(headers_sent)) {
+ HSE_SEND_HEADER_EX_INFO header_info;
+ LPEXTENSION_CONTROL_BLOCK lpECB = (LPEXTENSION_CONTROL_BLOCK) SG(server_context);
+
+ header_info.pszStatus = "500 Internal Server Error";
+ header_info.cchStatus = strlen(header_info.pszStatus);
+ header_info.pszHeader = "Content-Type: text/html\r\n\r\n";
+ header_info.cchHeader = strlen(header_info.pszHeader);
+
+ lpECB->dwHttpStatusCode = 500;
+ lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &header_info, NULL, NULL);
+ SG(headers_sent)=1;
+ }
+ sapi_isapi_ub_write(message, message_len TSRMLS_CC);
+}
+
+
+BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
+{
+ pVer->dwExtensionVersion = HSE_VERSION;
+#ifdef WITH_ZEUS
+ strncpy( pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN);
+#else
+ lstrcpyn(pVer->lpszExtensionDesc, isapi_sapi_module.name, HSE_MAX_EXT_DLL_NAME_LEN);
+#endif
+ return TRUE;
+}
+
+
+static void my_endthread()
+{
+#ifdef PHP_WIN32
+ if (bTerminateThreadsOnError) {
+ _endthread();
+ }
+#endif
+}
+
+#ifdef PHP_WIN32
+/* ep is accessible only in the context of the __except expression,
+ * so we have to call this function to obtain it.
+ */
+BOOL exceptionhandler(LPEXCEPTION_POINTERS *e, LPEXCEPTION_POINTERS ep)
+{
+ *e=ep;
+ return TRUE;
+}
+#endif
+
+DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
+{
+ zend_file_handle file_handle;
+ zend_bool stack_overflown=0;
+ int retval = FAILURE;
+#ifdef PHP_ENABLE_SEH
+ LPEXCEPTION_POINTERS e;
+#endif
+ TSRMLS_FETCH();
+
+ zend_first_try {
+#ifdef PHP_ENABLE_SEH
+ __try {
+#endif
+ init_request_info(lpECB TSRMLS_CC);
+ SG(server_context) = lpECB;
+
+ php_request_startup(TSRMLS_C);
+
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.free_filename = 0;
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.opened_path = NULL;
+
+ /* open the script here so we can 404 if it fails */
+ if (file_handle.filename)
+ retval = php_fopen_primary_script(&file_handle TSRMLS_CC);
+
+ if (!file_handle.filename || retval == FAILURE) {
+ SG(sapi_headers).http_response_code = 404;
+ PUTS("No input file specified.\n");
+ } else {
+ php_execute_script(&file_handle TSRMLS_CC);
+ }
+
+ if (SG(request_info).cookie_data) {
+ efree(SG(request_info).cookie_data);
+ }
+ if (SG(request_info).path_translated)
+ efree(SG(request_info).path_translated);
+#ifdef PHP_ENABLE_SEH
+ } __except(exceptionhandler(&e, GetExceptionInformation())) {
+ char buf[1024];
+ if (_exception_code()==EXCEPTION_STACK_OVERFLOW) {
+ LPBYTE lpPage;
+ static SYSTEM_INFO si;
+ static MEMORY_BASIC_INFORMATION mi;
+ static DWORD dwOldProtect;
+
+ GetSystemInfo(&si);
+
+ /* Get page ESP is pointing to */
+ _asm mov lpPage, esp;
+
+ /* Get stack allocation base */
+ VirtualQuery(lpPage, &mi, sizeof(mi));
+
+ /* Go to the page below the current page */
+ lpPage = (LPBYTE) (mi.BaseAddress) - si.dwPageSize;
+
+ /* Free pages below current page */
+ if (!VirtualFree(mi.AllocationBase, (LPBYTE)lpPage - (LPBYTE) mi.AllocationBase, MEM_DECOMMIT)) {
+ _endthread();
+ }
+
+ /* Restore the guard page */
+ if (!VirtualProtect(lpPage, si.dwPageSize, PAGE_GUARD | PAGE_READWRITE, &dwOldProtect)) {
+ _endthread();
+ }
+
+ CG(unclean_shutdown)=1;
+ _snprintf(buf, sizeof(buf)-1,"PHP has encountered a Stack overflow");
+ php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
+ } else if (_exception_code()==EXCEPTION_ACCESS_VIOLATION) {
+ _snprintf(buf, sizeof(buf)-1,"PHP has encountered an Access Violation at %p", e->ExceptionRecord->ExceptionAddress);
+ php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
+ my_endthread();
+ } else {
+ _snprintf(buf, sizeof(buf)-1,"PHP has encountered an Unhandled Exception Code %d at %p", e->ExceptionRecord->ExceptionCode , e->ExceptionRecord->ExceptionAddress);
+ php_isapi_report_exception(buf, strlen(buf) TSRMLS_CC);
+ my_endthread();
+ }
+ }
+#endif
+#ifdef PHP_ENABLE_SEH
+ __try {
+ php_request_shutdown(NULL);
+ } __except(EXCEPTION_EXECUTE_HANDLER) {
+ my_endthread();
+ }
+#else
+ php_request_shutdown(NULL);
+#endif
+ } zend_catch {
+ zend_try {
+ php_request_shutdown(NULL);
+ } zend_end_try();
+ return HSE_STATUS_ERROR;
+ } zend_end_try();
+
+ return HSE_STATUS_SUCCESS;
+}
+
+
+
+__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+#ifdef WITH_ZEUS
+ tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "TSRM.log");
+#else
+ tsrm_startup(128, 1, TSRM_ERROR_LEVEL_CORE, "C:\\TSRM.log");
+#endif
+ sapi_startup(&isapi_sapi_module);
+ if (isapi_sapi_module.startup) {
+ isapi_sapi_module.startup(&sapi_module);
+ }
+ break;
+ case DLL_THREAD_ATTACH:
+ break;
+ case DLL_THREAD_DETACH:
+ ts_free_thread();
+ break;
+ case DLL_PROCESS_DETACH:
+ if (isapi_sapi_module.shutdown) {
+ isapi_sapi_module.shutdown(&sapi_module);
+ }
+ sapi_shutdown();
+ tsrm_shutdown();
+ break;
+ }
+ return TRUE;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/sapi/isapi/php5isapi.def b/sapi/isapi/php5isapi.def
new file mode 100644
index 0000000..596023e
--- /dev/null
+++ b/sapi/isapi/php5isapi.def
@@ -0,0 +1,5 @@
+EXPORTS
+HttpFilterProc
+GetFilterVersion
+HttpExtensionProc
+GetExtensionVersion
diff --git a/sapi/isapi/php5isapi.dsp b/sapi/isapi/php5isapi.dsp
new file mode 100644
index 0000000..3dbab11
--- /dev/null
+++ b/sapi/isapi/php5isapi.dsp
@@ -0,0 +1,165 @@
+# Microsoft Developer Studio Project File - Name="php5isapi" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=php5isapi - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "php5isapi.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "php5isapi.mak" CFG="php5isapi - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "php5isapi - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5isapi - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5isapi - Win32 Release_TS_inline" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5isapi - Win32 Release_TSDbg" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "php5isapi - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\..\Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "_DEBUG" /D "COMPILE_LIBZEND" /D ZEND_DEBUG=1 /D "_WINDOWS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "_DEBUG"
+# ADD RSC /l 0x40d /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 wsock32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts_debug.lib /nologo /version:4.0 /dll /debug /machine:I386 /nodefaultlib:"libcmt" /pdbtype:sept /libpath:"..\..\Debug_TS"
+
+!ELSEIF "$(CFG)" == "php5isapi - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "NDEBUG"
+# ADD RSC /l 0x40d /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\Release_TS"
+
+!ELSEIF "$(CFG)" == "php5isapi - Win32 Release_TS_inline"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "php5isapi___Win32_Release_TS_inline"
+# PROP BASE Intermediate_Dir "php5isapi___Win32_Release_TS_inline"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS_inline"
+# PROP Intermediate_Dir "Release_TS_inline"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /D "NDEBUG" /D "_WINDOWS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "WIN32" /D "_MBCS" /D ZEND_DEBUG=0 /FR /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "ZEND_WIN32_FORCE_INLINE" /D "_WINDOWS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "NDEBUG"
+# ADD RSC /l 0x40d /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts.lib /nologo /dll /machine:I386 /libpath:"..\..\Release_TS"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\Release_TS_inline"
+
+!ELSEIF "$(CFG)" == "php5isapi - Win32 Release_TSDbg"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "php5isapi___Win32_Release_TSDbg"
+# PROP BASE Intermediate_Dir "php5isapi___Win32_Release_TSDbg"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TSDbg"
+# PROP Intermediate_Dir "Release_TSDbg"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /Zi /Od /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP5ISAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "NDEBUG"
+# ADD RSC /l 0x40d /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts.lib /nologo /version:4.0 /dll /machine:I386 /libpath:"..\..\Release_TS"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib php5ts.lib /nologo /version:4.0 /dll /debug /machine:I386 /libpath:"..\..\Release_TSDbg"
+
+!ENDIF
+
+# Begin Target
+
+# Name "php5isapi - Win32 Debug_TS"
+# Name "php5isapi - Win32 Release_TS"
+# Name "php5isapi - Win32 Release_TS_inline"
+# Name "php5isapi - Win32 Release_TSDbg"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\php5isapi.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\php5isapi.def
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# End Target
+# End Project
diff --git a/sapi/isapi/stresstest/getopt.c b/sapi/isapi/stresstest/getopt.c
new file mode 100644
index 0000000..57faa0f
--- /dev/null
+++ b/sapi/isapi/stresstest/getopt.c
@@ -0,0 +1,175 @@
+/* Borrowed from Apache NT Port */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "getopt.h"
+#define OPTERRCOLON (1)
+#define OPTERRNF (2)
+#define OPTERRARG (3)
+
+
+char *ap_optarg;
+int ap_optind = 1;
+static int ap_opterr = 1;
+static int ap_optopt;
+
+static int
+ap_optiserr(int argc, char * const *argv, int oint, const char *optstr,
+ int optchr, int err)
+{
+ if (ap_opterr)
+ {
+ fprintf(stderr, "Error in argument %d, char %d: ", oint, optchr+1);
+ switch(err)
+ {
+ case OPTERRCOLON:
+ fprintf(stderr, ": in flags\n");
+ break;
+ case OPTERRNF:
+ fprintf(stderr, "option not found %c\n", argv[oint][optchr]);
+ break;
+ case OPTERRARG:
+ fprintf(stderr, "no argument for option %c\n", argv[oint][optchr]);
+ break;
+ default:
+ fprintf(stderr, "unknown\n");
+ break;
+ }
+ }
+ ap_optopt = argv[oint][optchr];
+ return('?');
+}
+
+int ap_getopt(int argc, char* const *argv, const char *optstr)
+{
+ static int optchr = 0;
+ static int dash = 0; /* have already seen the - */
+
+ char *cp;
+
+ if (ap_optind >= argc)
+ return(EOF);
+ if (!dash && (argv[ap_optind][0] != '-'))
+ return(EOF);
+ if (!dash && (argv[ap_optind][0] == '-') && !argv[ap_optind][1])
+ {
+ /*
+ * use to specify stdin. Need to let pgm process this and
+ * the following args
+ */
+ return(EOF);
+ }
+ if ((argv[ap_optind][0] == '-') && (argv[ap_optind][1] == '-'))
+ {
+ /* -- indicates end of args */
+ ap_optind++;
+ return(EOF);
+ }
+ if (!dash)
+ {
+ assert((argv[ap_optind][0] == '-') && argv[ap_optind][1]);
+ dash = 1;
+ optchr = 1;
+ }
+
+ /* Check if the guy tries to do a -: kind of flag */
+ assert(dash);
+ if (argv[ap_optind][optchr] == ':')
+ {
+ dash = 0;
+ ap_optind++;
+ return(ap_optiserr(argc, argv, ap_optind-1, optstr, optchr, OPTERRCOLON));
+ }
+ if (!(cp = strchr(optstr, argv[ap_optind][optchr])))
+ {
+ int errind = ap_optind;
+ int errchr = optchr;
+
+ if (!argv[ap_optind][optchr+1])
+ {
+ dash = 0;
+ ap_optind++;
+ }
+ else
+ optchr++;
+ return(ap_optiserr(argc, argv, errind, optstr, errchr, OPTERRNF));
+ }
+ if (cp[1] == ':')
+ {
+ /* Check for cases where the value of the argument
+ is in the form -<arg> <val> or in the form -<arg><val> */
+ dash = 0;
+ if(!argv[ap_optind][2]) {
+ ap_optind++;
+ if (ap_optind == argc)
+ return(ap_optiserr(argc, argv, ap_optind-1, optstr, optchr, OPTERRARG));
+ ap_optarg = argv[ap_optind++];
+ }
+ else
+ {
+ ap_optarg = &argv[ap_optind][2];
+ ap_optind++;
+ }
+ return(*cp);
+ }
+ else
+ {
+ if (!argv[ap_optind][optchr+1])
+ {
+ dash = 0;
+ ap_optind++;
+ }
+ else
+ optchr++;
+ return(*cp);
+ }
+ assert(0);
+ return(0);
+}
+
+#ifdef TESTGETOPT
+int
+ main (int argc, char **argv)
+ {
+ int c;
+ extern char *ap_optarg;
+ extern int ap_optind;
+ int aflg = 0;
+ int bflg = 0;
+ int errflg = 0;
+ char *ofile = NULL;
+
+ while ((c = ap_getopt(argc, argv, "abo:")) != EOF)
+ switch (c) {
+ case 'a':
+ if (bflg)
+ errflg++;
+ else
+ aflg++;
+ break;
+ case 'b':
+ if (aflg)
+ errflg++;
+ else
+ bflg++;
+ break;
+ case 'o':
+ ofile = ap_optarg;
+ (void)printf("ofile = %s\n", ofile);
+ break;
+ case '?':
+ errflg++;
+ }
+ if (errflg) {
+ (void)fprintf(stderr,
+ "usage: cmd [-a|-b] [-o <filename>] files...\n");
+ exit (2);
+ }
+ for ( ; ap_optind < argc; ap_optind++)
+ (void)printf("%s\n", argv[ap_optind]);
+ return 0;
+ }
+
+#endif /* TESTGETOPT */
diff --git a/sapi/isapi/stresstest/getopt.h b/sapi/isapi/stresstest/getopt.h
new file mode 100644
index 0000000..a3e278e
--- /dev/null
+++ b/sapi/isapi/stresstest/getopt.h
@@ -0,0 +1,12 @@
+/* Borrowed from Apache NT Port */
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern char *ap_optarg;
+extern int ap_optind;
+
+int ap_getopt(int argc, char* const *argv, const char *optstr);
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/sapi/isapi/stresstest/notes.txt b/sapi/isapi/stresstest/notes.txt
new file mode 100644
index 0000000..f58ab3c
--- /dev/null
+++ b/sapi/isapi/stresstest/notes.txt
@@ -0,0 +1,56 @@
+This stress test program is for debugging threading issues with the ISAPI
+module.
+
+2 ways to use it:
+
+1: test any php script file on multiple threads
+2: run the php test scripts bundled with the source code
+
+
+
+GLOBAL SETTINGS
+===============
+
+If you need to set special environement variables, in addition to your
+regular environment, create a file that contains them, one setting per line:
+
+MY_ENV_VAR=XXXXXXXX
+
+This can be used to simulate ISAPI environment variables if need be.
+
+By default, stress test uses 10 threads. To change this, change the define
+NUM_THREADS in stresstest.cpp.
+
+
+
+1: Test any php script file on multiple threads
+===============================================
+
+Create a file that contains a list of php script files, one per line. If
+you need to provide input, place the GET data, or Query String, after the
+filename. File contents would look like:
+
+e:\inetpub\pages\index.php
+e:\inetpub\pages\info.php
+e:\inetpub\pages\test.php a=1&b=2
+
+Run: stresstest L files.txt
+
+
+
+2: Run the php test scripts bundled with the source code
+========================================================
+
+supply the path to the parent of the "tests" directory (expect a couple
+long pauses for a couple of the larger tests)
+
+Run: stresstest T c:\php5-source
+
+
+
+TODO:
+
+* Make more options configurable: number of threads, iterations, etc.
+* Improve stdout output to make it more useful
+* Implement support for SKIPIF
+* Improve speed of CompareFile function (too slow on big files).
diff --git a/sapi/isapi/stresstest/stresstest.cpp b/sapi/isapi/stresstest/stresstest.cpp
new file mode 100644
index 0000000..97824e6
--- /dev/null
+++ b/sapi/isapi/stresstest/stresstest.cpp
@@ -0,0 +1,936 @@
+/*
+ * ======================================================================= *
+ * File: stress .c *
+ * stress tester for isapi dll's *
+ * based on cgiwrap *
+ * ======================================================================= *
+ *
+*/
+#define WIN32_LEAN_AND_MEAN
+#include <afx.h>
+#include <afxtempl.h>
+#include <winbase.h>
+#include <winerror.h>
+#include <httpext.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "getopt.h"
+
+// These are things that go out in the Response Header
+//
+#define HTTP_VER "HTTP/1.0"
+#define SERVER_VERSION "Http-Srv-Beta2/1.0"
+
+//
+// Simple wrappers for the heap APIS
+//
+#define xmalloc(s) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (s))
+#define xfree(s) HeapFree(GetProcessHeap(), 0, (s))
+
+//
+// The mandatory exports from the ISAPI DLL
+//
+DWORD numThreads = 1;
+DWORD iterations = 1;
+
+HANDLE StartNow;
+// quick and dirty environment
+typedef CMapStringToString TEnvironment;
+TEnvironment IsapiEnvironment;
+
+typedef struct _TResults {
+ LONG ok;
+ LONG bad;
+} TResults;
+
+CStringArray IsapiFileList; // list of filenames
+CStringArray TestNames; // --TEST--
+CStringArray IsapiGetData; // --GET--
+CStringArray IsapiPostData; // --POST--
+CStringArray IsapiMatchData; // --EXPECT--
+CArray<TResults, TResults> Results;
+
+typedef struct _TIsapiContext {
+ HANDLE in;
+ HANDLE out;
+ DWORD tid;
+ TEnvironment env;
+ HANDLE waitEvent;
+} TIsapiContext;
+
+//
+// Prototypes of the functions this sample implements
+//
+extern "C" {
+HINSTANCE hDll;
+typedef BOOL (WINAPI *VersionProc)(HSE_VERSION_INFO *) ;
+typedef DWORD (WINAPI *HttpExtProc)(EXTENSION_CONTROL_BLOCK *);
+typedef BOOL (WINAPI *TerminateProc) (DWORD);
+BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *, TIsapiContext *) ;
+BOOL WINAPI GetServerVariable(HCONN, LPSTR, LPVOID, LPDWORD );
+BOOL WINAPI ReadClient(HCONN, LPVOID, LPDWORD);
+BOOL WINAPI WriteClient(HCONN, LPVOID, LPDWORD, DWORD);
+BOOL WINAPI ServerSupportFunction(HCONN, DWORD, LPVOID, LPDWORD, LPDWORD);
+VersionProc IsapiGetExtensionVersion;
+HttpExtProc IsapiHttpExtensionProc;
+TerminateProc TerminateExtensionProc;
+HSE_VERSION_INFO version_info;
+}
+
+char * MakeDateStr(VOID);
+char * GetEnv(char *);
+
+
+
+
+DWORD CALLBACK IsapiThread(void *);
+int stress_main(const char *filename,
+ const char *arg,
+ const char *postfile,
+ const char *matchdata);
+
+
+
+BOOL bUseTestFiles = FALSE;
+char temppath[MAX_PATH];
+
+void stripcrlf(char *line)
+{
+ DWORD l = strlen(line)-1;
+ if (line[l]==10 || line[l]==13) line[l]=0;
+ l = strlen(line)-1;
+ if (line[l]==10 || line[l]==13) line[l]=0;
+}
+
+#define COMPARE_BUF_SIZE 1024
+
+BOOL CompareFiles(const char*f1, const char*f2)
+{
+ FILE *fp1, *fp2;
+ bool retval;
+ char buf1[COMPARE_BUF_SIZE], buf2[COMPARE_BUF_SIZE];
+ int length1, length2;
+
+ if ((fp1=fopen(f1, "r"))==NULL) {
+ return FALSE;
+ }
+
+ if ((fp2=fopen(f2, "r"))==NULL) {
+ fclose(fp1);
+ return FALSE;
+ }
+
+ retval = TRUE; // success oriented
+ while (true) {
+ length1 = fread(buf1, 1, sizeof(buf1), fp1);
+ length2 = fread(buf2, 1, sizeof(buf2), fp2);
+
+ // check for end of file
+ if (feof(fp1)) {
+ if (!feof(fp2)) {
+ retval = FALSE;
+ }
+ break;
+ } else if (feof(fp2)) {
+ if (!feof(fp1)) {
+ retval = FALSE;
+ }
+ break;
+ }
+
+ // compare data
+ if (length1!=length2
+ || memcmp(buf1, buf2, length1)!=0) {
+ retval = FALSE;
+ break;
+ }
+ }
+ fclose(fp1);
+ fclose(fp2);
+
+ return retval;
+}
+
+
+BOOL CompareStringWithFile(const char *filename, const char *str, unsigned int str_length)
+{
+ FILE *fp;
+ bool retval;
+ char buf[COMPARE_BUF_SIZE];
+ unsigned int offset=0, readbytes;
+ fprintf(stderr, "test %s\n",filename);
+ if ((fp=fopen(filename, "rb"))==NULL) {
+ fprintf(stderr, "Error opening %s\n",filename);
+ return FALSE;
+ }
+
+ retval = TRUE; // success oriented
+ while (true) {
+ readbytes = fread(buf, 1, sizeof(buf), fp);
+
+ // check for end of file
+
+ if (offset+readbytes > str_length
+ || memcmp(buf, str+offset, readbytes)!=NULL) {
+ fprintf(stderr, "File missmatch %s\n",filename);
+ retval = FALSE;
+ break;
+ }
+ if (feof(fp)) {
+ if (!retval) fprintf(stderr, "File zero length %s\n",filename);
+ break;
+ }
+ }
+ fclose(fp);
+
+ return retval;
+}
+
+
+BOOL ReadGlobalEnvironment(const char *environment)
+{
+ if (environment) {
+ FILE *fp = fopen(environment, "r");
+ DWORD i=0;
+ if (fp) {
+ char line[2048];
+ while (fgets(line, sizeof(line)-1, fp)) {
+ // file.php arg1 arg2 etc.
+ char *p = strchr(line, '=');
+ if (p) {
+ *p=0;
+ IsapiEnvironment[line]=p+1;
+ }
+ }
+ fclose(fp);
+ return IsapiEnvironment.GetCount() > 0;
+ }
+ }
+ return FALSE;
+}
+
+BOOL ReadFileList(const char *filelist)
+{
+ FILE *fp = fopen(filelist, "r");
+ if (!fp) {
+ printf("Unable to open %s\r\n", filelist);
+ }
+ char line[2048];
+ int i=0;
+ while (fgets(line, sizeof(line)-1, fp)) {
+ // file.php arg1 arg2 etc.
+ stripcrlf(line);
+ if (strlen(line)>3) {
+ char *p = strchr(line, ' ');
+ if (p) {
+ *p = 0;
+ // get file
+
+ IsapiFileList.Add(line);
+ IsapiGetData.Add(p+1);
+ } else {
+ // just a filename is all
+ IsapiFileList.Add(line);
+ IsapiGetData.Add("");
+ }
+ }
+
+ // future use
+ IsapiPostData.Add("");
+ IsapiMatchData.Add("");
+ TestNames.Add("");
+
+ i++;
+ }
+ Results.SetSize(TestNames.GetSize());
+
+ fclose(fp);
+ return IsapiFileList.GetSize() > 0;
+}
+
+void DoThreads() {
+
+ if (IsapiFileList.GetSize() == 0) {
+ printf("No Files to test\n");
+ return;
+ }
+
+ printf("Starting Threads...\n");
+ // loop creating threads
+ DWORD tid;
+ HANDLE *threads = new HANDLE[numThreads];
+ DWORD i;
+ for (i=0; i< numThreads; i++) {
+ threads[i]=CreateThread(NULL, 0, IsapiThread, NULL, CREATE_SUSPENDED, &tid);
+ }
+ for (i=0; i< numThreads; i++) {
+ if (threads[i]) ResumeThread(threads[i]);
+ }
+ // wait for threads to finish
+ WaitForMultipleObjects(numThreads, threads, TRUE, INFINITE);
+ for (i=0; i< numThreads; i++) {
+ CloseHandle(threads[i]);
+ }
+ delete [] threads;
+}
+
+void DoFileList(const char *filelist, const char *environment)
+{
+ // read config files
+
+ if (!ReadFileList(filelist)) {
+ printf("No Files to test!\r\n");
+ return;
+ }
+
+ ReadGlobalEnvironment(environment);
+
+ DoThreads();
+}
+
+
+/**
+ * ParseTestFile
+ * parse a single phpt file and add it to the arrays
+ */
+BOOL ParseTestFile(const char *path, const char *fn)
+{
+ // parse the test file
+ char filename[MAX_PATH];
+ _snprintf(filename, sizeof(filename)-1, "%s\\%s", path, fn);
+ char line[1024];
+ memset(line, 0, sizeof(line));
+ CString cTest, cSkipIf, cPost, cGet, cFile, cExpect;
+ printf("Reading %s\r\n", filename);
+
+ enum state {none, test, skipif, post, get, file, expect} parsestate = none;
+
+ FILE *fp = fopen(filename, "rb");
+ char *tn = _tempnam(temppath,"pht.");
+ char *en = _tempnam(temppath,"exp.");
+ FILE *ft = fopen(tn, "wb+");
+ FILE *fe = fopen(en, "wb+");
+ if (fp && ft && fe) {
+ while (fgets(line, sizeof(line)-1, fp)) {
+ if (line[0]=='-') {
+ if (_strnicmp(line, "--TEST--", 8)==0) {
+ parsestate = test;
+ continue;
+ } else if (_strnicmp(line, "--SKIPIF--", 10)==0) {
+ parsestate = skipif;
+ continue;
+ } else if (_strnicmp(line, "--POST--", 8)==0) {
+ parsestate = post;
+ continue;
+ } else if (_strnicmp(line, "--GET--", 7)==0) {
+ parsestate = get;
+ continue;
+ } else if (_strnicmp(line, "--FILE--", 8)==0) {
+ parsestate = file;
+ continue;
+ } else if (_strnicmp(line, "--EXPECT--", 10)==0) {
+ parsestate = expect;
+ continue;
+ }
+ }
+ switch (parsestate) {
+ case test:
+ stripcrlf(line);
+ cTest = line;
+ break;
+ case skipif:
+ cSkipIf += line;
+ break;
+ case post:
+ cPost += line;
+ break;
+ case get:
+ cGet += line;
+ break;
+ case file:
+ fputs(line, ft);
+ break;
+ case expect:
+ fputs(line, fe);
+ break;
+ }
+ }
+
+ fclose(fp);
+ fclose(ft);
+ fclose(fe);
+
+ if (!cTest.IsEmpty()) {
+ IsapiFileList.Add(tn);
+ TestNames.Add(cTest);
+ IsapiGetData.Add(cGet);
+ IsapiPostData.Add(cPost);
+ IsapiMatchData.Add(en);
+ free(tn);
+ free(en);
+ return TRUE;
+ }
+ }
+ free(tn);
+ free(en);
+ return FALSE;
+}
+
+
+/**
+ * GetTestFiles
+ * Recurse through the path and subdirectories, parse each phpt file
+ */
+BOOL GetTestFiles(const char *path)
+{
+ // find all files .phpt under testpath\tests
+ char FindPath[MAX_PATH];
+ WIN32_FIND_DATA fd;
+ memset(&fd, 0, sizeof(WIN32_FIND_DATA));
+
+ _snprintf(FindPath, sizeof(FindPath)-1, "%s\\*.*", path);
+ HANDLE fh = FindFirstFile(FindPath, &fd);
+ if (fh != INVALID_HANDLE_VALUE) {
+ do {
+ if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ !strchr(fd.cFileName, '.')) {
+ // subdirectory, recurse into it
+ char NewFindPath[MAX_PATH];
+ _snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s", path, fd.cFileName);
+ GetTestFiles(NewFindPath);
+ } else if (strstr(fd.cFileName, ".phpt")) {
+ // got test file, parse it now
+ if (ParseTestFile(path, fd.cFileName)) {
+ printf("Test File Added: %s\\%s\r\n", path, fd.cFileName);
+ }
+ }
+ memset(&fd, 0, sizeof(WIN32_FIND_DATA));
+ } while (FindNextFile(fh, &fd) != 0);
+ FindClose(fh);
+ }
+ return IsapiFileList.GetSize() > 0;
+}
+
+void DeleteTempFiles(const char *mask)
+{
+ char FindPath[MAX_PATH];
+ WIN32_FIND_DATA fd;
+ memset(&fd, 0, sizeof(WIN32_FIND_DATA));
+
+ _snprintf(FindPath, sizeof(FindPath)-1, "%s\\%s", temppath, mask);
+ HANDLE fh = FindFirstFile(FindPath, &fd);
+ if (fh != INVALID_HANDLE_VALUE) {
+ do {
+ char NewFindPath[MAX_PATH];
+ _snprintf(NewFindPath, sizeof(NewFindPath)-1, "%s\\%s", temppath, fd.cFileName);
+ DeleteFile(NewFindPath);
+ memset(&fd, 0, sizeof(WIN32_FIND_DATA));
+ } while (FindNextFile(fh, &fd) != 0);
+ FindClose(fh);
+ }
+}
+
+void DoTestFiles(const char *filelist, const char *environment)
+{
+ if (!GetTestFiles(filelist)) {
+ printf("No Files to test!\r\n");
+ return;
+ }
+
+ Results.SetSize(IsapiFileList.GetSize());
+
+ ReadGlobalEnvironment(environment);
+
+ DoThreads();
+
+ printf("\r\nRESULTS:\r\n");
+ // show results:
+ DWORD r = Results.GetSize();
+ for (DWORD i=0; i< r; i++) {
+ TResults result = Results.GetAt(i);
+ printf("%s\r\nOK: %d FAILED: %d\r\n", TestNames.GetAt(i), result.ok, result.bad);
+ }
+
+ // delete temp files
+ printf("Deleting Temp Files\r\n");
+ DeleteTempFiles("exp.*");
+ DeleteTempFiles("pht.*");
+ printf("Done\r\n");
+}
+
+#define OPTSTRING "m:f:d:h:t:i:"
+static void _usage(char *argv0)
+{
+ char *prog;
+
+ prog = strrchr(argv0, '/');
+ if (prog) {
+ prog++;
+ } else {
+ prog = "stresstest";
+ }
+
+ printf("Usage: %s -m <isapi.dll> -d|-l <file> [-t <numthreads>] [-i <numiterations>]\n"
+ " -m path to isapi dll\n"
+ " -d <directory> php directory (to run php test files).\n"
+ " -f <file> file containing list of files to run\n"
+ " -t number of threads to use (default=1)\n"
+ " -i number of iterations per thread (default=1)\n"
+ " -h This help\n", prog);
+}
+int main(int argc, char* argv[])
+{
+ LPVOID lpMsgBuf;
+ char *filelist=NULL, *environment=NULL, *module=NULL;
+ int c = NULL;
+ while ((c=ap_getopt(argc, argv, OPTSTRING))!=-1) {
+ switch (c) {
+ case 'd':
+ bUseTestFiles = TRUE;
+ filelist = strdup(ap_optarg);
+ break;
+ case 'f':
+ bUseTestFiles = FALSE;
+ filelist = strdup(ap_optarg);
+ break;
+ case 'e':
+ environment = strdup(ap_optarg);
+ break;
+ case 't':
+ numThreads = atoi(ap_optarg);
+ break;
+ case 'i':
+ iterations = atoi(ap_optarg);
+ break;
+ case 'm':
+ module = strdup(ap_optarg);
+ break;
+ case 'h':
+ _usage(argv[0]);
+ exit(0);
+ break;
+ }
+ }
+ if (!module || !filelist) {
+ _usage(argv[0]);
+ exit(0);
+ }
+
+ GetTempPath(sizeof(temppath), temppath);
+ hDll = LoadLibrary(module); // Load our DLL
+
+ if (!hDll) {
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+ fprintf(stderr,"Error: Dll 'php5isapi.dll' not found -%d\n%s\n", GetLastError(), lpMsgBuf);
+ free (module);
+ free(filelist);
+ LocalFree( lpMsgBuf );
+ return -1;
+ }
+
+ //
+ // Find the exported functions
+
+ IsapiGetExtensionVersion = (VersionProc)GetProcAddress(hDll,"GetExtensionVersion");
+ if (!IsapiGetExtensionVersion) {
+ fprintf(stderr,"Can't Get Extension Version %d\n", GetLastError());
+ free (module);
+ free(filelist);
+ return -1;
+ }
+ IsapiHttpExtensionProc = (HttpExtProc)GetProcAddress(hDll,"HttpExtensionProc");
+ if (!IsapiHttpExtensionProc) {
+ fprintf(stderr,"Can't Get Extension proc %d\n", GetLastError());
+ free (module);
+ free(filelist);
+ return -1;
+ }
+ TerminateExtensionProc = (TerminateProc) GetProcAddress(hDll,
+ "TerminateExtension");
+
+ // This should really check if the version information matches what we
+ // expect.
+ //
+ if (!IsapiGetExtensionVersion(&version_info) ) {
+ fprintf(stderr,"Fatal: GetExtensionVersion failed\n");
+ free (module);
+ free(filelist);
+ return -1;
+ }
+
+ if (bUseTestFiles) {
+ char TestPath[MAX_PATH];
+ if (filelist != NULL)
+ _snprintf(TestPath, sizeof(TestPath)-1, "%s\\tests", filelist);
+ else strcpy(TestPath, "tests");
+ DoTestFiles(TestPath, environment);
+ } else {
+ DoFileList(filelist, environment);
+ }
+
+ // cleanup
+ if (TerminateExtensionProc) TerminateExtensionProc(0);
+
+ // We should really free memory (e.g., from GetEnv), but we'll be dead
+ // soon enough
+
+ FreeLibrary(hDll);
+ free (module);
+ free(filelist);
+ return 0;
+}
+
+
+DWORD CALLBACK IsapiThread(void *p)
+{
+ DWORD filecount = IsapiFileList.GetSize();
+
+ for (DWORD j=0; j<iterations; j++) {
+ for (DWORD i=0; i<filecount; i++) {
+ // execute each file
+ CString testname = TestNames.GetAt(i);
+ BOOL ok = FALSE;
+ if (stress_main(IsapiFileList.GetAt(i),
+ IsapiGetData.GetAt(i),
+ IsapiPostData.GetAt(i),
+ IsapiMatchData.GetAt(i))) {
+ InterlockedIncrement(&Results[i].ok);
+ ok = TRUE;
+ } else {
+ InterlockedIncrement(&Results[i].bad);
+ ok = FALSE;
+ }
+
+ if (testname.IsEmpty()) {
+ printf("Thread %d File %s\n", GetCurrentThreadId(), IsapiFileList.GetAt(i));
+ } else {
+ printf("tid %d: %s %s\n", GetCurrentThreadId(), testname, ok?"OK":"FAIL");
+ }
+ Sleep(10);
+ }
+ }
+ printf("Thread ending...\n");
+ return 0;
+}
+
+/*
+ * ======================================================================= *
+ * In the startup of this program, we look at our executable name and *
+ * replace the ".EXE" with ".DLL" to find the ISAPI DLL we need to load. *
+ * This means that the executable need only be given the same "name" as *
+ * the DLL to load. There is no recompilation required. *
+ * ======================================================================= *
+*/
+BOOL stress_main(const char *filename,
+ const char *arg,
+ const char *postdata,
+ const char *matchdata)
+{
+
+ EXTENSION_CONTROL_BLOCK ECB;
+ DWORD rc;
+ TIsapiContext context;
+
+ // open output and input files
+ context.tid = GetCurrentThreadId();
+ CString fname;
+ fname.Format("%08X.out", context.tid);
+
+ context.out = CreateFile(fname, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, NULL);
+ if (context.out==INVALID_HANDLE_VALUE) {
+ printf("failed to open output file %s\n", fname);
+ return 0;
+ }
+
+ // not using post files
+ context.in = INVALID_HANDLE_VALUE;
+
+ //
+ // Fill the ECB with the necessary information
+ //
+ if (!FillExtensionControlBlock(&ECB, &context) ) {
+ fprintf(stderr,"Fill Ext Block Failed\n");
+ return -1;
+ }
+
+ // check for command line argument,
+ // first arg = filename
+ // this is added for testing php from command line
+
+ context.env.RemoveAll();
+ context.env["PATH_TRANSLATED"]= filename;
+ context.env["SCRIPT_MAP"]= filename;
+ context.env["CONTENT_TYPE"]= "";
+ context.env["CONTENT_LENGTH"]= "";
+ context.env["QUERY_STRING"]= arg;
+ context.env["METHOD"]="GET";
+ context.env["PATH_INFO"] = "";
+ context.waitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ char buf[MAX_PATH];
+ if (postdata && *postdata !=0) {
+ ECB.cbAvailable = strlen(postdata);
+ ECB.cbTotalBytes = ECB.cbAvailable;
+ ECB.lpbData = (unsigned char *)postdata;
+ context.env["METHOD"]="POST";
+
+ _snprintf(buf, sizeof(buf)-1, "%d", ECB.cbTotalBytes);
+ context.env["CONTENT_LENGTH"]=buf;
+
+ context.env["CONTENT_TYPE"]="application/x-www-form-urlencoded";
+ }
+ ECB.lpszMethod = strdup(context.env["METHOD"]);
+ ECB.lpszPathTranslated = strdup(filename);
+ ECB.lpszQueryString = strdup(arg);
+ ECB.lpszPathInfo = strdup(context.env["PATH_INFO"]);
+
+
+ // Call the DLL
+ //
+ rc = IsapiHttpExtensionProc(&ECB);
+ if (rc == HSE_STATUS_PENDING) {
+ // We will exit in ServerSupportFunction
+ WaitForSingleObject(context.waitEvent, INFINITE);
+ }
+ CloseHandle(context.waitEvent);
+ //Sleep(75);
+ free(ECB.lpszPathTranslated);
+ free(ECB.lpszQueryString);
+ free(ECB.lpszMethod);
+ free(ECB.lpszPathInfo);
+
+ BOOL ok = TRUE;
+
+ if (context.out != INVALID_HANDLE_VALUE) CloseHandle(context.out);
+
+ // compare the output with the EXPECT section
+ if (matchdata && *matchdata != 0) {
+ ok = CompareFiles(fname, matchdata);
+ }
+
+ DeleteFile(fname);
+
+ return ok;
+
+}
+//
+// GetServerVariable() is how the DLL calls the main program to figure out
+// the environment variables it needs. This is a required function.
+//
+BOOL WINAPI GetServerVariable(HCONN hConn, LPSTR lpszVariableName,
+ LPVOID lpBuffer, LPDWORD lpdwSize){
+
+ DWORD rc;
+ CString value;
+ TIsapiContext *c = (TIsapiContext *)hConn;
+ if (!c) return FALSE;
+
+ if (IsapiEnvironment.Lookup(lpszVariableName, value)) {
+ rc = value.GetLength();
+ strncpy((char *)lpBuffer, value, *lpdwSize-1);
+ } else if (c->env.Lookup(lpszVariableName, value)) {
+ rc = value.GetLength();
+ strncpy((char *)lpBuffer, value, *lpdwSize-1);
+ } else
+ rc = GetEnvironmentVariable(lpszVariableName, (char *)lpBuffer, *lpdwSize) ;
+
+ if (!rc) { // return of 0 indicates the variable was not found
+ SetLastError(ERROR_NO_DATA);
+ return FALSE;
+ }
+
+ if (rc > *lpdwSize) {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+
+ *lpdwSize =rc + 1 ; // GetEnvironmentVariable does not count the NULL
+
+ return TRUE;
+
+}
+//
+// Again, we don't have an HCONN, so we simply wrap ReadClient() to
+// ReadFile on stdin. The semantics of the two functions are the same
+//
+BOOL WINAPI ReadClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize) {
+ TIsapiContext *c = (TIsapiContext *)hConn;
+ if (!c) return FALSE;
+
+ if (c->in != INVALID_HANDLE_VALUE)
+ return ReadFile(c->in, lpBuffer, (*lpdwSize), lpdwSize, NULL);
+
+ return FALSE;
+}
+//
+// ditto for WriteClient()
+//
+BOOL WINAPI WriteClient(HCONN hConn, LPVOID lpBuffer, LPDWORD lpdwSize,
+ DWORD dwReserved) {
+ TIsapiContext *c = (TIsapiContext *)hConn;
+ if (!c) return FALSE;
+
+ if (c->out != INVALID_HANDLE_VALUE)
+ return WriteFile(c->out, lpBuffer, *lpdwSize, lpdwSize, NULL);
+ return FALSE;
+}
+//
+// This is a special callback function used by the DLL for certain extra
+// functionality. Look at the API help for details.
+//
+BOOL WINAPI ServerSupportFunction(HCONN hConn, DWORD dwHSERequest,
+ LPVOID lpvBuffer, LPDWORD lpdwSize, LPDWORD lpdwDataType){
+
+ TIsapiContext *c = (TIsapiContext *)hConn;
+ char *lpszRespBuf;
+ char * temp = NULL;
+ DWORD dwBytes;
+ BOOL bRet = TRUE;
+
+ switch(dwHSERequest) {
+ case (HSE_REQ_SEND_RESPONSE_HEADER) :
+ lpszRespBuf = (char *)xmalloc(*lpdwSize);//+ 80);//accomodate our header
+ if (!lpszRespBuf)
+ return FALSE;
+ wsprintf(lpszRespBuf,"%s",
+ //HTTP_VER,
+
+ /* Default response is 200 Ok */
+
+ //lpvBuffer?lpvBuffer:"200 Ok",
+
+ /* Create a string for the time. */
+ //temp=MakeDateStr(),
+
+ //SERVER_VERSION,
+
+ /* If this exists, it is a pointer to a data buffer to
+ be sent. */
+ lpdwDataType?(char *)lpdwDataType:NULL);
+
+ if (temp) xfree(temp);
+
+ dwBytes = strlen(lpszRespBuf);
+ bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
+ xfree(lpszRespBuf);
+
+ break;
+ //
+ // A real server would do cleanup here
+ case (HSE_REQ_DONE_WITH_SESSION):
+ SetEvent(c->waitEvent);
+ //ExitThread(0);
+ break;
+
+ //
+ // This sends a redirect (temporary) to the client.
+ // The header construction is similar to RESPONSE_HEADER above.
+ //
+ case (HSE_REQ_SEND_URL_REDIRECT_RESP):
+ lpszRespBuf = (char *)xmalloc(*lpdwSize +80) ;
+ if (!lpszRespBuf)
+ return FALSE;
+ wsprintf(lpszRespBuf,"%s %s %s\r\n",
+ HTTP_VER,
+ "302 Moved Temporarily",
+ (lpdwSize > 0)?lpvBuffer:0);
+ xfree(temp);
+ dwBytes = strlen(lpszRespBuf);
+ bRet = WriteClient(0, lpszRespBuf, &dwBytes, 0);
+ xfree(lpszRespBuf);
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+ return bRet;
+
+}
+//
+// Makes a string of the date and time from GetSystemTime().
+// This is in UTC, as required by the HTTP spec.`
+//
+char * MakeDateStr(void){
+ SYSTEMTIME systime;
+ char *szDate= (char *)xmalloc(64);
+
+ char * DaysofWeek[] = {"Sun","Mon","Tue","Wed","Thurs","Fri","Sat"};
+ char * Months[] = {"NULL","Jan","Feb","Mar","Apr","May","Jun","Jul","Aug",
+ "Sep","Oct","Nov","Dec"};
+
+ GetSystemTime(&systime);
+
+ wsprintf(szDate,"%s, %d %s %d %d:%d.%d", DaysofWeek[systime.wDayOfWeek],
+ systime.wDay,
+ Months[systime.wMonth],
+ systime.wYear,
+ systime.wHour, systime.wMinute,
+ systime.wSecond );
+
+ return szDate;
+}
+//
+// Fill the ECB up
+//
+BOOL WINAPI FillExtensionControlBlock(EXTENSION_CONTROL_BLOCK *ECB, TIsapiContext *context) {
+
+ char * temp;
+ ECB->cbSize = sizeof(EXTENSION_CONTROL_BLOCK);
+ ECB->dwVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR);
+ ECB->ConnID = (void *)context;
+ //
+ // Pointers to the functions the DLL will call.
+ //
+ ECB->GetServerVariable = GetServerVariable;
+ ECB->ReadClient = ReadClient;
+ ECB->WriteClient = WriteClient;
+ ECB->ServerSupportFunction = ServerSupportFunction;
+
+ //
+ // Fill in the standard CGI environment variables
+ //
+ ECB->lpszMethod = GetEnv("REQUEST_METHOD");
+ if (!ECB->lpszMethod) ECB->lpszMethod = "GET";
+
+ ECB->lpszQueryString = GetEnv("QUERY_STRING");
+ ECB->lpszPathInfo = GetEnv("PATH_INFO");
+ ECB->lpszPathTranslated = GetEnv("PATH_TRANSLATED");
+ ECB->cbTotalBytes=( (temp=GetEnv("CONTENT_LENGTH")) ? (atoi(temp)): 0);
+ ECB->cbAvailable = 0;
+ ECB->lpbData = (unsigned char *)"";
+ ECB->lpszContentType = GetEnv("CONTENT_TYPE");
+ return TRUE;
+
+}
+
+//
+// Works like _getenv(), but uses win32 functions instead.
+//
+char *GetEnv(LPSTR lpszEnvVar)
+{
+
+ char *var, dummy;
+ DWORD dwLen;
+
+ if (!lpszEnvVar)
+ return "";
+
+ dwLen =GetEnvironmentVariable(lpszEnvVar, &dummy, 1);
+
+ if (dwLen == 0)
+ return "";
+
+ var = (char *)xmalloc(dwLen);
+ if (!var)
+ return "";
+ (void)GetEnvironmentVariable(lpszEnvVar, var, dwLen);
+
+ return var;
+}
diff --git a/sapi/isapi/stresstest/stresstest.dsp b/sapi/isapi/stresstest/stresstest.dsp
new file mode 100644
index 0000000..fb82303
--- /dev/null
+++ b/sapi/isapi/stresstest/stresstest.dsp
@@ -0,0 +1,108 @@
+# Microsoft Developer Studio Project File - Name="stresstest" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=stresstest - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "stresstest.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "stresstest.mak" CFG="stresstest - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "stresstest - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "stresstest - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "stresstest - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "_AFXDLL" /FD /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "stresstest - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "c:\php-fcgi"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "_AFXDLL" /FD /GZ /c
+# SUBTRACT CPP /YX /Yc /Yu
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "stresstest - Win32 Release"
+# Name "stresstest - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\getopt.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\getopt.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\stresstest.cpp
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\notes.txt
+# End Source File
+# End Target
+# End Project
diff --git a/sapi/litespeed/CREDITS b/sapi/litespeed/CREDITS
new file mode 100644
index 0000000..2fa192e
--- /dev/null
+++ b/sapi/litespeed/CREDITS
@@ -0,0 +1,2 @@
+litespeed
+George Wang
diff --git a/sapi/litespeed/Makefile.frag b/sapi/litespeed/Makefile.frag
new file mode 100644
index 0000000..b70e5e8
--- /dev/null
+++ b/sapi/litespeed/Makefile.frag
@@ -0,0 +1,9 @@
+litespeed: $(SAPI_LITESPEED_PATH)
+
+$(SAPI_LITESPEED_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_LITESPEED_OBJS)
+ $(BUILD_LITESPEED)
+
+install-litespeed: $(SAPI_LITESPEED_PATH)
+ @echo "Installing PHP LitSpeed binary: $(INSTALL_ROOT)$(bindir)/"
+ @$(INSTALL) -m 0755 $(SAPI_LITESPEED_PATH) $(INSTALL_ROOT)$(bindir)/lsphp
+
diff --git a/sapi/litespeed/README b/sapi/litespeed/README
new file mode 100644
index 0000000..e548ec9
--- /dev/null
+++ b/sapi/litespeed/README
@@ -0,0 +1,225 @@
+Introduction
+============
+
+LiteSpeed SAPI module is a dedicated interface for PHP integration with
+LiteSpeed Web Server. LiteSpeed SAPI has similar architecture to the
+FastCGI SAPI with there major enhancements: better performance, dynamic
+spawning and PHP configuration modification through web server
+configuration and .htaccess files.
+
+Our simple benchmark test ("hello world") shows that PHP with
+LiteSpeed SAPI has 30% better performance over PHP with FastCGI SAPI,
+which is nearly twice the performance that Apache mod_php can deliver.
+
+A major drawback of FastCGI PHP comparing to Apache mod_php is lacking
+the flexibilities in PHP configurations. PHP configurations cannot be
+changed at runtime via configuration files like .htaccess files or web
+server's virtual host configuration. In shared hosting environment,
+each hosting account will has its own "open_basedir" overridden in
+server configuration to enhance server security when mod_php is used.
+usually, FastCGI PHP is not an option in shared hosting environment
+due to lacking of this flexibility. LiteSpeed SAPI is carefully designed
+to address this issue. PHP configurations can be modified the same way
+as that in mod_php with the same configuration directives.
+
+PHP with LiteSpeed SAPI is highly recommended over FastCGI PHP for
+PHP scripting with LiteSpeed web server.
+
+
+Building PHP with LiteSpeed SAPI
+================================
+
+You need to add "--with-litespeed" to the configure command to build
+PHP with LiteSpeed SAPI, all other SAPI related configure options
+should be removed.
+
+For example:
+ ./configure --with-litespeed
+ make
+
+You should find an executable called 'php' under sapi/litespeed/
+directory after the compilation succeeds. Copy it to
+'lsws/fcgi-bin/lsphp' or wherever you prefer, if LiteSpeed web server
+has been configured to run PHP with LiteSpeed SAPI already, you just
+need to overwrite the old executable with this one and you are all
+set.
+
+Start PHP from command line
+===========================
+
+Usually, lsphp is managed by LiteSpeed web server in a single server
+installation. lsphp can be used in clustered environment with one
+LiteSpeed web server at the front, load balancing lsphp processes
+running on multiple backend servers. In such environment, lsphp can be
+start manually from command with option "-b <socket_address>", socket
+address can be IPv4, IPv6 or Unix Domain Socket address.
+for example:
+
+ ./lsphp -b [::]:3000
+
+have lsphp bind to port 3000 on all IPv4 and IPv6 address,
+
+ ./lsphp -b *:3000
+
+have lsphp bind to port 300 on all IPv4 address.
+
+ ./lsphp -b 192.168.0.2:3000
+
+have lsphp bind to address 192.168.0.2:3000.
+
+ ./lsphp -b /tmp/lsphp_manual.sock
+
+have lsphp accept request on Unix domain socket "/tmp/lsphp_manual.sock"
+
+
+Using LiteSpeed PHP with LiteSpeed Web Server
+=============================================
+
+Detailed information about how to configure LiteSpeed web server with
+PHP support is available from our website, at:
+
+http://www.litespeedtech.com/docs/HowTo_QA.html
+
+Usually, PHP support has been configured out of box, you don't need to
+change it unless you want to change PHP interface from FastCGI to
+LiteSpeed SAPI or vice versa.
+
+Brief instructions are as follow:
+
+1) Login to web administration interface, go to 'Server'->'Ext App' tab,
+ add an external application of type "LSAPI app", "Command" should be
+ set to a shell command that executes the PHP binary you just built.
+ "Instances" should be set to "1". Add "LSAPI_CHILDREN" environment
+ variable to match the value of "Max Connections". More tunable
+ environment variable described below can be added.
+
+2) Go to 'Server'->'Script Handler' tab, add a script handler
+ configuration: set 'suffix' to 'php', 'Handler Type' to 'LiteSpeed
+ API', 'Handler Name' should be the name of external application
+ just defined.
+
+
+3) Click 'Apply Changes' link on the top left of the page, then click
+ 'graceful restart'. Now PHP is running with LiteSpeed SAPI.
+
+Tunings
+-------
+
+There are a few environment variables that can be tweaked to control the
+behavior of LSAPI application.
+
+* LSAPI_CHILDREN or PHP_LSAPI_CHILDREN (default: 0)
+
+There are two ways to let PHP handle multiple requests concurrently,
+Server Managed Mode and Self Managed Mode. In Server Managed Mode,
+LiteSpeed web server dynamically spawn/stop PHP processes, in this mode
+"Instances" should match "Max Connections" configuration for PHP
+external application. To start PHP in Self Managed Mode, "Instances"
+should be set to "1", while "LSAPI_CHILDREN" environment variable should
+be set to match the value of "Max Connections" and >1. Web Server will
+start one PHP process, this process will start/stop children PHP processes
+dynamically based on on demand. If "LSAPI_CHILDREN" <=1, PHP will be
+started in server managed mode.
+
+Self Managed Mode is preferred because all PHP processes can share one
+shared memory block for the opcode cache.
+
+Usually, there is no need to set value of LSAPI_CHILDREN over 100 in
+most server environment.
+
+
+* LSAPI_AVOID_FORK (default: 0)
+
+LSAPI_AVOID_FORK specifies the policy of the internal process manager in
+"Self Managed Mode". When set to 0, the internal process manager will stop
+and start children process on demand to save system resource. This is
+preferred in a shared hosting environment. When set to 1, the internal
+process manager will try to avoid freqently stopping and starting children
+process. This might be preferred in a dedicate hosting environment.
+
+
+* LSAPI_EXTRA_CHILDREN (default: 1/3 of LSAPI_CHILDREN or 0)
+
+LSAPI_EXTRA_CHILDREN controls the maximum number of extra children processes
+can be started when some or all existing children processes are in
+malfunctioning state. Total number of children processes will be reduced to
+LSAPI_CHILDREN level as soon as service is back to normal.
+When LSAPI_AVOID_FORK is set to 0, the default value is 1/3 of
+LSAPI_CHIDLREN, When LSAPI_AVOID_FORK is set to 1, the default value is 0.
+
+
+* LSAPI_MAX_REQS or PHP_LSAPI_MAX_REQUESTS (default value: 10000)
+
+This controls how many requests each child process will handle before
+it exits automatically. Several PHP functions have been identified
+having memory leaks. This parameter can help reducing memory usage
+of leaky PHP functions.
+
+
+* LSAPI_MAX_IDLE (default value: 300 seconds)
+
+In Self Managed Mode, LSAPI_MAX_IDLE controls how long a idle child
+process will wait for a new request before it exits. This option help
+releasing system resources taken by idle processes.
+
+
+* LSAPI_MAX_IDLE_CHILDREN
+ (default value: 1/3 of LSAPI_CHILDREN or LSAPI_CHILDREN)
+
+In Self Managed Mode, LSAI_MAX_IDLE_CHILDREN controls how many idle
+children processes are allowed. Excessive idle children processes
+will be killed by the parent process immediately.
+When LSAPI_AVOID_FORK is set to 0, the default value is 1/3 of
+LSAPI_CHIDLREN, When LSAPI_AVOID_FORK is set to 1, the default value
+is LSAPI_CHILDREN.
+
+
+* LSAPI_MAX_PROCESS_TIME (default value: 300 seconds)
+
+In Self Managed Mode, LSAPI_MAX_PROCESS_TIME controls the maximum
+processing time allowed when processing a request. If a child process
+can not finish processing of a request in the given time period, it
+will be killed by the parent process. This option can help getting rid
+of dead or runaway child process.
+
+
+* LSAPI_PGRP_MAX_IDLE (default value: FOREVER )
+
+In Self Managed Mode, LSAPI_PGRP_MAX_IDLE controls how long the parent
+process will wait before exiting when there is no child process.
+This option help releasing system resources taken by an idle parent
+process.
+
+
+* LSAPI_PPID_NO_CHECK
+
+By default a LSAPI application check the existence of its parent process
+and exits automatically if the parent process died. This is to reduce
+orphan process when web server is restarted. However, it is desireable
+to disable this feature, such as when a LSAPI process was started
+manually from command line. LSAPI_PPID_NO_CHECK should be set when
+you want to disable the checking of existence of parent process.
+When PHP started by "-b" option, it is disabled automatically.
+
+
+Compatibility with Apache mod_php
+=================================
+
+LSAPI PHP supports PHP configuration overridden via web server configuration
+as well as .htaccess.
+Since 4.0 release "apache_response_headers" function is supported.
+
+
+
+Contact
+=======
+
+For support questions, please post to our free support forum, at:
+
+http://www.litespeedtech.com/forum/
+
+For bug report, please send bug report to bug [at] litespeedtech.com.
+
+
+
+
diff --git a/sapi/litespeed/config.m4 b/sapi/litespeed/config.m4
new file mode 100644
index 0000000..268b9ae
--- /dev/null
+++ b/sapi/litespeed/config.m4
@@ -0,0 +1,31 @@
+dnl
+dnl $Id$
+dnl
+
+AC_MSG_CHECKING(for LiteSpeed support)
+
+PHP_ARG_WITH(litespeed,,
+[ --with-litespeed Build PHP as litespeed module], no)
+
+if test "$PHP_LITESPEED" != "no"; then
+ PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/sapi/litespeed/Makefile.frag,$abs_srcdir/sapi/litespeed,sapi/litespeed)
+ SAPI_LITESPEED_PATH=sapi/litespeed/php
+ PHP_SELECT_SAPI(litespeed, program, lsapi_main.c lsapilib.c, "", '$(SAPI_LITESPEED_PATH)')
+ case $host_alias in
+ *darwin*)
+ BUILD_LITESPEED="\$(CC) \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(NATIVE_RPATHS) \$(PHP_GLOBAL_OBJS:.lo=.o) \$(PHP_BINARY_OBJS:.lo=.o) \$(PHP_LITESPEED_OBJS:.lo=.o) \$(PHP_FRAMEWORKS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)"
+ ;;
+ *cygwin*)
+ SAPI_LITESPEED_PATH=sapi/litespeed/php.exe
+ BUILD_LITESPEED="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_LITESPEED_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)"
+ ;;
+ *)
+ BUILD_LITESPEED="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS_PROGRAM) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_LITESPEED_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_LITESPEED_PATH)"
+ ;;
+ esac
+
+ PHP_SUBST(SAPI_LITESPEED_PATH)
+ PHP_SUBST(BUILD_LITESPEED)
+fi
+
+AC_MSG_RESULT($PHP_LITESPEED)
diff --git a/sapi/litespeed/lsapi_main.c b/sapi/litespeed/lsapi_main.c
new file mode 100644
index 0000000..f33b049
--- /dev/null
+++ b/sapi/litespeed/lsapi_main.c
@@ -0,0 +1,1118 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt. |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: George Wang <gwang@litespeedtech.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "php.h"
+#include "SAPI.h"
+#include "php_main.h"
+#include "php_ini.h"
+#include "php_variables.h"
+#include "zend_highlight.h"
+#include "zend.h"
+
+#include "lsapilib.h"
+
+#include <stdio.h>
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef PHP_WIN32
+
+#include <io.h>
+#include <fcntl.h>
+#include "win32/php_registry.h"
+
+#else
+
+#include <sys/wait.h>
+
+#endif
+
+#include <sys/stat.h>
+
+#if HAVE_SYS_TYPES_H
+
+#include <sys/types.h>
+
+#endif
+
+#if HAVE_SIGNAL_H
+
+#include <signal.h>
+
+#endif
+
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+
+#define SAPI_LSAPI_MAX_HEADER_LENGTH 2048
+
+static int lsapi_mode = 1;
+static char *php_self = "";
+static char *script_filename = "";
+static int source_highlight = 0;
+static char * argv0 = NULL;
+static int engine = 1;
+#ifdef ZTS
+zend_compiler_globals *compiler_globals;
+zend_executor_globals *executor_globals;
+php_core_globals *core_globals;
+sapi_globals_struct *sapi_globals;
+void ***tsrm_ls;
+#endif
+
+zend_module_entry litespeed_module_entry;
+
+/* {{{ php_lsapi_startup
+ */
+static int php_lsapi_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, NULL, 0)==FAILURE) {
+ return FAILURE;
+ }
+ argv0 = sapi_module->executable_location;
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ sapi_lsapi_ini_defaults */
+
+/* overwriteable ini defaults must be set in sapi_cli_ini_defaults() */
+#define INI_DEFAULT(name,value)\
+ ZVAL_STRING(tmp, value, 0);\
+ zend_hash_update(configuration_hash, name, sizeof(name), tmp, sizeof(zval), (void**)&entry);\
+ Z_STRVAL_P(entry) = zend_strndup(Z_STRVAL_P(entry), Z_STRLEN_P(entry))
+
+static void sapi_lsapi_ini_defaults(HashTable *configuration_hash)
+{
+ zval *tmp, *entry;
+
+#if PHP_MAJOR_VERSION > 4
+/*
+ MAKE_STD_ZVAL(tmp);
+
+ INI_DEFAULT("register_long_arrays", "0");
+
+ FREE_ZVAL(tmp);
+*/
+#endif
+
+}
+/* }}} */
+
+
+/* {{{ sapi_lsapi_ub_write
+ */
+static int sapi_lsapi_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ int ret;
+ int remain;
+ if ( lsapi_mode ) {
+ ret = LSAPI_Write( str, str_length );
+ if ( ret < str_length ) {
+ php_handle_aborted_connection();
+ return str_length - ret;
+ }
+ } else {
+ remain = str_length;
+ while( remain > 0 ) {
+ ret = write( 1, str, remain );
+ if ( ret <= 0 ) {
+ php_handle_aborted_connection();
+ return str_length - remain;
+ }
+ str += ret;
+ remain -= ret;
+ }
+ }
+ return str_length;
+}
+/* }}} */
+
+
+/* {{{ sapi_lsapi_flush
+ */
+static void sapi_lsapi_flush( void * server_context )
+{
+ if ( lsapi_mode ) {
+ if ( LSAPI_Flush() == -1) {
+ php_handle_aborted_connection();
+ }
+ }
+}
+/* }}} */
+
+
+/* {{{ sapi_lsapi_deactivate
+ */
+static int sapi_lsapi_deactivate(TSRMLS_D)
+{
+ if ( SG(request_info).path_translated )
+ {
+ efree( SG(request_info).path_translated );
+ }
+
+ return SUCCESS;
+}
+/* }}} */
+
+
+
+
+/* {{{ sapi_lsapi_getenv
+ */
+static char *sapi_lsapi_getenv( char * name, size_t name_len TSRMLS_DC )
+{
+ if ( lsapi_mode ) {
+ return LSAPI_GetEnv( name );
+ } else {
+ return getenv( name );
+ }
+}
+/* }}} */
+
+
+/*
+static int add_variable( const char * pKey, int keyLen, const char * pValue, int valLen,
+ void * arg )
+{
+ php_register_variable_safe((char *)pKey, (char *)pValue, valLen, (zval *)arg TSRMLS_CC);
+ return 1;
+}
+*/
+
+static int add_variable( const char * pKey, int keyLen, const char * pValue, int valLen,
+ void * arg )
+{
+ zval * gpc_element, **gpc_element_p;
+ HashTable * symtable1 = Z_ARRVAL_P((zval * )arg);
+ register char * pKey1 = (char *)pKey;
+
+ MAKE_STD_ZVAL(gpc_element);
+ Z_STRLEN_P( gpc_element ) = valLen;
+ Z_STRVAL_P( gpc_element ) = estrndup(pValue, valLen);
+ Z_TYPE_P( gpc_element ) = IS_STRING;
+#if PHP_MAJOR_VERSION > 4
+ zend_symtable_update( symtable1, pKey1, keyLen + 1, &gpc_element, sizeof( zval *), (void **) &gpc_element_p );
+#else
+ zend_hash_update( symtable1, pKey1, keyLen + 1, &gpc_element, sizeof( zval *), (void **) &gpc_element_p );
+#endif
+ return 1;
+}
+
+
+#if ((PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || PHP_MAJOR_VERSION < 5)
+static int add_variable_magic_quote( const char * pKey, int keyLen, const char * pValue, int valLen,
+ void * arg )
+{
+ zval * gpc_element, **gpc_element_p;
+ HashTable * symtable1 = Z_ARRVAL_P((zval * )arg);
+ register char * pKey1 = (char *)pKey;
+
+ MAKE_STD_ZVAL(gpc_element);
+ Z_STRLEN_P( gpc_element ) = valLen;
+ Z_STRVAL_P( gpc_element ) = php_addslashes((char *)pValue, valLen, &Z_STRLEN_P( gpc_element ), 0 );
+ Z_TYPE_P( gpc_element ) = IS_STRING;
+#if PHP_MAJOR_VERSION > 4
+ zend_symtable_update( symtable1, pKey1, keyLen + 1, &gpc_element, sizeof( zval *), (void **) &gpc_element_p );
+#else
+ zend_hash_update( symtable1, pKey1, keyLen + 1, &gpc_element, sizeof( zval *), (void **) &gpc_element_p );
+#endif
+ return 1;
+}
+
+#endif
+
+/* {{{ sapi_lsapi_register_variables
+ */
+static void sapi_lsapi_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ char * php_self = "";
+ if ( lsapi_mode ) {
+ if ( (SG(request_info).request_uri ) )
+ php_self = (SG(request_info).request_uri );
+
+#if ((PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || PHP_MAJOR_VERSION < 5)
+ if (!PG(magic_quotes_gpc)) {
+#endif
+ LSAPI_ForeachHeader( add_variable, track_vars_array );
+ LSAPI_ForeachEnv( add_variable, track_vars_array );
+ add_variable("PHP_SELF", 8, php_self, strlen( php_self ), track_vars_array );
+#if ((PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 4) || PHP_MAJOR_VERSION < 5)
+ } else {
+ LSAPI_ForeachHeader( add_variable_magic_quote, track_vars_array );
+ LSAPI_ForeachEnv( add_variable_magic_quote, track_vars_array );
+ add_variable_magic_quote("PHP_SELF", 8, php_self, strlen( php_self ), track_vars_array );
+ }
+#endif
+ php_import_environment_variables(track_vars_array TSRMLS_CC);
+ } else {
+ php_import_environment_variables(track_vars_array TSRMLS_CC);
+
+ php_register_variable("PHP_SELF", php_self, track_vars_array TSRMLS_CC);
+ php_register_variable("SCRIPT_NAME", php_self, track_vars_array TSRMLS_CC);
+ php_register_variable("SCRIPT_FILENAME", script_filename, track_vars_array TSRMLS_CC);
+ php_register_variable("PATH_TRANSLATED", script_filename, track_vars_array TSRMLS_CC);
+ php_register_variable("DOCUMENT_ROOT", "", track_vars_array TSRMLS_CC);
+
+ }
+}
+/* }}} */
+
+
+/* {{{ sapi_lsapi_read_post
+ */
+static int sapi_lsapi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ if ( lsapi_mode ) {
+ return LSAPI_ReadReqBody( buffer, count_bytes );
+ } else {
+ return 0;
+ }
+}
+/* }}} */
+
+
+
+
+/* {{{ sapi_lsapi_read_cookies
+ */
+static char *sapi_lsapi_read_cookies(TSRMLS_D)
+{
+ if ( lsapi_mode ) {
+ return LSAPI_GetHeader( H_COOKIE );
+ } else {
+ return NULL;
+ }
+}
+/* }}} */
+
+
+/* {{{ sapi_lsapi_send_headers
+ */
+static int sapi_lsapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ sapi_header_struct *h;
+ zend_llist_position pos;
+ if ( lsapi_mode ) {
+ LSAPI_SetRespStatus( SG(sapi_headers).http_response_code );
+
+ h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+ if ( h->header_len > 0 ) {
+ LSAPI_AppendRespHeader(h->header, h->header_len);
+ }
+ h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ }
+ if (SG(sapi_headers).send_default_content_type) {
+ char *hd;
+ int len;
+ char headerBuf[SAPI_LSAPI_MAX_HEADER_LENGTH];
+
+ hd = sapi_get_default_content_type(TSRMLS_C);
+ len = snprintf( headerBuf, SAPI_LSAPI_MAX_HEADER_LENGTH - 1,
+ "Content-type: %s", hd );
+ efree(hd);
+
+ LSAPI_AppendRespHeader( headerBuf, len );
+ }
+ }
+ LSAPI_FinalizeRespHeaders();
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+
+
+}
+/* }}} */
+
+
+/* {{{ sapi_lsapi_send_headers
+ */
+static void sapi_lsapi_log_message(char *message TSRMLS_DC)
+{
+ int len = strlen( message );
+ LSAPI_Write_Stderr( message, len);
+}
+/* }}} */
+
+
+/* {{{ sapi_module_struct cgi_sapi_module
+ */
+static sapi_module_struct lsapi_sapi_module =
+{
+ "litespeed",
+ "LiteSpeed V5.5",
+
+ php_lsapi_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ sapi_lsapi_deactivate, /* deactivate */
+
+ sapi_lsapi_ub_write, /* unbuffered write */
+ sapi_lsapi_flush, /* flush */
+ NULL, /* get uid */
+ sapi_lsapi_getenv, /* getenv */
+
+ php_error, /* error handler */
+
+ NULL, /* header handler */
+ sapi_lsapi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ sapi_lsapi_read_post, /* read POST data */
+ sapi_lsapi_read_cookies, /* read Cookies */
+
+ sapi_lsapi_register_variables, /* register server variables */
+ sapi_lsapi_log_message, /* Log message */
+
+ NULL, /* php.ini path override */
+ NULL, /* block interruptions */
+ NULL, /* unblock interruptions */
+ NULL, /* default post reader */
+ NULL, /* treat data */
+ NULL, /* executable location */
+
+ 0, /* php.ini ignore */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+
+};
+/* }}} */
+
+static int init_request_info( TSRMLS_D )
+{
+ char * pContentType = LSAPI_GetHeader( H_CONTENT_TYPE );
+ char * pAuth;
+
+ SG(request_info).content_type = pContentType ? pContentType : "";
+ SG(request_info).request_method = LSAPI_GetRequestMethod();
+ SG(request_info).query_string = LSAPI_GetQueryString();
+ SG(request_info).request_uri = LSAPI_GetScriptName();
+ SG(request_info).content_length = LSAPI_GetReqBodyLen();
+ SG(request_info).path_translated = estrdup( LSAPI_GetScriptFileName());
+
+ /* It is not reset by zend engine, set it to 0. */
+ SG(sapi_headers).http_response_code = 0;
+
+ pAuth = LSAPI_GetHeader( H_AUTHORIZATION );
+ php_handle_auth_data(pAuth TSRMLS_CC);
+}
+
+static char s_cur_chdir[4096] = "";
+
+static int lsapi_chdir_primary_script( zend_file_handle * file_handle )
+{
+#if PHP_MAJOR_VERSION > 4
+ char * p;
+ char ch;
+
+ SG(options) |= SAPI_OPTION_NO_CHDIR;
+ getcwd( s_cur_chdir, sizeof( s_cur_chdir ) );
+
+ p = strrchr( file_handle->filename, '/' );
+ if ( *p )
+ {
+ *p = 0;
+ if ( strcmp( file_handle->filename, s_cur_chdir ) != 0 ) {
+ chdir( file_handle->filename );
+ }
+ *p++ = '/';
+ ch = *p;
+ *p = 0;
+ if ( !CWDG(cwd).cwd ||
+ ( strcmp( file_handle->filename, CWDG(cwd).cwd ) != 0 ) ) {
+ CWDG(cwd).cwd_length = p - file_handle->filename;
+ CWDG(cwd).cwd = (char *) realloc(CWDG(cwd).cwd, CWDG(cwd).cwd_length+1);
+ memmove( CWDG(cwd).cwd, file_handle->filename, CWDG(cwd).cwd_length+1 );
+ }
+ *p = ch;
+ }
+ /* virtual_file_ex(&CWDG(cwd), file_handle->filename, NULL, CWD_REALPATH); */
+#else
+ VCWD_CHDIR_FILE( file_handle->filename );
+#endif
+ return 0;
+}
+
+static int lsapi_fopen_primary_script( zend_file_handle * file_handle )
+{
+ FILE * fp;
+ char * p;
+ fp = fopen( SG(request_info).path_translated, "rb" );
+ if ( !fp )
+ {
+ return -1;
+ }
+ file_handle->type = ZEND_HANDLE_FP;
+ file_handle->handle.fp = fp;
+ file_handle->filename = SG(request_info).path_translated;
+ file_handle->free_filename = 0;
+ file_handle->opened_path = NULL;
+
+ lsapi_chdir_primary_script( file_handle );
+
+ return 0;
+}
+
+static int lsapi_execute_script( zend_file_handle * file_handle TSRMLS_DC)
+{
+ char *p;
+ int len;
+ file_handle->type = ZEND_HANDLE_FILENAME;
+ file_handle->handle.fd = 0;
+ file_handle->filename = SG(request_info).path_translated;
+ file_handle->free_filename = 0;
+ file_handle->opened_path = NULL;
+
+ p = argv0;
+ *p++ = ':';
+ len = strlen( SG(request_info).path_translated );
+ if ( len > 45 )
+ len = len - 45;
+ else
+ len = 0;
+ memccpy( p, SG(request_info).path_translated + len, 0, 46 );
+
+ php_execute_script(file_handle TSRMLS_CC);
+ return 0;
+
+}
+
+
+static int lsapi_module_main(int show_source TSRMLS_DC)
+{
+ zend_file_handle file_handle = {0};
+
+ if (php_request_startup(TSRMLS_C) == FAILURE ) {
+ return -1;
+ }
+ if (show_source) {
+ zend_syntax_highlighter_ini syntax_highlighter_ini;
+
+ php_get_highlight_struct(&syntax_highlighter_ini);
+ highlight_file(SG(request_info).path_translated, &syntax_highlighter_ini TSRMLS_CC);
+ } else {
+ lsapi_execute_script( &file_handle TSRMLS_CC);
+ }
+ zend_try {
+ php_request_shutdown(NULL);
+ *argv0 = 0;
+ } zend_end_try();
+ return 0;
+}
+
+
+static int alter_ini( const char * pKey, int keyLen, const char * pValue, int valLen,
+ void * arg )
+{
+ int type = ZEND_INI_PERDIR;
+ if ( '\001' == *pKey ) {
+ ++pKey;
+ if ( *pKey == 4 ) {
+ type = ZEND_INI_SYSTEM;
+ }
+ ++pKey;
+ --keyLen;
+ if (( keyLen == 7 )&&( strncasecmp( pKey, "engine", 6 )== 0 ))
+ {
+ if ( *pValue == '0' )
+ engine = 0;
+ }
+ else
+ zend_alter_ini_entry((char *)pKey, keyLen,
+ (char *)pValue, valLen,
+ type, PHP_INI_STAGE_ACTIVATE);
+ }
+ return 1;
+}
+
+
+static void override_ini()
+{
+
+ LSAPI_ForeachSpecialEnv( alter_ini, NULL );
+
+}
+
+static int processReq( TSRMLS_D )
+{
+ int ret = 0;
+ zend_first_try {
+ /* avoid server_context==NULL checks */
+ SG(server_context) = (void *) 1;
+
+ engine = 1;
+ override_ini();
+
+ if ( engine ) {
+ init_request_info( TSRMLS_C );
+
+ if ( lsapi_module_main( source_highlight TSRMLS_CC ) == -1 ) {
+ ret = -1;
+ }
+ } else {
+ LSAPI_AppendRespHeader( "status: 403", 11 );
+ LSAPI_AppendRespHeader( "content-type: text/html", 23 );
+ LSAPI_Write( "Forbidden: PHP engine is disable.\n", 34 );
+ }
+ } zend_end_try();
+ return ret;
+}
+
+static void cli_usage( TSRMLS_D )
+{
+ static const char * usage =
+ "Usage: php\n"
+ " php -[b|c|h|i|q|s|v|?] [<file>] [args...]\n"
+ " Run in LSAPI mode, only '-b', '-s' and '-c' are effective\n"
+ " Run in Command Line Interpreter mode when parameters are specified\n"
+ "\n"
+ " -b <address:port>|<port> Bind Path for external LSAPI Server mode\n"
+ " -c <path>|<file> Look for php.ini file in this directory\n"
+ " -h This help\n"
+ " -i PHP information\n"
+ " -q Quiet-mode. Suppress HTTP Header output.\n"
+ " -s Display colour syntax highlighted source.\n"
+ " -v Version number\n"
+ " -? This help\n"
+ "\n"
+ " args... Arguments passed to script.\n";
+ php_output_startup();
+ php_output_activate(TSRMLS_C);
+ php_printf( "%s", usage );
+#ifdef PHP_OUTPUT_NEWAPI
+ php_output_end_all(TSRMLS_C);
+#else
+ php_end_ob_buffers(1 TSRMLS_CC);
+#endif
+}
+
+static int parse_opt( int argc, char * argv[], int *climode,
+ char **php_ini_path, char ** php_bind )
+{
+ char ** p = &argv[1];
+ char ** argend= &argv[argc];
+ int c;
+ while (( p < argend )&&(**p == '-' )) {
+ c = *((*p)+1);
+ ++p;
+ switch( c ) {
+ case 'b':
+ if ( p >= argend ) {
+ fprintf( stderr, "TCP or socket address must be specified following '-b' option.\n");
+ return -1;
+ }
+ *php_bind = *p++;
+ break;
+
+ case 'c':
+ if ( p >= argend ) {
+ fprintf( stderr, "<path> or <file> must be specified following '-c' option.\n");
+
+ return -1;
+ }
+ *php_ini_path = *p++;
+ break;
+ case 's':
+ source_highlight = 1;
+ break;
+ case 'h':
+ case 'i':
+ case 'q':
+ case 'v':
+ case '?':
+ default:
+ *climode = 1;
+ break;
+ }
+ }
+ if ( p - argv < argc ) {
+ *climode = 1;
+ }
+ return 0;
+}
+
+static int cli_main( int argc, char * argv[] )
+{
+
+ static const char * ini_defaults[] = {
+ "report_zend_debug", "0",
+ "display_errors", "1",
+ "register_argc_argv", "1",
+ "html_errors", "0",
+ "implicit_flush", "1",
+ "output_buffering", "0",
+ "max_execution_time", "0",
+ "max_input_time", "-1",
+ NULL
+ };
+
+ const char ** ini;
+ char ** p = &argv[1];
+ char ** argend= &argv[argc];
+ int ret = 0;
+ int c;
+ lsapi_mode = 0; /* enter CLI mode */
+
+#ifdef PHP_WIN32
+ _fmode = _O_BINARY; /*sets default for file streams to binary */
+ setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
+ setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
+#endif
+
+ zend_first_try {
+ SG(server_context) = (void *) 1;
+
+ zend_uv.html_errors = 0; /* tell the engine we're in non-html mode */
+ CG(in_compilation) = 0; /* not initialized but needed for several options */
+ EG(uninitialized_zval_ptr) = NULL;
+
+ for( ini = ini_defaults; *ini; ini+=2 ) {
+ zend_alter_ini_entry( (char *)*ini, strlen( *ini )+1,
+ (char *)*(ini+1), strlen( *(ini+1) ),
+ PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+ }
+
+ while (( p < argend )&&(**p == '-' )) {
+ c = *((*p)+1);
+ ++p;
+ switch( c ) {
+ case 'q':
+ break;
+ case 'i':
+ if (php_request_startup(TSRMLS_C) != FAILURE) {
+ php_print_info(0xFFFFFFFF TSRMLS_CC);
+#ifdef PHP_OUTPUT_NEWAPI
+ php_output_end_all(TSRMLS_C);
+#else
+ php_end_ob_buffers(1 TSRMLS_CC);
+#endif
+ php_request_shutdown( NULL );
+ }
+ ret = 1;
+ break;
+ case 'v':
+ if (php_request_startup(TSRMLS_C) != FAILURE) {
+#if ZEND_DEBUG
+ php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2013 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
+#else
+ php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2013 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
+#endif
+#ifdef PHP_OUTPUT_NEWAPI
+ php_output_end_all(TSRMLS_C);
+#else
+ php_end_ob_buffers(1 TSRMLS_CC);
+#endif
+ php_request_shutdown( NULL );
+ }
+ ret = 1;
+ break;
+ case 'c':
+ ++p;
+ /* fall through */
+ case 's':
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ cli_usage(TSRMLS_C);
+ ret = 1;
+ break;
+
+ }
+ }
+ if ( !ret ) {
+ if ( *p ) {
+ zend_file_handle file_handle = {0};
+
+ file_handle.type = ZEND_HANDLE_FP;
+ file_handle.handle.fp = VCWD_FOPEN(*p, "rb");
+
+ if ( file_handle.handle.fp ) {
+ script_filename = *p;
+ php_self = *p;
+
+ SG(request_info).path_translated = estrdup(*p);
+ SG(request_info).argc = argc - (p - argv);
+ SG(request_info).argv = p;
+
+ if (php_request_startup(TSRMLS_C) == FAILURE ) {
+ fclose( file_handle.handle.fp );
+ ret = 2;
+ } else {
+ if (source_highlight) {
+ zend_syntax_highlighter_ini syntax_highlighter_ini;
+
+ php_get_highlight_struct(&syntax_highlighter_ini);
+ highlight_file(SG(request_info).path_translated, &syntax_highlighter_ini TSRMLS_CC);
+ } else {
+ file_handle.filename = *p;
+ file_handle.free_filename = 0;
+ file_handle.opened_path = NULL;
+
+ php_execute_script(&file_handle TSRMLS_CC);
+ }
+
+ php_request_shutdown( NULL );
+ }
+ } else {
+ php_printf("Could not open input file: %s.\n", *p);
+ }
+ } else {
+ cli_usage(TSRMLS_C);
+ }
+ }
+
+ }zend_end_try();
+
+ php_module_shutdown(TSRMLS_C);
+
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ return ret;
+}
+
+static int s_stop;
+void litespeed_cleanup(int signal)
+{
+ s_stop = signal;
+}
+
+
+void start_children( int children )
+{
+ struct sigaction act, old_term, old_quit, old_int, old_usr1;
+ int running = 0;
+ int status;
+ pid_t pid;
+
+ /* Create a process group */
+ setsid();
+
+ /* Set up handler to kill children upon exit */
+ act.sa_flags = 0;
+ act.sa_handler = litespeed_cleanup;
+ if( sigaction( SIGTERM, &act, &old_term ) ||
+ sigaction( SIGINT, &act, &old_int ) ||
+ sigaction( SIGUSR1, &act, &old_usr1 ) ||
+ sigaction( SIGQUIT, &act, &old_quit )) {
+ perror( "Can't set signals" );
+ exit( 1 );
+ }
+ s_stop = 0;
+ while( 1 ) {
+ while((!s_stop )&&( running < children )) {
+ pid = fork();
+ switch( pid ) {
+ case 0: /* children process */
+
+ /* don't catch our signals */
+ sigaction( SIGTERM, &old_term, 0 );
+ sigaction( SIGQUIT, &old_quit, 0 );
+ sigaction( SIGINT, &old_int, 0 );
+ sigaction( SIGUSR1, &old_usr1, 0 );
+ return ;
+ case -1:
+ perror( "php (pre-forking)" );
+ exit( 1 );
+ break;
+ default: /* parent process */
+ running++;
+ break;
+ }
+ }
+ if ( s_stop ) {
+ break;
+ }
+ pid = wait( &status );
+ running--;
+ }
+ kill( -getpgrp(), SIGUSR1 );
+ exit( 0 );
+}
+
+
+
+#include <fcntl.h>
+int main( int argc, char * argv[] )
+{
+ int ret;
+ int bindFd;
+
+ char * php_ini_path = NULL;
+ char * php_bind = NULL;
+ char * p;
+ int n;
+ int climode = 0;
+ struct timeval tv_req_begin;
+ struct timeval tv_req_end;
+ int slow_script_msec = 0;
+ char time_buf[40];
+
+#ifdef HAVE_SIGNAL_H
+#if defined(SIGPIPE) && defined(SIG_IGN)
+ signal(SIGPIPE, SIG_IGN);
+#endif
+#endif
+
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+#endif
+
+ if (argc > 1 ) {
+ if ( parse_opt( argc, argv, &climode,
+ &php_ini_path, &php_bind ) == -1 ) {
+ return 1;
+ }
+ }
+ if ( climode ) {
+ lsapi_sapi_module.phpinfo_as_text = 1;
+ }
+ argv0 = argv[0] + strlen( argv[0] );
+ sapi_startup(&lsapi_sapi_module);
+
+#ifdef ZTS
+ compiler_globals = ts_resource(compiler_globals_id);
+ executor_globals = ts_resource(executor_globals_id);
+ core_globals = ts_resource(core_globals_id);
+ sapi_globals = ts_resource(sapi_globals_id);
+ tsrm_ls = ts_resource(0);
+
+ SG(request_info).path_translated = NULL;
+#endif
+
+ lsapi_sapi_module.executable_location = argv[0];
+
+ if ( php_ini_path ) {
+ lsapi_sapi_module.php_ini_path_override = php_ini_path;
+ }
+
+
+ lsapi_sapi_module.ini_defaults = sapi_lsapi_ini_defaults;
+
+ if (php_module_startup(&lsapi_sapi_module, &litespeed_module_entry, 1) == FAILURE) {
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ return FAILURE;
+ }
+
+ if ( climode ) {
+ return cli_main(argc, argv);
+ }
+
+ if ( php_bind ) {
+ bindFd = LSAPI_CreateListenSock( php_bind, 10 );
+ if ( bindFd == -1 ) {
+ fprintf( stderr,
+ "Failed to bind socket [%s]: %s\n", php_bind, strerror( errno ) );
+ exit( 2 );
+ }
+ if ( bindFd != 0 ) {
+ dup2( bindFd, 0 );
+ close( bindFd );
+ }
+ }
+
+ LSAPI_Init();
+
+ LSAPI_Init_Env_Parameters( NULL );
+
+ slow_script_msec = LSAPI_Get_Slow_Req_Msecs();
+
+ if ( php_bind ) {
+ LSAPI_No_Check_ppid();
+ }
+
+ while( LSAPI_Prefork_Accept_r( &g_req ) >= 0 ) {
+ if ( slow_script_msec ) {
+ gettimeofday( &tv_req_begin, NULL );
+ }
+ ret = processReq(TSRMLS_C);
+ if ( slow_script_msec ) {
+ gettimeofday( &tv_req_end, NULL );
+ n = ((long) tv_req_end.tv_sec - tv_req_begin.tv_sec ) * 1000
+ + (tv_req_end.tv_usec - tv_req_begin.tv_usec) / 1000;
+ if ( n > slow_script_msec )
+ {
+ strftime( time_buf, 30, "%d/%b/%Y:%H:%M:%S", localtime( &tv_req_end.tv_sec ) );
+ fprintf( stderr, "[%s] Slow PHP script: %d ms\n URL: %s %s\n Query String: %s\n Script: %s\n",
+ time_buf, n, LSAPI_GetRequestMethod(),
+ LSAPI_GetScriptName(), LSAPI_GetQueryString(),
+ LSAPI_GetScriptFileName() );
+
+ }
+ }
+ LSAPI_Finish();
+ if ( ret ) {
+ break;
+ }
+ }
+ php_module_shutdown(TSRMLS_C);
+
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ return ret;
+}
+
+
+/* LiteSpeed PHP module starts here */
+
+#if PHP_MAJOR_VERSION > 4
+
+/* {{{ arginfo */
+ZEND_BEGIN_ARG_INFO(arginfo_litespeed__void, 0)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+#else
+#define arginfo_litespeed__void NULL
+#endif
+
+PHP_FUNCTION(litespeed_request_headers);
+PHP_FUNCTION(litespeed_response_headers);
+
+PHP_MINFO_FUNCTION(litespeed);
+
+zend_function_entry litespeed_functions[] = {
+ PHP_FE(litespeed_request_headers, arginfo_litespeed__void)
+ PHP_FE(litespeed_response_headers, arginfo_litespeed__void)
+ PHP_FALIAS(getallheaders, litespeed_request_headers, arginfo_litespeed__void)
+ PHP_FALIAS(apache_request_headers, litespeed_request_headers, arginfo_litespeed__void)
+ PHP_FALIAS(apache_response_headers, litespeed_response_headers, arginfo_litespeed__void)
+ {NULL, NULL, NULL}
+};
+
+static PHP_MINIT_FUNCTION(litespeed)
+{
+ /* REGISTER_INI_ENTRIES(); */
+ return SUCCESS;
+}
+
+
+static PHP_MSHUTDOWN_FUNCTION(litespeed)
+{
+ /* UNREGISTER_INI_ENTRIES(); */
+ return SUCCESS;
+}
+
+zend_module_entry litespeed_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "litespeed",
+ litespeed_functions,
+ PHP_MINIT(litespeed),
+ PHP_MSHUTDOWN(litespeed),
+ NULL,
+ NULL,
+ NULL,
+ NO_VERSION_YET,
+ STANDARD_MODULE_PROPERTIES
+};
+
+static int add_associate_array( const char * pKey, int keyLen, const char * pValue, int valLen,
+ void * arg )
+{
+ add_assoc_string_ex( (zval *)arg, (char *)pKey, keyLen+1, (char *)pValue, 1 );
+ return 1;
+}
+
+
+/* {{{ proto array litespeed_request_headers(void)
+ Fetch all HTTP request headers */
+PHP_FUNCTION(litespeed_request_headers)
+{
+ /* TODO: */
+ if (ZEND_NUM_ARGS() > 0) {
+ WRONG_PARAM_COUNT;
+ }
+ array_init(return_value);
+
+ if ( lsapi_mode )
+ LSAPI_ForeachOrgHeader( add_associate_array, return_value );
+
+}
+/* }}} */
+
+
+
+/* {{{ proto array litespeed_response_headers(void)
+ Fetch all HTTP response headers */
+PHP_FUNCTION(litespeed_response_headers)
+{
+ sapi_header_struct *h;
+ zend_llist_position pos;
+ char * p;
+ int len;
+ char headerBuf[SAPI_LSAPI_MAX_HEADER_LENGTH];
+
+ if (ZEND_NUM_ARGS() > 0) {
+ WRONG_PARAM_COUNT;
+ }
+
+ if (!&SG(sapi_headers).headers) {
+ RETURN_FALSE;
+ }
+ array_init(return_value);
+
+ h = zend_llist_get_first_ex(&SG(sapi_headers).headers, &pos);
+ while (h) {
+ if ( h->header_len > 0 ) {
+ p = strchr( h->header, ':' );
+ len = p - h->header;
+ if (( p )&&( len > 0 )) {
+ memmove( headerBuf, h->header, len );
+ while( len > 0 && (isspace( headerBuf[len-1])) ) {
+ --len;
+ }
+ headerBuf[len] = 0;
+ if ( len ) {
+ while( isspace(*++p));
+ add_assoc_string_ex(return_value, headerBuf, len+1, p, 1 );
+ }
+ }
+ }
+ h = zend_llist_get_next_ex(&SG(sapi_headers).headers, &pos);
+ }
+}
+
+/* }}} */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
+
+
diff --git a/sapi/litespeed/lsapidef.h b/sapi/litespeed/lsapidef.h
new file mode 100644
index 0000000..c8940a9
--- /dev/null
+++ b/sapi/litespeed/lsapidef.h
@@ -0,0 +1,196 @@
+
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt. |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: George Wang <gwang@litespeedtech.com> |
+ +----------------------------------------------------------------------+
+*/
+
+
+/*
+Copyright (c) 2007, Lite Speed Technologies Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of the Lite Speed Technologies Inc nor the
+ names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef _LSAPIDEF_H_
+#define _LSAPIDEF_H_
+
+#include <inttypes.h>
+
+#if defined (c_plusplus) || defined (__cplusplus)
+extern "C" {
+#endif
+
+enum
+{
+ H_ACCEPT = 0,
+ H_ACC_CHARSET,
+ H_ACC_ENCODING,
+ H_ACC_LANG,
+ H_AUTHORIZATION,
+ H_CONNECTION,
+ H_CONTENT_TYPE,
+ H_CONTENT_LENGTH,
+ H_COOKIE,
+ H_COOKIE2,
+ H_HOST,
+ H_PRAGMA,
+ H_REFERER,
+ H_USERAGENT,
+ H_CACHE_CTRL,
+ H_IF_MODIFIED_SINCE,
+ H_IF_MATCH,
+ H_IF_NO_MATCH,
+ H_IF_RANGE,
+ H_IF_UNMOD_SINCE,
+ H_KEEP_ALIVE,
+ H_RANGE,
+ H_X_FORWARDED_FOR,
+ H_VIA,
+ H_TRANSFER_ENCODING
+
+};
+#define LSAPI_SOCK_FILENO 0
+
+#define LSAPI_VERSION_B0 'L'
+#define LSAPI_VERSION_B1 'S'
+
+/* Values for m_flag in lsapi_packet_header */
+#define LSAPI_ENDIAN_LITTLE 0
+#define LSAPI_ENDIAN_BIG 1
+#define LSAPI_ENDIAN_BIT 1
+
+#if defined(__i386__)||defined( __x86_64 )||defined( __x86_64__ )
+#define LSAPI_ENDIAN LSAPI_ENDIAN_LITTLE
+#else
+#define LSAPI_ENDIAN LSAPI_ENDIAN_BIG
+#endif
+
+/* Values for m_type in lsapi_packet_header */
+#define LSAPI_BEGIN_REQUEST 1
+#define LSAPI_ABORT_REQUEST 2
+#define LSAPI_RESP_HEADER 3
+#define LSAPI_RESP_STREAM 4
+#define LSAPI_RESP_END 5
+#define LSAPI_STDERR_STREAM 6
+#define LSAPI_REQ_RECEIVED 7
+
+
+#define LSAPI_MAX_HEADER_LEN 65535
+#define LSAPI_MAX_DATA_PACKET_LEN 16384
+
+#define LSAPI_RESP_HTTP_HEADER_MAX 4096
+#define LSAPI_PACKET_HEADER_LEN 8
+
+
+struct lsapi_packet_header
+{
+ char m_versionB0; /* LSAPI protocol version */
+ char m_versionB1;
+ char m_type;
+ char m_flag;
+ union
+ {
+ int32_t m_iLen; /* include this header */
+ char m_bytes[4];
+ }m_packetLen;
+};
+
+/*
+ LSAPI request header packet
+
+ 1. struct lsapi_req_header
+ 2. struct lsapi_http_header_index
+ 3. lsapi_header_offset * unknownHeaders
+ 4. org http request header
+ 5. request body if available
+*/
+
+struct lsapi_req_header
+{
+ struct lsapi_packet_header m_pktHeader;
+
+ int32_t m_httpHeaderLen;
+ int32_t m_reqBodyLen;
+ int32_t m_scriptFileOff; /* path to the script file. */
+ int32_t m_scriptNameOff; /* decrypted URI, without pathinfo, */
+ int32_t m_queryStringOff; /* Query string inside env */
+ int32_t m_requestMethodOff;
+ int32_t m_cntUnknownHeaders;
+ int32_t m_cntEnv;
+ int32_t m_cntSpecialEnv;
+} ;
+
+
+struct lsapi_http_header_index
+{
+ int16_t m_headerLen[H_TRANSFER_ENCODING+1];
+ int32_t m_headerOff[H_TRANSFER_ENCODING+1];
+} ;
+
+struct lsapi_header_offset
+{
+ int32_t nameOff;
+ int32_t nameLen;
+ int32_t valueOff;
+ int32_t valueLen;
+} ;
+
+struct lsapi_resp_info
+{
+ int32_t m_cntHeaders;
+ int32_t m_status;
+};
+
+struct lsapi_resp_header
+{
+ struct lsapi_packet_header m_pktHeader;
+ struct lsapi_resp_info m_respInfo;
+};
+
+#if defined (c_plusplus) || defined (__cplusplus)
+}
+#endif
+
+
+#endif
+
diff --git a/sapi/litespeed/lsapilib.c b/sapi/litespeed/lsapilib.c
new file mode 100644
index 0000000..10ea749
--- /dev/null
+++ b/sapi/litespeed/lsapilib.c
@@ -0,0 +1,2227 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt. |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: George Wang <gwang@litespeedtech.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+/*
+Copyright (c) 2007, Lite Speed Technologies Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of the Lite Speed Technologies Inc nor the
+ names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <lsapilib.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/un.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#define LSAPI_ST_REQ_HEADER 1
+#define LSAPI_ST_REQ_BODY 2
+#define LSAPI_ST_RESP_HEADER 4
+#define LSAPI_ST_RESP_BODY 8
+
+#define LSAPI_RESP_BUF_SIZE 8192
+#define LSAPI_INIT_RESP_HEADER_LEN 4096
+
+
+static int g_inited = 0;
+static int g_running = 1;
+static int s_ppid;
+static int s_slow_req_msecs = 0;
+LSAPI_Request g_req = { -1, -1 };
+
+void Flush_RespBuf_r( LSAPI_Request * pReq );
+
+static const char *CGI_HEADERS[H_TRANSFER_ENCODING+1] =
+{
+ "HTTP_ACCEPT", "HTTP_ACCEPT_CHARSET",
+ "HTTP_ACCEPT_ENCODING",
+ "HTTP_ACCEPT_LANGUAGE", "HTTP_AUTHORIZATION",
+ "HTTP_CONNECTION", "CONTENT_TYPE",
+ "CONTENT_LENGTH", "HTTP_COOKIE", "HTTP_COOKIE2",
+ "HTTP_HOST", "HTTP_PRAGMA",
+ "HTTP_REFERER", "HTTP_USER_AGENT",
+ "HTTP_CACHE_CONTROL",
+ "HTTP_IF_MODIFIED_SINCE", "HTTP_IF_MATCH",
+ "HTTP_IF_NONE_MATCH",
+ "HTTP_IF_RANGE",
+ "HTTP_IF_UNMODIFIED_SINCE",
+ "HTTP_KEEP_ALIVE",
+ "HTTP_RANGE",
+ "HTTP_X_FORWARDED_FOR",
+ "HTTP_VIA",
+ "HTTP_TRANSFER_ENCODING"
+};
+
+static int CGI_HEADER_LEN[H_TRANSFER_ENCODING+1] = {
+ 11, 19, 20, 20, 18, 15, 12, 14, 11, 12, 9, 11, 12, 15, 18,
+ 22, 13, 18, 13, 24, 15, 10, 20, 8, 22
+};
+
+
+static const char *HTTP_HEADERS[H_TRANSFER_ENCODING+1] = {
+ "Accept", "Accept-Charset",
+ "Accept-Encoding",
+ "Accept-Language", "Authorization",
+ "Connection", "Content-Type",
+ "Content-Length", "Cookie", "Cookie2",
+ "Host", "Pragma",
+ "Referer", "User-Agent",
+ "Cache-Control",
+ "If-Modified-Since", "If-Match",
+ "If-None-Match",
+ "If-Range",
+ "If-Unmodified-Since",
+ "Keep-Alive",
+ "Range",
+ "X-Forwarded-For",
+ "Via",
+ "Transfer-Encoding"
+};
+
+static int HTTP_HEADER_LEN[H_TRANSFER_ENCODING+1] = {
+ 6, 14, 15, 15, 13, 10, 12, 14, 6, 7, 4, 6, 7, 10, /* user-agent */
+ 13,17, 8, 13, 8, 19, 10, 5, 15, 3, 17
+};
+
+static void lsapi_sigpipe( int sig )
+{
+}
+static void lsapi_siguser1( int sig )
+{
+ g_running = 0;
+}
+
+#ifndef sighandler_t
+typedef void (*sighandler_t)(int);
+#endif
+
+static void lsapi_signal(int signo, sighandler_t handler)
+{
+ struct sigaction sa;
+
+ sigaction(signo, NULL, &sa);
+
+ if (sa.sa_handler == SIG_DFL) {
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sa.sa_handler = handler;
+ sigaction(signo, &sa, NULL);
+ }
+}
+
+
+static inline void lsapi_buildPacketHeader( struct lsapi_packet_header * pHeader,
+ char type, int len )
+{
+ pHeader->m_versionB0 = LSAPI_VERSION_B0; /* LSAPI protocol version */
+ pHeader->m_versionB1 = LSAPI_VERSION_B1;
+ pHeader->m_type = type;
+ pHeader->m_flag = LSAPI_ENDIAN;
+ pHeader->m_packetLen.m_iLen = len;
+}
+
+static int lsapi_set_nblock( int fd, int nonblock )
+{
+ int val = fcntl( fd, F_GETFL, 0 );
+ if ( nonblock )
+ {
+ if (!( val & O_NONBLOCK ))
+ {
+ return fcntl( fd, F_SETFL, val | O_NONBLOCK );
+ }
+ }
+ else
+ {
+ if ( val & O_NONBLOCK )
+ {
+ return fcntl( fd, F_SETFL, val &(~O_NONBLOCK) );
+ }
+ }
+ return 0;
+}
+
+
+static int lsapi_close( int fd )
+{
+ int ret;
+ while( 1 ) {
+ ret = close( fd );
+ if (( ret == -1 )&&( errno == EINTR )&&(g_running)) {
+ continue;
+ }
+ return ret;
+ }
+}
+
+static inline int lsapi_read( int fd, void * pBuf, int len )
+{
+ int ret;
+ while( 1 ) {
+ ret = read( fd, (char *)pBuf, len );
+ if (( ret == -1 )&&( errno == EINTR )&&(g_running)) {
+ continue;
+ }
+ return ret;
+ }
+}
+
+
+static int lsapi_writev( int fd, struct iovec ** pVec, int count, int totalLen )
+{
+ int ret;
+ int left = totalLen;
+ int n = count;
+ while(( left > 0 )&&g_running ) {
+ ret = writev( fd, *pVec, n );
+ if ( ret > 0 ) {
+ left -= ret;
+ if (( left <= 0)||( !g_running )) {
+ return totalLen - left;
+ }
+ while( ret > 0 ) {
+ if ( (*pVec)->iov_len <= ret ) {
+ ret -= (*pVec)->iov_len;
+ ++(*pVec);
+ } else {
+ (*pVec)->iov_base = (char *)(*pVec)->iov_base + ret;
+ (*pVec)->iov_len -= ret;
+ break;
+ }
+ }
+ } else if ( ret == -1 ) {
+ if ( errno == EAGAIN ) {
+ if ( totalLen - left > 0 ) {
+ return totalLen - left;
+ } else {
+ return -1;
+ }
+ } else {
+ if ( errno != EINTR ) {
+ return ret;
+ }
+ }
+ }
+ }
+ return totalLen - left;
+}
+
+static inline int allocateBuf( LSAPI_Request * pReq, int size )
+{
+ char * pBuf = (char *)realloc( pReq->m_pReqBuf, size );
+ if ( pBuf ) {
+ pReq->m_pReqBuf = pBuf;
+ pReq->m_reqBufSize = size;
+ pReq->m_pHeader = (struct lsapi_req_header *)pReq->m_pReqBuf;
+ return 0;
+ }
+ return -1;
+}
+
+
+static int allocateIovec( LSAPI_Request * pReq, int n )
+{
+ struct iovec * p = (struct iovec *)realloc(
+ pReq->m_pIovec, sizeof(struct iovec) * n );
+ if ( !p ) {
+ return -1;
+ }
+ pReq->m_pIovecToWrite = p + ( pReq->m_pIovecToWrite - pReq->m_pIovec );
+ pReq->m_pIovecCur = p + ( pReq->m_pIovecCur - pReq->m_pIovec );
+ pReq->m_pIovec = p;
+ pReq->m_pIovecEnd = p + n;
+ return 0;
+}
+
+static int allocateRespHeaderBuf( LSAPI_Request * pReq, int size )
+{
+ char * p = (char *)realloc( pReq->m_pRespHeaderBuf, size );
+ if ( !p ) {
+ return -1;
+ }
+ pReq->m_pRespHeaderBufPos = p + ( pReq->m_pRespHeaderBufPos - pReq->m_pRespHeaderBuf );
+ pReq->m_pRespHeaderBuf = p;
+ pReq->m_pRespHeaderBufEnd = p + size;
+ return 0;
+}
+
+
+static inline int verifyHeader( struct lsapi_packet_header * pHeader, char pktType )
+{
+ if (( LSAPI_VERSION_B0 != pHeader->m_versionB0 )||
+ ( LSAPI_VERSION_B1 != pHeader->m_versionB1 )||
+ ( pktType != pHeader->m_type )) {
+ return -1;
+ }
+ if ( LSAPI_ENDIAN != (pHeader->m_flag & LSAPI_ENDIAN_BIT )) {
+ register char b;
+ b = pHeader->m_packetLen.m_bytes[0];
+ pHeader->m_packetLen.m_bytes[0] = pHeader->m_packetLen.m_bytes[3];
+ pHeader->m_packetLen.m_bytes[3] = b;
+ b = pHeader->m_packetLen.m_bytes[1];
+ pHeader->m_packetLen.m_bytes[1] = pHeader->m_packetLen.m_bytes[2];
+ pHeader->m_packetLen.m_bytes[2] = b;
+ }
+ return pHeader->m_packetLen.m_iLen;
+}
+
+static int allocateEnvList( struct LSAPI_key_value_pair ** pEnvList,
+ int *curSize, int newSize )
+{
+ struct LSAPI_key_value_pair * pBuf;
+ if ( *curSize >= newSize ) {
+ return 0;
+ }
+ if ( newSize > 8192 ) {
+ return -1;
+ }
+ pBuf = (struct LSAPI_key_value_pair *)realloc( *pEnvList, newSize *
+ sizeof(struct LSAPI_key_value_pair) );
+ if ( pBuf ) {
+ *pEnvList = pBuf;
+ *curSize = newSize;
+ return 0;
+ } else {
+ return -1;
+ }
+
+}
+
+static inline int isPipe( int fd )
+{
+ char achPeer[128];
+ socklen_t len = 128;
+ if (( getpeername( fd, (struct sockaddr *)achPeer, &len ) != 0 )&&
+ ( errno == ENOTCONN )) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+static int parseEnv( struct LSAPI_key_value_pair * pEnvList, int count,
+ char **pBegin, char * pEnd )
+{
+ struct LSAPI_key_value_pair * pEnvEnd;
+ int keyLen = 0, valLen = 0;
+ if ( count > 8192 ) {
+ return -1;
+ }
+ pEnvEnd = pEnvList + count;
+ while( pEnvList != pEnvEnd ) {
+ if ( pEnd - *pBegin < 4 ) {
+ return -1;
+ }
+ keyLen = *((unsigned char *)((*pBegin)++));
+ keyLen = (keyLen << 8) + *((unsigned char *)((*pBegin)++));
+ valLen = *((unsigned char *)((*pBegin)++));
+ valLen = (valLen << 8) + *((unsigned char *)((*pBegin)++));
+ if ( *pBegin + keyLen + valLen > pEnd ) {
+ return -1;
+ }
+ if (( !keyLen )||( !valLen )) {
+ return -1;
+ }
+
+ pEnvList->pKey = *pBegin;
+ *pBegin += keyLen;
+ pEnvList->pValue = *pBegin;
+ *pBegin += valLen;
+
+ pEnvList->keyLen = keyLen - 1;
+ pEnvList->valLen = valLen - 1;
+ ++pEnvList;
+ }
+ if ( memcmp( *pBegin, "\0\0\0\0", 4 ) != 0 ) {
+ return -1;
+ }
+ *pBegin += 4;
+ return 0;
+}
+
+static inline void swapIntEndian( int * pInteger )
+{
+ char * p = (char *)pInteger;
+ register char b;
+ b = p[0];
+ p[0] = p[3];
+ p[3] = b;
+ b = p[1];
+ p[1] = p[2];
+ p[2] = b;
+
+}
+
+static inline void fixEndian( LSAPI_Request * pReq )
+{
+ struct lsapi_req_header *p= pReq->m_pHeader;
+ swapIntEndian( &p->m_httpHeaderLen );
+ swapIntEndian( &p->m_reqBodyLen );
+ swapIntEndian( &p->m_scriptFileOff );
+ swapIntEndian( &p->m_scriptNameOff );
+ swapIntEndian( &p->m_queryStringOff );
+ swapIntEndian( &p->m_requestMethodOff );
+ swapIntEndian( &p->m_cntUnknownHeaders );
+ swapIntEndian( &p->m_cntEnv );
+ swapIntEndian( &p->m_cntSpecialEnv );
+}
+
+static void fixHeaderIndexEndian( LSAPI_Request * pReq )
+{
+ int i;
+ for( i = 0; i < H_TRANSFER_ENCODING; ++i ) {
+ if ( pReq->m_pHeaderIndex->m_headerOff[i] ) {
+ register char b;
+ char * p = (char *)(&pReq->m_pHeaderIndex->m_headerLen[i]);
+ b = p[0];
+ p[0] = p[1];
+ p[1] = b;
+ swapIntEndian( &pReq->m_pHeaderIndex->m_headerOff[i] );
+ }
+ }
+ if ( pReq->m_pHeader->m_cntUnknownHeaders > 0 ) {
+ struct lsapi_header_offset * pCur, *pEnd;
+ pCur = pReq->m_pUnknownHeader;
+ pEnd = pCur + pReq->m_pHeader->m_cntUnknownHeaders;
+ while( pCur < pEnd ) {
+ swapIntEndian( &pCur->nameOff );
+ swapIntEndian( &pCur->nameLen );
+ swapIntEndian( &pCur->valueOff );
+ swapIntEndian( &pCur->valueLen );
+ ++pCur;
+ }
+ }
+}
+
+static int parseRequest( LSAPI_Request * pReq, int totalLen )
+{
+ int shouldFixEndian;
+ char * pBegin = pReq->m_pReqBuf + sizeof( struct lsapi_req_header );
+ char * pEnd = pReq->m_pReqBuf + totalLen;
+ shouldFixEndian = ( LSAPI_ENDIAN != (
+ pReq->m_pHeader->m_pktHeader.m_flag & LSAPI_ENDIAN_BIT ) );
+ if ( shouldFixEndian ) {
+ fixEndian( pReq );
+ }
+ if ( (pReq->m_specialEnvListSize < pReq->m_pHeader->m_cntSpecialEnv )&&
+ allocateEnvList( &pReq->m_pSpecialEnvList,
+ &pReq->m_specialEnvListSize,
+ pReq->m_pHeader->m_cntSpecialEnv ) == -1 ) {
+ return -1;
+ }
+ if ( (pReq->m_envListSize < pReq->m_pHeader->m_cntEnv )&&
+ allocateEnvList( &pReq->m_pEnvList, &pReq->m_envListSize,
+ pReq->m_pHeader->m_cntEnv ) == -1 ) {
+ return -1;
+ }
+ if ( parseEnv( pReq->m_pSpecialEnvList,
+ pReq->m_pHeader->m_cntSpecialEnv,
+ &pBegin, pEnd ) == -1 ) {
+ return -1;
+ }
+ if ( parseEnv( pReq->m_pEnvList, pReq->m_pHeader->m_cntEnv,
+ &pBegin, pEnd ) == -1 ) {
+ return -1;
+ }
+ pReq->m_pScriptFile = pReq->m_pReqBuf + pReq->m_pHeader->m_scriptFileOff;
+ pReq->m_pScriptName = pReq->m_pReqBuf + pReq->m_pHeader->m_scriptNameOff;
+ pReq->m_pQueryString = pReq->m_pReqBuf + pReq->m_pHeader->m_queryStringOff;
+ pReq->m_pRequestMethod = pReq->m_pReqBuf + pReq->m_pHeader->m_requestMethodOff;
+
+ pBegin = pReq->m_pReqBuf + (( pBegin - pReq->m_pReqBuf + 7 ) & (~0x7));
+ pReq->m_pHeaderIndex = ( struct lsapi_http_header_index * )pBegin;
+ pBegin += sizeof( struct lsapi_http_header_index );
+
+ pReq->m_pUnknownHeader = (struct lsapi_header_offset *)pBegin;
+ pBegin += sizeof( struct lsapi_header_offset) *
+ pReq->m_pHeader->m_cntUnknownHeaders;
+
+ pReq->m_pHttpHeader = pBegin;
+ pBegin += pReq->m_pHeader->m_httpHeaderLen;
+ if ( pBegin != pEnd ) {
+ return -1;
+ }
+
+ if ( shouldFixEndian ) {
+ fixHeaderIndexEndian( pReq );
+ }
+
+ return 0;
+}
+
+static int s_accept_notify = 0;
+
+static struct lsapi_packet_header ack = {'L', 'S',
+ LSAPI_REQ_RECEIVED, LSAPI_ENDIAN, {LSAPI_PACKET_HEADER_LEN} };
+static inline int notify_req_received( int fd )
+{
+ if ( write( fd, &ack, LSAPI_PACKET_HEADER_LEN )
+ < LSAPI_PACKET_HEADER_LEN ) {
+ return -1;
+ }
+ return 0;
+}
+
+
+static int readReq( LSAPI_Request * pReq )
+{
+ int len;
+ int packetLen;
+ if ( !pReq ) {
+ return -1;
+ }
+ if ( pReq->m_reqBufSize < 8192 ) {
+ if ( allocateBuf( pReq, 8192 ) == -1 ) {
+ return -1;
+ }
+ }
+
+ while ( pReq->m_bufRead < LSAPI_PACKET_HEADER_LEN ) {
+ len = lsapi_read( pReq->m_fd, pReq->m_pReqBuf, pReq->m_reqBufSize );
+ if ( len <= 0 ) {
+ return -1;
+ }
+ pReq->m_bufRead += len;
+ }
+ pReq->m_reqState = LSAPI_ST_REQ_HEADER;
+
+ packetLen = verifyHeader( &pReq->m_pHeader->m_pktHeader, LSAPI_BEGIN_REQUEST );
+ if ( packetLen < 0 ) {
+ return -1;
+ }
+ if ( packetLen > LSAPI_MAX_HEADER_LEN ) {
+ return -1;
+ }
+
+ if ( packetLen + 1024 > pReq->m_reqBufSize ) {
+ if ( allocateBuf( pReq, packetLen + 1024 ) == -1 ) {
+ return -1;
+ }
+ }
+ while( packetLen > pReq->m_bufRead ) {
+ len = lsapi_read( pReq->m_fd, pReq->m_pReqBuf + pReq->m_bufRead, packetLen - pReq->m_bufRead );
+ if ( len <= 0 ) {
+ return -1;
+ }
+ pReq->m_bufRead += len;
+ }
+ if ( parseRequest( pReq, packetLen ) < 0 ) {
+ return -1;
+ }
+ pReq->m_bufProcessed = packetLen;
+ pReq->m_reqState = LSAPI_ST_REQ_BODY | LSAPI_ST_RESP_HEADER;
+
+ if ( !s_accept_notify )
+ return notify_req_received( pReq->m_fd );
+ else
+ return 0;
+}
+
+
+
+int LSAPI_Init(void)
+{
+ if ( !g_inited ) {
+ lsapi_signal(SIGPIPE, lsapi_sigpipe);
+ lsapi_signal(SIGUSR1, lsapi_siguser1);
+
+#if defined(SIGXFSZ) && defined(SIG_IGN)
+ signal(SIGXFSZ, SIG_IGN);
+#endif
+ /* let STDOUT function as STDERR,
+ just in case writing to STDOUT directly */
+ dup2( 2, 1 );
+
+ if ( LSAPI_InitRequest( &g_req, LSAPI_SOCK_FILENO ) == -1 ) {
+ return -1;
+ }
+ g_inited = 1;
+ s_ppid = getppid();
+ }
+ return 0;
+}
+
+void LSAPI_Stop(void)
+{
+ g_running = 0;
+}
+
+int LSAPI_IsRunning(void)
+{
+ return g_running;
+}
+
+int LSAPI_InitRequest( LSAPI_Request * pReq, int fd )
+{
+ if ( !pReq ) {
+ return -1;
+ }
+ memset( pReq, 0, sizeof( LSAPI_Request ) );
+ if ( allocateIovec( pReq, 16 ) == -1 ) {
+ return -1;
+ }
+ pReq->m_pRespBuf = pReq->m_pRespBufPos = (char *)malloc( LSAPI_RESP_BUF_SIZE );
+ if ( !pReq->m_pRespBuf ) {
+ return -1;
+ }
+ pReq->m_pRespBufEnd = pReq->m_pRespBuf + LSAPI_RESP_BUF_SIZE;
+ pReq->m_pIovecCur = pReq->m_pIovecToWrite = pReq->m_pIovec + 1;
+ pReq->m_respPktHeaderEnd = &pReq->m_respPktHeader[5];
+ if ( allocateRespHeaderBuf( pReq, LSAPI_INIT_RESP_HEADER_LEN ) == -1 ) {
+ return -1;
+ }
+
+ if ( isPipe( fd ) ) {
+ pReq->m_fdListen = -1;
+ pReq->m_fd = fd;
+ } else {
+ pReq->m_fdListen = fd;
+ pReq->m_fd = -1;
+ lsapi_set_nblock( fd, 1 );
+ }
+ return 0;
+}
+
+int LSAPI_Is_Listen( void )
+{
+ return LSAPI_Is_Listen_r( &g_req );
+}
+
+int LSAPI_Is_Listen_r( LSAPI_Request * pReq)
+{
+ return pReq->m_fdListen != -1;
+}
+
+
+
+int LSAPI_Accept_r( LSAPI_Request * pReq )
+{
+ char achPeer[128];
+ socklen_t len;
+ int nodelay = 1;
+
+ if ( !pReq ) {
+ return -1;
+ }
+ if ( LSAPI_Finish_r( pReq ) == -1 ) {
+ return -1;
+ }
+ while( g_running ) {
+ if ( pReq->m_fd == -1 ) {
+ if ( pReq->m_fdListen != -1) {
+ len = sizeof( achPeer );
+ pReq->m_fd = accept( pReq->m_fdListen,
+ (struct sockaddr *)&achPeer, &len );
+ if ( pReq->m_fd == -1 ) {
+ if (( errno == EINTR )||( errno == EAGAIN)) {
+ continue;
+ } else {
+ return -1;
+ }
+ } else {
+ lsapi_set_nblock( pReq->m_fd , 0 );
+ if (((struct sockaddr *)&achPeer)->sa_family == AF_INET ) {
+ setsockopt(pReq->m_fd, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&nodelay, sizeof(nodelay));
+ }
+
+ if ( s_accept_notify )
+ return notify_req_received( pReq->m_fd );
+
+ }
+ } else {
+ return -1;
+ }
+ }
+ if ( !readReq( pReq ) ) {
+ break;
+ }
+ lsapi_close( pReq->m_fd );
+ pReq->m_fd = -1;
+ LSAPI_Reset_r( pReq );
+ }
+ return 0;
+}
+
+static struct lsapi_packet_header finish = {'L', 'S',
+ LSAPI_RESP_END, LSAPI_ENDIAN, {LSAPI_PACKET_HEADER_LEN} };
+
+int LSAPI_Finish_r( LSAPI_Request * pReq )
+{
+ /* finish req body */
+ if ( !pReq ) {
+ return -1;
+ }
+ if (pReq->m_reqState) {
+ if ( pReq->m_fd != -1 ) {
+ if ( pReq->m_reqState & LSAPI_ST_RESP_HEADER ) {
+ LSAPI_FinalizeRespHeaders_r( pReq );
+ }
+ if ( pReq->m_pRespBufPos != pReq->m_pRespBuf ) {
+ Flush_RespBuf_r( pReq );
+ }
+
+ pReq->m_pIovecCur->iov_base = (void *)&finish;
+ pReq->m_pIovecCur->iov_len = LSAPI_PACKET_HEADER_LEN;
+ pReq->m_totalLen += LSAPI_PACKET_HEADER_LEN;
+ ++pReq->m_pIovecCur;
+ LSAPI_Flush_r( pReq );
+ }
+ LSAPI_Reset_r( pReq );
+ }
+ return 0;
+}
+
+
+void LSAPI_Reset_r( LSAPI_Request * pReq )
+{
+ pReq->m_pRespBufPos = pReq->m_pRespBuf;
+ pReq->m_pIovecCur = pReq->m_pIovecToWrite = pReq->m_pIovec + 1;
+ pReq->m_pRespHeaderBufPos = pReq->m_pRespHeaderBuf;
+
+ memset( &pReq->m_pHeaderIndex, 0,
+ (char *)(pReq->m_respHeaderLen) - (char *)&pReq->m_pHeaderIndex );
+}
+
+
+int LSAPI_Release_r( LSAPI_Request * pReq )
+{
+ if ( pReq->m_pReqBuf ) {
+ free( pReq->m_pReqBuf );
+ }
+ if ( pReq->m_pSpecialEnvList ) {
+ free( pReq->m_pSpecialEnvList );
+ }
+ if ( pReq->m_pEnvList ) {
+ free( pReq->m_pEnvList );
+ }
+ if ( pReq->m_pRespHeaderBuf ) {
+ free( pReq->m_pRespHeaderBuf );
+ }
+ return 0;
+}
+
+
+char * LSAPI_GetHeader_r( LSAPI_Request * pReq, int headerIndex )
+{
+ int off;
+ if ( !pReq || ((unsigned int)headerIndex > H_TRANSFER_ENCODING) ) {
+ return NULL;
+ }
+ off = pReq->m_pHeaderIndex->m_headerOff[ headerIndex ];
+ if ( !off ) {
+ return NULL;
+ }
+ if ( *(pReq->m_pHttpHeader + off +
+ pReq->m_pHeaderIndex->m_headerLen[ headerIndex ]) ) {
+ *( pReq->m_pHttpHeader + off +
+ pReq->m_pHeaderIndex->m_headerLen[ headerIndex ]) = 0;
+ }
+ return pReq->m_pHttpHeader + off;
+}
+
+static int readBodyToReqBuf( LSAPI_Request * pReq )
+{
+ int bodyLeft;
+ int len = pReq->m_bufRead - pReq->m_bufProcessed;
+ if ( len > 0 ) {
+ return len;
+ }
+ pReq->m_bufRead = pReq->m_bufProcessed = pReq->m_pHeader->m_pktHeader.m_packetLen.m_iLen;
+
+ bodyLeft = pReq->m_pHeader->m_reqBodyLen - pReq->m_reqBodyRead;
+ len = pReq->m_reqBufSize - pReq->m_bufRead;
+ if ( len < 0 ) {
+ return -1;
+ }
+ if ( len > bodyLeft ) {
+ len = bodyLeft;
+ }
+ len = lsapi_read( pReq->m_fd, pReq->m_pReqBuf + pReq->m_bufRead, len );
+ if ( len > 0 ) {
+ pReq->m_bufRead += len;
+ }
+ return len;
+}
+
+
+int LSAPI_ReqBodyGetChar_r( LSAPI_Request * pReq )
+{
+ if (!pReq || (pReq->m_fd ==-1) ) {
+ return EOF;
+ }
+ if ( pReq->m_bufProcessed >= pReq->m_bufRead ) {
+ if ( readBodyToReqBuf( pReq ) <= 0 ) {
+ return EOF;
+ }
+ }
+ ++pReq->m_reqBodyRead;
+ return (unsigned char)*(pReq->m_pReqBuf + pReq->m_bufProcessed++);
+}
+
+
+
+int LSAPI_ReqBodyGetLine_r( LSAPI_Request * pReq, char * pBuf, int bufLen, int *getLF )
+{
+ int len;
+ int left;
+ char * pBufEnd = pBuf + bufLen - 1;
+ char * pBufCur = pBuf;
+ char * pCur;
+ char * p;
+ if (!pReq || (pReq->m_fd ==-1) ||( !pBuf )||(bufLen < 0 )|| !getLF ) {
+ return -1;
+ }
+ *getLF = 0;
+ while( (left = pBufEnd - pBufCur ) > 0 ) {
+
+ len = pReq->m_bufRead - pReq->m_bufProcessed;
+ if ( len <= 0 ) {
+ if ( (len = readBodyToReqBuf( pReq )) <= 0 ) {
+ *getLF = 1;
+ break;
+ }
+ }
+ if ( len > left ) {
+ len = left;
+ }
+ pCur = pReq->m_pReqBuf + pReq->m_bufProcessed;
+ p = memchr( pCur, '\n', len );
+ if ( p ) {
+ len = p - pCur + 1;
+ }
+ memmove( pBufCur, pCur, len );
+ pBufCur += len;
+ pReq->m_bufProcessed += len;
+
+ pReq->m_reqBodyRead += len;
+
+ if ( p ) {
+ *getLF = 1;
+ break;
+ }
+ }
+ *pBufCur = 0;
+
+ return pBufCur - pBuf;
+}
+
+
+int LSAPI_ReadReqBody_r( LSAPI_Request * pReq, char * pBuf, int bufLen )
+{
+ int len;
+ int total;
+ /* char *pOldBuf = pBuf; */
+ if (!pReq || (pReq->m_fd ==-1) || ( !pBuf )||(bufLen < 0 )) {
+ return -1;
+ }
+ total = pReq->m_pHeader->m_reqBodyLen - pReq->m_reqBodyRead;
+
+ if ( total <= 0 ) {
+ return 0;
+ }
+ if ( total < bufLen ) {
+ bufLen = total;
+ }
+
+ total = 0;
+ len = pReq->m_bufRead - pReq->m_bufProcessed;
+ if ( len > 0 ) {
+ if ( len > bufLen ) {
+ len = bufLen;
+ }
+ memmove( pBuf, pReq->m_pReqBuf + pReq->m_bufProcessed, len );
+ pReq->m_bufProcessed += len;
+ total += len;
+ pBuf += len;
+ bufLen -= len;
+ }
+ while( bufLen > 0 ) {
+ len = lsapi_read( pReq->m_fd, pBuf, bufLen );
+ if ( len > 0 ) {
+ total += len;
+ pBuf += len;
+ bufLen -= len;
+ } else {
+ if ( len <= 0 ) {
+ if ( !total) {
+ return -1;
+ }
+ break;
+ }
+ }
+ }
+ pReq->m_reqBodyRead += total;
+ return total;
+
+}
+
+
+int LSAPI_Write_r( LSAPI_Request * pReq, const char * pBuf, int len )
+{
+ struct lsapi_packet_header * pHeader;
+ const char * pEnd;
+ const char * p;
+ int bufLen;
+ int toWrite;
+ int packetLen;
+
+ if ( !pReq || !pBuf || (pReq->m_fd == -1) ) {
+ return -1;
+ }
+ if ( len < pReq->m_pRespBufEnd - pReq->m_pRespBufPos ) {
+ memmove( pReq->m_pRespBufPos, pBuf, len );
+ pReq->m_pRespBufPos += len;
+ return len;
+ }
+
+ if ( pReq->m_reqState & LSAPI_ST_RESP_HEADER ) {
+ LSAPI_FinalizeRespHeaders_r( pReq );
+ }
+ pReq->m_reqState |= LSAPI_ST_RESP_BODY;
+
+ pHeader = pReq->m_respPktHeader;
+ p = pBuf;
+ pEnd = pBuf + len;
+ bufLen = pReq->m_pRespBufPos - pReq->m_pRespBuf;
+
+ while( ( toWrite = pEnd - p ) > 0 ) {
+ packetLen = toWrite + bufLen;
+ if ( LSAPI_MAX_DATA_PACKET_LEN < packetLen) {
+ packetLen = LSAPI_MAX_DATA_PACKET_LEN;
+ toWrite = packetLen - bufLen;
+ }
+
+ lsapi_buildPacketHeader( pHeader, LSAPI_RESP_STREAM,
+ packetLen + LSAPI_PACKET_HEADER_LEN );
+ pReq->m_totalLen += packetLen + LSAPI_PACKET_HEADER_LEN;
+
+ pReq->m_pIovecCur->iov_base = (void *)pHeader;
+ pReq->m_pIovecCur->iov_len = LSAPI_PACKET_HEADER_LEN;
+ ++pReq->m_pIovecCur;
+ ++pHeader;
+ if ( bufLen > 0 ) {
+ pReq->m_pIovecCur->iov_base = (void *)pReq->m_pRespBuf;
+ pReq->m_pIovecCur->iov_len = bufLen;
+ pReq->m_pRespBufPos = pReq->m_pRespBuf;
+ ++pReq->m_pIovecCur;
+ bufLen = 0;
+ }
+
+ pReq->m_pIovecCur->iov_base = (void *)p;
+ pReq->m_pIovecCur->iov_len = toWrite;
+ ++pReq->m_pIovecCur;
+ p += toWrite;
+
+ if ( pHeader >= pReq->m_respPktHeaderEnd - 1) {
+ if ( LSAPI_Flush_r( pReq ) == -1 ) {
+ return -1;
+ }
+ pHeader = pReq->m_respPktHeader;
+ }
+ }
+ if ( pHeader != pReq->m_respPktHeader ) {
+ if ( LSAPI_Flush_r( pReq ) == -1 ) {
+ return -1;
+ }
+ }
+ return p - pBuf;
+}
+
+void Flush_RespBuf_r( LSAPI_Request * pReq )
+{
+ struct lsapi_packet_header * pHeader = pReq->m_respPktHeader;
+ int bufLen = pReq->m_pRespBufPos - pReq->m_pRespBuf;
+ pReq->m_reqState |= LSAPI_ST_RESP_BODY;
+ lsapi_buildPacketHeader( pHeader, LSAPI_RESP_STREAM,
+ bufLen + LSAPI_PACKET_HEADER_LEN );
+ pReq->m_totalLen += bufLen + LSAPI_PACKET_HEADER_LEN;
+
+ pReq->m_pIovecCur->iov_base = (void *)pHeader;
+ pReq->m_pIovecCur->iov_len = LSAPI_PACKET_HEADER_LEN;
+ ++pReq->m_pIovecCur;
+ ++pHeader;
+ if ( bufLen > 0 ) {
+ pReq->m_pIovecCur->iov_base = (void *)pReq->m_pRespBuf;
+ pReq->m_pIovecCur->iov_len = bufLen;
+ pReq->m_pRespBufPos = pReq->m_pRespBuf;
+ ++pReq->m_pIovecCur;
+ bufLen = 0;
+ }
+}
+
+
+
+
+int LSAPI_Flush_r( LSAPI_Request * pReq )
+{
+ int ret = 0;
+ int n;
+ if ( !pReq ) {
+ return -1;
+ }
+ n = pReq->m_pIovecCur - pReq->m_pIovecToWrite;
+ if (( 0 == n )&&( pReq->m_pRespBufPos == pReq->m_pRespBuf )) {
+ return 0;
+ }
+ if ( pReq->m_fd == -1 ) {
+ pReq->m_pRespBufPos = pReq->m_pRespBuf;
+ pReq->m_totalLen = 0;
+ pReq->m_pIovecCur = pReq->m_pIovecToWrite = pReq->m_pIovec;
+ return -1;
+ }
+ if ( pReq->m_reqState & LSAPI_ST_RESP_HEADER ) {
+ LSAPI_FinalizeRespHeaders_r( pReq );
+ }
+ if ( pReq->m_pRespBufPos != pReq->m_pRespBuf ) {
+ Flush_RespBuf_r( pReq );
+ }
+
+ n = pReq->m_pIovecCur - pReq->m_pIovecToWrite;
+ if ( n > 0 ) {
+
+ ret = lsapi_writev( pReq->m_fd, &pReq->m_pIovecToWrite,
+ n, pReq->m_totalLen );
+ if ( ret < pReq->m_totalLen ) {
+ lsapi_close( pReq->m_fd );
+ pReq->m_fd = -1;
+ ret = -1;
+ }
+ pReq->m_totalLen = 0;
+ pReq->m_pIovecCur = pReq->m_pIovecToWrite = pReq->m_pIovec;
+ }
+ return ret;
+}
+
+
+int LSAPI_Write_Stderr_r( LSAPI_Request * pReq, const char * pBuf, int len )
+{
+ struct lsapi_packet_header header;
+ const char * pEnd;
+ const char * p;
+ int packetLen;
+ int totalLen;
+ int ret;
+ struct iovec iov[2];
+ struct iovec *pIov;
+
+ if ( !pReq ) {
+ return -1;
+ }
+ if (( pReq->m_fd == -1 )||(pReq->m_fd == pReq->m_fdListen )) {
+ return write( 2, pBuf, len );
+ }
+ if ( pReq->m_pRespBufPos != pReq->m_pRespBuf ) {
+ LSAPI_Flush_r( pReq );
+ }
+
+ p = pBuf;
+ pEnd = pBuf + len;
+
+ while( ( packetLen = pEnd - p ) > 0 ) {
+ if ( LSAPI_MAX_DATA_PACKET_LEN < packetLen) {
+ packetLen = LSAPI_MAX_DATA_PACKET_LEN;
+ }
+
+ lsapi_buildPacketHeader( &header, LSAPI_STDERR_STREAM,
+ packetLen + LSAPI_PACKET_HEADER_LEN );
+ totalLen = packetLen + LSAPI_PACKET_HEADER_LEN;
+
+ iov[0].iov_base = (void *)&header;
+ iov[0].iov_len = LSAPI_PACKET_HEADER_LEN;
+
+ iov[1].iov_base = (void *)p;
+ iov[1].iov_len = packetLen;
+ p += packetLen;
+ pIov = iov;
+ ret = lsapi_writev( pReq->m_fd, &pIov,
+ 2, totalLen );
+ if ( ret < totalLen ) {
+ lsapi_close( pReq->m_fd );
+ pReq->m_fd = -1;
+ ret = -1;
+ }
+ }
+ return p - pBuf;
+}
+
+static char * GetHeaderVar( LSAPI_Request * pReq, const char * name )
+{
+ int i;
+ for( i = 0; i < H_TRANSFER_ENCODING; ++i ) {
+ if ( pReq->m_pHeaderIndex->m_headerOff[i] ) {
+ if ( strcmp( name, CGI_HEADERS[i] ) == 0 ) {
+ return pReq->m_pHttpHeader + pReq->m_pHeaderIndex->m_headerOff[i];
+ }
+ }
+ }
+ if ( pReq->m_pHeader->m_cntUnknownHeaders > 0 ) {
+ const char *p;
+ char *pKey;
+ char *pKeyEnd;
+ int keyLen;
+ struct lsapi_header_offset * pCur, *pEnd;
+ pCur = pReq->m_pUnknownHeader;
+ pEnd = pCur + pReq->m_pHeader->m_cntUnknownHeaders;
+ while( pCur < pEnd ) {
+ pKey = pReq->m_pHttpHeader + pCur->nameOff;
+ keyLen = pCur->nameLen;
+ pKeyEnd = pKey + keyLen;
+ p = &name[5];
+
+ while(( pKey < pKeyEnd )&&( *p )) {
+ char ch = toupper( *pKey );
+ if ((ch != *p )||(( *p == '_' )&&( ch != '-'))) {
+ break;
+ }
+ ++p; ++pKey;
+ }
+ if (( pKey == pKeyEnd )&& (!*p )) {
+ return pReq->m_pHttpHeader + pCur->valueOff;
+ }
+ ++pCur;
+ }
+ }
+ return NULL;
+}
+
+
+char * LSAPI_GetEnv_r( LSAPI_Request * pReq, const char * name )
+{
+ struct LSAPI_key_value_pair * pBegin = pReq->m_pEnvList;
+ struct LSAPI_key_value_pair * pEnd = pBegin + pReq->m_pHeader->m_cntEnv;
+ if ( !pReq || !name ) {
+ return NULL;
+ }
+ if ( strncmp( name, "HTTP_", 5 ) == 0 ) {
+ return GetHeaderVar( pReq, name );
+ }
+ while( pBegin < pEnd ) {
+ if ( strcmp( name, pBegin->pKey ) == 0 ) {
+ return pBegin->pValue;
+ }
+ ++pBegin;
+ }
+ return NULL;
+}
+
+int LSAPI_ForeachOrgHeader_r( LSAPI_Request * pReq,
+ LSAPI_CB_EnvHandler fn, void * arg )
+{
+ int i;
+ int len = 0;
+ char * pValue;
+ int ret;
+ int count = 0;
+ if ( !pReq || !fn ) {
+ return -1;
+ }
+ for( i = 0; i < H_TRANSFER_ENCODING; ++i ) {
+ if ( pReq->m_pHeaderIndex->m_headerOff[i] ) {
+ len = pReq->m_pHeaderIndex->m_headerLen[i];
+ pValue = pReq->m_pHttpHeader + pReq->m_pHeaderIndex->m_headerOff[i];
+ *(pValue + len ) = 0;
+ ret = (*fn)( HTTP_HEADERS[i], HTTP_HEADER_LEN[i],
+ pValue, len, arg );
+ ++count;
+ if ( ret <= 0 ) {
+ return ret;
+ }
+ }
+ }
+ if ( pReq->m_pHeader->m_cntUnknownHeaders > 0 ) {
+ char *pKey;
+ int keyLen;
+ struct lsapi_header_offset * pCur, *pEnd;
+ pCur = pReq->m_pUnknownHeader;
+ pEnd = pCur + pReq->m_pHeader->m_cntUnknownHeaders;
+ while( pCur < pEnd ) {
+ pKey = pReq->m_pHttpHeader + pCur->nameOff;
+ keyLen = pCur->nameLen;
+
+ pValue = pReq->m_pHttpHeader + pCur->valueOff;
+ *(pValue + pCur->valueLen ) = 0;
+ ret = (*fn)( pKey, keyLen,
+ pValue, pCur->valueLen, arg );
+ if ( ret <= 0 ) {
+ return ret;
+ }
+ ++pCur;
+ }
+ }
+ return count + pReq->m_pHeader->m_cntUnknownHeaders;
+
+}
+
+
+int LSAPI_ForeachHeader_r( LSAPI_Request * pReq,
+ LSAPI_CB_EnvHandler fn, void * arg )
+{
+ int i;
+ int len = 0;
+ char * pValue;
+ int ret;
+ int count = 0;
+ if ( !pReq || !fn ) {
+ return -1;
+ }
+ for( i = 0; i < H_TRANSFER_ENCODING; ++i ) {
+ if ( pReq->m_pHeaderIndex->m_headerOff[i] ) {
+ len = pReq->m_pHeaderIndex->m_headerLen[i];
+ pValue = pReq->m_pHttpHeader + pReq->m_pHeaderIndex->m_headerOff[i];
+ *(pValue + len ) = 0;
+ ret = (*fn)( CGI_HEADERS[i], CGI_HEADER_LEN[i],
+ pValue, len, arg );
+ ++count;
+ if ( ret <= 0 ) {
+ return ret;
+ }
+ }
+ }
+ if ( pReq->m_pHeader->m_cntUnknownHeaders > 0 ) {
+ char achHeaderName[256];
+ char *p;
+ char *pKey;
+ char *pKeyEnd ;
+ int keyLen;
+ struct lsapi_header_offset * pCur, *pEnd;
+ pCur = pReq->m_pUnknownHeader;
+ pEnd = pCur + pReq->m_pHeader->m_cntUnknownHeaders;
+ while( pCur < pEnd ) {
+ pKey = pReq->m_pHttpHeader + pCur->nameOff;
+ keyLen = pCur->nameLen;
+ if ( keyLen > 250 ) {
+ keyLen = 250;
+ }
+
+ pKeyEnd = pKey + keyLen;
+ memcpy( achHeaderName, "HTTP_", 5 );
+ p = &achHeaderName[5];
+
+ while( pKey < pKeyEnd ) {
+ char ch = *pKey++;
+ if ( ch == '-' ) {
+ *p++ = '_';
+ } else {
+ *p++ = toupper( ch );
+ }
+ }
+ *p = 0;
+ keyLen += 5;
+
+ pValue = pReq->m_pHttpHeader + pCur->valueOff;
+ *(pValue + pCur->valueLen ) = 0;
+ ret = (*fn)( achHeaderName, keyLen,
+ pValue, pCur->valueLen, arg );
+ if ( ret <= 0 ) {
+ return ret;
+ }
+ ++pCur;
+ }
+ }
+ return count + pReq->m_pHeader->m_cntUnknownHeaders;
+
+}
+
+static int EnvForeach( struct LSAPI_key_value_pair * pEnv,
+ int n, LSAPI_CB_EnvHandler fn, void * arg )
+{
+ struct LSAPI_key_value_pair * pEnd = pEnv + n;
+ int ret;
+ if ( !pEnv || !fn ) {
+ return -1;
+ }
+ while( pEnv < pEnd ) {
+ ret = (*fn)( pEnv->pKey, pEnv->keyLen,
+ pEnv->pValue, pEnv->valLen, arg );
+ if ( ret <= 0 ) {
+ return ret;
+ }
+ ++pEnv;
+ }
+ return n;
+}
+
+
+
+int LSAPI_ForeachEnv_r( LSAPI_Request * pReq,
+ LSAPI_CB_EnvHandler fn, void * arg )
+{
+ if ( !pReq || !fn ) {
+ return -1;
+ }
+ if ( pReq->m_pHeader->m_cntEnv > 0 ) {
+ return EnvForeach( pReq->m_pEnvList, pReq->m_pHeader->m_cntEnv,
+ fn, arg );
+ }
+ return 0;
+}
+
+
+
+int LSAPI_ForeachSpecialEnv_r( LSAPI_Request * pReq,
+ LSAPI_CB_EnvHandler fn, void * arg )
+{
+ if ( !pReq || !fn ) {
+ return -1;
+ }
+ if ( pReq->m_pHeader->m_cntSpecialEnv > 0 ) {
+ return EnvForeach( pReq->m_pSpecialEnvList,
+ pReq->m_pHeader->m_cntSpecialEnv,
+ fn, arg );
+ }
+ return 0;
+
+}
+
+
+
+int LSAPI_FinalizeRespHeaders_r( LSAPI_Request * pReq )
+{
+ if ( !pReq || !pReq->m_pIovec ) {
+ return -1;
+ }
+ if ( !( pReq->m_reqState & LSAPI_ST_RESP_HEADER ) ) {
+ return 0;
+ }
+ pReq->m_reqState &= ~LSAPI_ST_RESP_HEADER;
+ if ( pReq->m_pRespHeaderBufPos > pReq->m_pRespHeaderBuf ) {
+ pReq->m_pIovecCur->iov_base = (void *)pReq->m_pRespHeaderBuf;
+ pReq->m_pIovecCur->iov_len = pReq->m_pRespHeaderBufPos - pReq->m_pRespHeaderBuf;
+ pReq->m_totalLen += pReq->m_pIovecCur->iov_len;
+ ++pReq->m_pIovecCur;
+ }
+
+ pReq->m_pIovec->iov_len = sizeof( struct lsapi_resp_header)
+ + pReq->m_respHeader.m_respInfo.m_cntHeaders * sizeof( short );
+ pReq->m_totalLen += pReq->m_pIovec->iov_len;
+
+ lsapi_buildPacketHeader( &pReq->m_respHeader.m_pktHeader,
+ LSAPI_RESP_HEADER, pReq->m_totalLen );
+ pReq->m_pIovec->iov_base = (void *)&pReq->m_respHeader;
+ pReq->m_pIovecToWrite = pReq->m_pIovec;
+ return 0;
+}
+
+
+
+
+int LSAPI_AppendRespHeader_r( LSAPI_Request * pReq, char * pBuf, int len )
+{
+ if ( !pReq || !pBuf || len <= 0 || len > LSAPI_RESP_HTTP_HEADER_MAX ) {
+ return -1;
+ }
+ if ( pReq->m_reqState & LSAPI_ST_RESP_BODY ) {
+ return -1;
+ }
+ if ( pReq->m_respHeader.m_respInfo.m_cntHeaders >= LSAPI_MAX_RESP_HEADERS ) {
+ return -1;
+ }
+ if ( pReq->m_pRespHeaderBufPos + len + 1 > pReq->m_pRespHeaderBufEnd ) {
+ int newlen = pReq->m_pRespHeaderBufPos + len + 4096 - pReq->m_pRespHeaderBuf;
+ newlen -= newlen % 4096;
+ if ( allocateRespHeaderBuf( pReq, newlen ) == -1 ) {
+ return -1;
+ }
+ }
+ memmove( pReq->m_pRespHeaderBufPos, pBuf, len );
+ pReq->m_pRespHeaderBufPos += len;
+ *pReq->m_pRespHeaderBufPos++ = 0;
+ ++len; /* add one byte padding for \0 */
+ pReq->m_respHeaderLen[pReq->m_respHeader.m_respInfo.m_cntHeaders] = len;
+ ++pReq->m_respHeader.m_respInfo.m_cntHeaders;
+ return 0;
+}
+
+
+int LSAPI_CreateListenSock2( const struct sockaddr * pServerAddr, int backlog )
+{
+ int ret;
+ int fd;
+ int flag = 1;
+ int addr_len;
+
+ switch( pServerAddr->sa_family ) {
+ case AF_INET:
+ addr_len = 16;
+ break;
+ case AF_INET6:
+ addr_len = sizeof( struct sockaddr_in6 );
+ break;
+ case AF_UNIX:
+ addr_len = sizeof( struct sockaddr_un );
+ unlink( ((struct sockaddr_un *)pServerAddr)->sun_path );
+ break;
+ default:
+ return -1;
+ }
+
+ fd = socket( pServerAddr->sa_family, SOCK_STREAM, 0 );
+ if ( fd == -1 ) {
+ return -1;
+ }
+
+ fcntl( fd, F_SETFD, FD_CLOEXEC );
+
+ if(setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)( &flag ), sizeof(flag)) == 0) {
+ ret = bind( fd, pServerAddr, addr_len );
+ if ( !ret ) {
+ ret = listen( fd, backlog );
+ if ( !ret ) {
+ return fd;
+ }
+ }
+ }
+
+ ret = errno;
+ close(fd);
+ errno = ret;
+ return -1;
+
+}
+
+int LSAPI_ParseSockAddr( const char * pBind, struct sockaddr * pAddr )
+{
+ char achAddr[256];
+ char * p = achAddr;
+ char * pEnd;
+ struct addrinfo *res, hints;
+ int doAddrInfo = 0;
+ int port;
+
+ if ( !pBind ) {
+ return -1;
+ }
+
+ while( isspace( *pBind ) ) {
+ ++pBind;
+ }
+
+ strncpy( achAddr, pBind, 256 );
+
+ switch( *p ) {
+ case '/':
+ pAddr->sa_family = AF_UNIX;
+ strncpy( ((struct sockaddr_un *)pAddr)->sun_path, p,
+ sizeof(((struct sockaddr_un *)pAddr)->sun_path) );
+ return 0;
+
+ case '[':
+ pAddr->sa_family = AF_INET6;
+ ++p;
+ pEnd = strchr( p, ']' );
+ if ( !pEnd )
+ return -1;
+ *pEnd++ = 0;
+
+ if ( *p == '*' ) {
+ strcpy( achAddr, "::" );
+ p = achAddr;
+ }
+ doAddrInfo = 1;
+ break;
+
+ default:
+ pAddr->sa_family = AF_INET;
+ pEnd = strchr( p, ':' );
+ if ( !pEnd ) {
+ return -1;
+ }
+ *pEnd++ = 0;
+
+ doAddrInfo = 0;
+ if ( *p == '*' ) {
+ ((struct sockaddr_in *)pAddr)->sin_addr.s_addr = htonl(INADDR_ANY);
+ } else {
+ if (!strcasecmp( p, "localhost" ) ) {
+ ((struct sockaddr_in *)pAddr)->sin_addr.s_addr = htonl( INADDR_LOOPBACK );
+ } else {
+ ((struct sockaddr_in *)pAddr)->sin_addr.s_addr = inet_addr( p );
+ if ( ((struct sockaddr_in *)pAddr)->sin_addr.s_addr == INADDR_BROADCAST) {
+ doAddrInfo = 1;
+ }
+ }
+ }
+ break;
+ }
+ if ( *pEnd == ':' ) {
+ ++pEnd;
+ }
+
+ port = atoi( pEnd );
+ if (( port <= 0 )||( port > 65535 )) {
+ return -1;
+ }
+ if ( doAddrInfo ) {
+
+ memset(&hints, 0, sizeof(hints));
+
+ hints.ai_family = pAddr->sa_family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ if ( getaddrinfo(p, NULL, &hints, &res) ) {
+ return -1;
+ }
+
+ memcpy(pAddr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ }
+
+ if ( pAddr->sa_family == AF_INET ) {
+ ((struct sockaddr_in *)pAddr)->sin_port = htons( port );
+ } else {
+ ((struct sockaddr_in6 *)pAddr)->sin6_port = htons( port );
+ }
+ return 0;
+
+}
+
+int LSAPI_CreateListenSock( const char * pBind, int backlog )
+{
+ char serverAddr[128];
+ int ret;
+ int fd = -1;
+ ret = LSAPI_ParseSockAddr( pBind, (struct sockaddr *)serverAddr );
+ if ( !ret ) {
+ fd = LSAPI_CreateListenSock2( (struct sockaddr *)serverAddr, backlog );
+ }
+ return fd;
+}
+
+static fn_select_t g_fnSelect = select;
+
+typedef struct _lsapi_child_status
+{
+ int m_pid;
+
+ volatile short m_iKillSent;
+ volatile short m_inProcess;
+
+ volatile long m_tmWaitBegin;
+ volatile long m_tmReqBegin;
+ volatile long m_tmLastCheckPoint;
+}
+lsapi_child_status;
+
+static lsapi_child_status * s_pChildStatus = NULL;
+
+typedef struct _lsapi_prefork_server
+{
+ int m_fd;
+ int m_iMaxChildren;
+ int m_iExtraChildren;
+ int m_iCurChildren;
+ int m_iMaxIdleChildren;
+ int m_iServerMaxIdle;
+ int m_iChildrenMaxIdleTime;
+ int m_iMaxReqProcessTime;
+ int m_iAvoidFork;
+
+ lsapi_child_status * m_pChildrenStatus;
+
+}lsapi_prefork_server;
+
+static lsapi_prefork_server * g_prefork_server = NULL;
+
+int LSAPI_Init_Prefork_Server( int max_children, fn_select_t fp, int avoidFork )
+{
+ if ( g_prefork_server ) {
+ return 0;
+ }
+ if ( max_children <= 1 ) {
+ return -1;
+ }
+ if ( max_children >= 10000) {
+ max_children = 10000;
+ }
+
+
+ g_prefork_server = (lsapi_prefork_server *)malloc( sizeof( lsapi_prefork_server ) );
+ if ( !g_prefork_server ) {
+ return -1;
+ }
+ memset( g_prefork_server, 0, sizeof( lsapi_prefork_server ) );
+
+ if ( fp != NULL ) {
+ g_fnSelect = fp;
+ }
+
+ s_ppid = getppid();
+ g_prefork_server->m_iAvoidFork = avoidFork;
+ g_prefork_server->m_iMaxChildren = max_children;
+
+ g_prefork_server->m_iExtraChildren = ( avoidFork ) ? 0 : (max_children / 3) ;
+ g_prefork_server->m_iMaxIdleChildren = ( avoidFork ) ? (max_children + 1) : (max_children / 3);
+ g_prefork_server->m_iChildrenMaxIdleTime = 300;
+ g_prefork_server->m_iMaxReqProcessTime = 300;
+ return 0;
+}
+
+void LSAPI_Set_Server_fd( int fd )
+{
+ if( g_prefork_server ) {
+ g_prefork_server->m_fd = fd;
+ }
+}
+
+
+static int lsapi_accept( int fdListen )
+{
+ int fd;
+ int nodelay = 1;
+ socklen_t len;
+ char achPeer[128];
+
+ len = sizeof( achPeer );
+ fd = accept( fdListen, (struct sockaddr *)&achPeer, &len );
+ if ( fd != -1 ) {
+ if (((struct sockaddr *)&achPeer)->sa_family == AF_INET ) {
+ setsockopt( fd, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&nodelay, sizeof(nodelay));
+ }
+
+ if ( s_accept_notify )
+ notify_req_received( fd );
+ }
+ return fd;
+
+}
+
+
+
+
+static int s_req_processed = 0;
+static int s_max_reqs = 10000;
+static int s_max_idle_secs = 300;
+
+static int s_stop;
+
+static void lsapi_cleanup(int signal)
+{
+ s_stop = signal;
+}
+
+static lsapi_child_status * find_child_status( int pid )
+{
+ lsapi_child_status * pStatus = g_prefork_server->m_pChildrenStatus;
+ lsapi_child_status * pEnd = g_prefork_server->m_pChildrenStatus + g_prefork_server->m_iMaxChildren * 2;
+ while( pStatus < pEnd ) {
+ if ( pStatus->m_pid == pid ) {
+ return pStatus;
+ }
+ ++pStatus;
+ }
+ return NULL;
+}
+
+
+
+static void lsapi_sigchild( int signal )
+{
+ int status, pid;
+ lsapi_child_status * child_status;
+ while( 1 ) {
+ pid = waitpid( -1, &status, WNOHANG|WUNTRACED );
+ if ( pid <= 0 ) {
+ break;
+ }
+ child_status = find_child_status( pid );
+ if ( child_status ) {
+ child_status->m_pid = 0;
+ }
+ --g_prefork_server->m_iCurChildren;
+ }
+
+}
+
+static int lsapi_init_children_status()
+{
+ int size = 4096;
+
+ char * pBuf;
+ size = g_prefork_server->m_iMaxChildren * sizeof( lsapi_child_status ) * 2;
+ size = (size + 4095 ) / 4096 * 4096;
+ pBuf =( char*) mmap( NULL, size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_SHARED, -1, 0 );
+ if ( pBuf == MAP_FAILED ) {
+ perror( "Anonymous mmap() failed" );
+ return -1;
+ }
+ memset( pBuf, 0, size );
+ g_prefork_server->m_pChildrenStatus = (lsapi_child_status *)pBuf;
+ return 0;
+}
+
+static void lsapi_check_child_status( long tmCur )
+{
+ int idle = 0;
+ int tobekilled;
+ int dying = 0;
+ lsapi_child_status * pStatus = g_prefork_server->m_pChildrenStatus;
+ lsapi_child_status * pEnd = g_prefork_server->m_pChildrenStatus + g_prefork_server->m_iMaxChildren * 2;
+ while( pStatus < pEnd ) {
+ tobekilled = pStatus->m_iKillSent;
+ if ( pStatus->m_pid != 0 ) {
+ if ( !tobekilled ) {
+ if ( !pStatus->m_inProcess ) {
+
+ if (( g_prefork_server->m_iCurChildren - dying > g_prefork_server->m_iMaxChildren)||
+ ( idle >= g_prefork_server->m_iMaxIdleChildren )) {
+
+ tobekilled = 1;
+ } else {
+ if (( s_max_idle_secs> 0)&&(tmCur - pStatus->m_tmWaitBegin > s_max_idle_secs + 5 )) {
+ tobekilled = 1;
+ }
+ }
+ if ( !tobekilled ) {
+ ++idle;
+ }
+ } else {
+ if ( tmCur - pStatus->m_tmReqBegin >
+ g_prefork_server->m_iMaxReqProcessTime ) {
+ tobekilled = 1;
+ }
+ }
+ } else {
+ if ( pStatus->m_inProcess ) {
+ tobekilled = pStatus->m_iKillSent = 0;
+ }
+ }
+ if ( tobekilled ) {
+ tobekilled = 0;
+ if ( pStatus->m_iKillSent > 5 ) {
+ tobekilled = SIGKILL;
+ } else {
+ if ( pStatus->m_iKillSent == 3 ) {
+ tobekilled = SIGTERM;
+ } else {
+ if ( pStatus->m_iKillSent == 1 ) {
+ tobekilled = SIGUSR1;
+ }
+ }
+ }
+ if ( tobekilled ) {
+ kill( pStatus->m_pid, tobekilled );
+ }
+ ++pStatus->m_iKillSent;
+ ++dying;
+ }
+
+ } else {
+ ++dying;
+ }
+ ++pStatus;
+ }
+}
+
+static int lsapi_all_children_must_die()
+{
+ int maxWait;
+ int sec =0;
+ g_prefork_server->m_iMaxReqProcessTime = 10;
+ g_prefork_server->m_iMaxIdleChildren = -1;
+ maxWait = 15;
+
+ while( g_prefork_server->m_iCurChildren && (sec < maxWait) ) {
+ lsapi_check_child_status(time(NULL));
+ sleep( 1 );
+ sec++;
+ }
+ if ( g_prefork_server->m_iCurChildren != 0 ) {
+ kill( -getpgrp(), SIGKILL );
+ }
+ return 0;
+}
+
+
+
+static int lsapi_prefork_server_accept( lsapi_prefork_server * pServer, LSAPI_Request * pReq )
+{
+ struct sigaction act, old_term, old_quit, old_int,
+ old_usr1, old_child;
+ lsapi_child_status * child_status;
+ int wait_secs = 0;
+ int ret = 0;
+ int pid;
+ time_t lastTime = 0;
+ time_t curTime = 0;
+ fd_set readfds;
+ struct timeval timeout;
+
+ lsapi_init_children_status();
+
+ setsid();
+
+ act.sa_flags = 0;
+ act.sa_handler = lsapi_sigchild;
+ if( sigaction( SIGCHLD, &act, &old_child ) ) {
+ perror( "Can't set signal handler for SIGCHILD" );
+ return -1;
+ }
+
+ /* Set up handler to kill children upon exit */
+ act.sa_flags = 0;
+ act.sa_handler = lsapi_cleanup;
+ if( sigaction( SIGTERM, &act, &old_term ) ||
+ sigaction( SIGINT, &act, &old_int ) ||
+ sigaction( SIGUSR1, &act, &old_usr1 ) ||
+ sigaction( SIGQUIT, &act, &old_quit )) {
+ perror( "Can't set signals" );
+ return -1;
+ }
+ s_stop = 0;
+ while( !s_stop ) {
+ if ( ret ) {
+ curTime = time( NULL );
+ } else {
+ ++curTime;
+ }
+ if (curTime != lastTime ) {
+ lastTime = curTime;
+ if (s_ppid && (getppid() != s_ppid )) {
+ break;
+ }
+ lsapi_check_child_status(curTime );
+ if (pServer->m_iServerMaxIdle) {
+ if ( pServer->m_iCurChildren <= 0 ) {
+ ++wait_secs;
+ if ( wait_secs > pServer->m_iServerMaxIdle ) {
+ return -1;
+ }
+ } else {
+ wait_secs = 0;
+ }
+ }
+ }
+
+ if ( pServer->m_iCurChildren >= (pServer->m_iMaxChildren + pServer->m_iExtraChildren ) ) {
+ usleep( 100000 );
+ continue;
+ }
+
+ FD_ZERO( &readfds );
+ FD_SET( pServer->m_fd, &readfds );
+ timeout.tv_sec = 1; timeout.tv_usec = 0;
+ if ((ret = (*g_fnSelect)(pServer->m_fd+1, &readfds, NULL, NULL, &timeout)) == 1 ) {
+ if ( pServer->m_iCurChildren >= 0 ) {
+ usleep( 10 );
+ FD_ZERO( &readfds );
+ FD_SET( pServer->m_fd, &readfds );
+ timeout.tv_sec = 0; timeout.tv_usec = 0;
+ if ( (*g_fnSelect)(pServer->m_fd+1, &readfds, NULL, NULL, &timeout) == 0 ) {
+ continue;
+ }
+ }
+ } else {
+ if ( ret == -1 ) {
+ if ( errno == EINTR ) {
+ continue;
+ }
+ /* perror( "select()" ); */
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ pReq->m_fd = lsapi_accept( pServer->m_fd );
+ if ( pReq->m_fd != -1 ) {
+ child_status = find_child_status( 0 );
+ pid = fork();
+ if ( !pid ) {
+ g_prefork_server = NULL;
+ s_ppid = getppid();
+ s_req_processed = 0;
+ s_pChildStatus = child_status;
+ child_status->m_iKillSent = 0;
+ lsapi_set_nblock( pReq->m_fd, 0 );
+
+ /* don't catch our signals */
+ sigaction( SIGCHLD, &old_child, 0 );
+ sigaction( SIGTERM, &old_term, 0 );
+ sigaction( SIGQUIT, &old_quit, 0 );
+ sigaction( SIGINT, &old_int, 0 );
+ sigaction( SIGUSR1, &old_usr1, 0 );
+ return 0;
+ } else {
+ if ( pid == -1 ) {
+ perror( "fork() failed, please increase process limit" );
+ } else {
+ ++pServer->m_iCurChildren;
+ if ( child_status ) {
+ child_status->m_pid = pid;
+ child_status->m_iKillSent = 0;
+ child_status->m_tmWaitBegin = time(NULL);
+ }
+ }
+ }
+ close( pReq->m_fd );
+ pReq->m_fd = -1;
+
+ } else {
+ if (( errno == EINTR )||( errno == EAGAIN)) {
+ continue;
+ }
+ perror( "accept() failed" );
+ return -1;
+ }
+ }
+ sigaction( SIGUSR1, &old_usr1, 0 );
+ kill( -getpgrp(), SIGUSR1 );
+ lsapi_all_children_must_die(); /* Sorry, children ;-) */
+ return -1;
+
+}
+
+int LSAPI_Prefork_Accept_r( LSAPI_Request * pReq )
+{
+ int fd;
+ int ret;
+ int wait_secs;
+ fd_set readfds;
+ struct timeval timeout;
+
+ LSAPI_Finish_r( pReq );
+
+
+ if ( g_prefork_server ) {
+ if ( g_prefork_server->m_fd != -1 ) {
+ if ( lsapi_prefork_server_accept( g_prefork_server, pReq ) == -1 ) {
+ return -1;
+ }
+ }
+ }
+ if ( s_req_processed >= s_max_reqs ) {
+ return -1;
+ }
+
+ if ( s_pChildStatus ) {
+ s_pChildStatus->m_tmWaitBegin = time( NULL );
+ }
+
+ while( g_running ) {
+ if ( pReq->m_fd != -1 ) {
+ fd = pReq->m_fd;
+ } else {
+ if ( pReq->m_fdListen != -1 ) {
+ fd = pReq->m_fdListen;
+ } else {
+ return -1;
+ }
+ }
+ wait_secs = 0;
+ while( 1 ) {
+ if ( !g_running ) {
+ return -1;
+ }
+ if (( s_pChildStatus )&&( s_pChildStatus->m_iKillSent )) {
+ return -1;
+ }
+ FD_ZERO( &readfds );
+ FD_SET( fd, &readfds );
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ ret = (*g_fnSelect)(fd+1, &readfds, NULL, NULL, &timeout);
+ if ( ret == 0 ) {
+ if ( s_pChildStatus ) {
+ s_pChildStatus->m_inProcess = 0;
+ }
+ ++wait_secs;
+ if (( s_max_idle_secs > 0 )&&(wait_secs >= s_max_idle_secs )) {
+ return -1;
+ }
+ if ( s_ppid &&( getppid() != s_ppid)) {
+ return -1;
+ }
+ } else {
+ if ( ret == -1 ) {
+ if ( errno == EINTR ) {
+ continue;
+ } else {
+ return -1;
+ }
+ } else {
+ if ( ret >= 1 ) {
+ if (( s_pChildStatus )&&( s_pChildStatus->m_iKillSent )) {
+ return -1;
+ }
+ if ( fd == pReq->m_fdListen ) {
+ pReq->m_fd = lsapi_accept( pReq->m_fdListen );
+ if ( pReq->m_fd != -1 ) {
+ fd = pReq->m_fd;
+ lsapi_set_nblock( fd, 0 );
+ } else {
+ if (( errno == EINTR )||( errno == EAGAIN)) {
+ continue;
+ }
+ return -1;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if ( !readReq( pReq ) ) {
+ if ( s_pChildStatus ) {
+ s_pChildStatus->m_inProcess = 1;
+ s_pChildStatus->m_tmReqBegin = s_pChildStatus->m_tmLastCheckPoint = time(NULL);
+ }
+ ++s_req_processed;
+ return 0;
+ }
+ lsapi_close( pReq->m_fd );
+ pReq->m_fd = -1;
+ LSAPI_Reset_r( pReq );
+ }
+ return -1;
+
+}
+
+void LSAPI_Set_Max_Reqs( int reqs )
+{
+ s_max_reqs = reqs;
+}
+
+void LSAPI_Set_Max_Idle( int secs )
+{
+ s_max_idle_secs = secs;
+}
+
+void LSAPI_Set_Max_Children( int maxChildren )
+{
+ if ( g_prefork_server ) {
+ g_prefork_server->m_iMaxChildren = maxChildren;
+ }
+}
+
+void LSAPI_Set_Extra_Children( int extraChildren )
+{
+ if (( g_prefork_server )&&( extraChildren >= 0 )) {
+ g_prefork_server->m_iExtraChildren = extraChildren;
+ }
+}
+
+void LSAPI_Set_Max_Process_Time( int secs )
+{
+ if (( g_prefork_server )&&( secs > 0 )) {
+ g_prefork_server->m_iMaxReqProcessTime = secs;
+ }
+}
+
+
+void LSAPI_Set_Max_Idle_Children( int maxIdleChld )
+{
+ if (( g_prefork_server )&&( maxIdleChld > 0 )) {
+ g_prefork_server->m_iMaxIdleChildren = maxIdleChld;
+ }
+}
+
+void LSAPI_Set_Server_Max_Idle_Secs( int serverMaxIdle )
+{
+ if ( g_prefork_server ) {
+ g_prefork_server->m_iServerMaxIdle = serverMaxIdle;
+ }
+}
+
+void LSAPI_Set_Slow_Req_Msecs( int msecs )
+{
+ s_slow_req_msecs = msecs;
+}
+
+int LSAPI_Get_Slow_Req_Msecs()
+{
+ return s_slow_req_msecs;
+}
+
+void LSAPI_No_Check_ppid()
+{
+ s_ppid = 0;
+}
+
+#if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
+#include <crt_externs.h>
+#else
+extern char ** environ;
+#endif
+static void unset_lsapi_envs()
+{
+ char **env;
+#if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
+ env = *_NSGetEnviron();
+#else
+ env = environ;
+#endif
+ while( env != NULL && *env != NULL ) {
+ if ( !strncmp(*env, "LSAPI_", 6) ||
+ !strncmp( *env, "PHP_LSAPI_", 10 ) ) {
+ char ** del = env;
+ do {
+ *del = del[1];
+ } while( *del++ );
+ } else {
+ ++env;
+ }
+ }
+}
+
+void LSAPI_Init_Env_Parameters( fn_select_t fp )
+{
+ const char *p;
+ int n;
+ int avoidFork = 0;
+ p = getenv( "PHP_LSAPI_MAX_REQUESTS" );
+ if ( !p ) {
+ p = getenv( "LSAPI_MAX_REQS" );
+ }
+ if ( p ) {
+ n = atoi( p );
+ if ( n > 0 ) {
+ LSAPI_Set_Max_Reqs( n );
+ }
+ }
+
+ p = getenv( "LSAPI_AVOID_FORK" );
+ if ( p ) {
+ avoidFork = atoi( p );
+ }
+
+ p = getenv( "LSAPI_ACCEPT_NOTIFY" );
+ if ( p ) {
+ s_accept_notify = atoi( p );
+ }
+
+ p = getenv( "LSAPI_SLOW_REQ_MSECS" );
+ if ( p ) {
+ n = atoi( p );
+ LSAPI_Set_Slow_Req_Msecs( n );
+ }
+
+
+#if defined( RLIMIT_CORE )
+ p = getenv( "LSAPI_ALLOW_CORE_DUMP" );
+ if ( !p ) {
+ struct rlimit limit = { 0, 0 };
+ setrlimit( RLIMIT_CORE, &limit );
+ }
+#endif
+
+ p = getenv( "LSAPI_MAX_IDLE" );
+ if ( p ) {
+ n = atoi( p );
+ LSAPI_Set_Max_Idle( n );
+ }
+
+ if ( LSAPI_Is_Listen() ) {
+ n = 0;
+ p = getenv( "PHP_LSAPI_CHILDREN" );
+ if ( !p ) {
+ p = getenv( "LSAPI_CHILDREN" );
+ }
+ if ( p ) {
+ n = atoi( p );
+ }
+ if ( n > 1 ) {
+ LSAPI_Init_Prefork_Server( n, fp, avoidFork );
+ LSAPI_Set_Server_fd( g_req.m_fdListen );
+ }
+
+ p = getenv( "LSAPI_EXTRA_CHILDREN" );
+ if ( p ) {
+ LSAPI_Set_Extra_Children( atoi( p ) );
+ }
+
+ p = getenv( "LSAPI_MAX_IDLE_CHILDREN" );
+ if ( p ) {
+ LSAPI_Set_Max_Idle_Children( atoi( p ) );
+ }
+ p = getenv( "LSAPI_PGRP_MAX_IDLE" );
+ if ( p ) {
+ LSAPI_Set_Server_Max_Idle_Secs( atoi( p ) );
+ }
+
+ p = getenv( "LSAPI_MAX_PROCESS_TIME" );
+ if ( p ) {
+ LSAPI_Set_Max_Process_Time( atoi( p ) );
+ }
+ if ( getenv( "LSAPI_PPID_NO_CHECK" ) ) {
+ LSAPI_No_Check_ppid();
+ }
+ }
+ unset_lsapi_envs();
+}
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
+
+
diff --git a/sapi/litespeed/lsapilib.h b/sapi/litespeed/lsapilib.h
new file mode 100644
index 0000000..3a987b4
--- /dev/null
+++ b/sapi/litespeed/lsapilib.h
@@ -0,0 +1,363 @@
+
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available at through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt. |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: George Wang <gwang@litespeedtech.com> |
+ +----------------------------------------------------------------------+
+*/
+
+/*
+Copyright (c) 2007, Lite Speed Technologies Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of the Lite Speed Technologies Inc nor the
+ names of its contributors may be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+
+#ifndef _LSAPILIB_H_
+#define _LSAPILIB_H_
+
+#if defined (c_plusplus) || defined (__cplusplus)
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <lsapidef.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+
+struct LSAPI_key_value_pair
+{
+ char * pKey;
+ char * pValue;
+ int keyLen;
+ int valLen;
+};
+
+
+#define LSAPI_MAX_RESP_HEADERS 100
+
+typedef struct lsapi_request
+{
+ int m_fdListen;
+ int m_fd;
+
+ long m_lLastActive;
+ long m_lReqBegin;
+
+ char * m_pReqBuf;
+ int m_reqBufSize;
+
+ char * m_pRespBuf;
+ char * m_pRespBufEnd;
+ char * m_pRespBufPos;
+
+ char * m_pRespHeaderBuf;
+ char * m_pRespHeaderBufEnd;
+ char * m_pRespHeaderBufPos;
+
+
+ struct iovec * m_pIovec;
+ struct iovec * m_pIovecEnd;
+ struct iovec * m_pIovecCur;
+ struct iovec * m_pIovecToWrite;
+
+ struct lsapi_packet_header * m_respPktHeaderEnd;
+
+ struct lsapi_req_header * m_pHeader;
+ struct LSAPI_key_value_pair * m_pEnvList;
+ struct LSAPI_key_value_pair * m_pSpecialEnvList;
+ int m_envListSize;
+ int m_specialEnvListSize;
+
+ struct lsapi_http_header_index * m_pHeaderIndex;
+ struct lsapi_header_offset * m_pUnknownHeader;
+
+ char * m_pScriptFile;
+ char * m_pScriptName;
+ char * m_pQueryString;
+ char * m_pHttpHeader;
+ char * m_pRequestMethod;
+ int m_totalLen;
+ int m_reqState;
+ int m_reqBodyRead;
+ int m_bufProcessed;
+ int m_bufRead;
+
+ struct lsapi_packet_header m_respPktHeader[5];
+
+ struct lsapi_resp_header m_respHeader;
+ short m_respHeaderLen[LSAPI_MAX_RESP_HEADERS];
+
+}LSAPI_Request;
+
+extern LSAPI_Request g_req;
+
+
+/* return: >0 continue, ==0 stop, -1 failed */
+typedef int (*LSAPI_CB_EnvHandler )( const char * pKey, int keyLen,
+ const char * pValue, int valLen, void * arg );
+
+
+int LSAPI_Init(void);
+
+void LSAPI_Stop(void);
+
+int LSAPI_Is_Listen_r( LSAPI_Request * pReq);
+
+int LSAPI_InitRequest( LSAPI_Request * pReq, int fd );
+
+int LSAPI_Accept_r( LSAPI_Request * pReq );
+
+void LSAPI_Reset_r( LSAPI_Request * pReq );
+
+int LSAPI_Finish_r( LSAPI_Request * pReq );
+
+int LSAPI_Release_r( LSAPI_Request * pReq );
+
+char * LSAPI_GetHeader_r( LSAPI_Request * pReq, int headerIndex );
+
+int LSAPI_ForeachHeader_r( LSAPI_Request * pReq,
+ LSAPI_CB_EnvHandler fn, void * arg );
+
+int LSAPI_ForeachOrgHeader_r( LSAPI_Request * pReq,
+ LSAPI_CB_EnvHandler fn, void * arg );
+
+int LSAPI_ForeachEnv_r( LSAPI_Request * pReq,
+ LSAPI_CB_EnvHandler fn, void * arg );
+
+int LSAPI_ForeachSpecialEnv_r( LSAPI_Request * pReq,
+ LSAPI_CB_EnvHandler fn, void * arg );
+
+char * LSAPI_GetEnv_r( LSAPI_Request * pReq, const char * name );
+
+
+int LSAPI_ReadReqBody_r( LSAPI_Request * pReq, char * pBuf, int len );
+
+int LSAPI_ReqBodyGetChar_r( LSAPI_Request * pReq );
+
+int LSAPI_ReqBodyGetLine_r( LSAPI_Request * pReq, char * pBuf, int bufLen, int *getLF );
+
+
+int LSAPI_FinalizeRespHeaders_r( LSAPI_Request * pReq );
+
+int LSAPI_Write_r( LSAPI_Request * pReq, const char * pBuf, int len );
+
+int LSAPI_Write_Stderr_r( LSAPI_Request * pReq, const char * pBuf, int len );
+
+int LSAPI_Flush_r( LSAPI_Request * pReq );
+
+int LSAPI_AppendRespHeader_r( LSAPI_Request * pHeader, char * pBuf, int len );
+
+static inline int LSAPI_SetRespStatus_r( LSAPI_Request * pReq, int code )
+{
+ if ( !pReq )
+ return -1;
+ pReq->m_respHeader.m_respInfo.m_status = code;
+ return 0;
+}
+
+static inline char * LSAPI_GetQueryString_r( LSAPI_Request * pReq )
+{
+ if ( pReq )
+ return pReq->m_pQueryString;
+ return NULL;
+}
+
+
+static inline char * LSAPI_GetScriptFileName_r( LSAPI_Request * pReq )
+{
+ if ( pReq )
+ return pReq->m_pScriptFile;
+ return NULL;
+}
+
+
+static inline char * LSAPI_GetScriptName_r( LSAPI_Request * pReq )
+{
+ if ( pReq )
+ return pReq->m_pScriptName;
+ return NULL;
+}
+
+
+static inline char * LSAPI_GetRequestMethod_r( LSAPI_Request * pReq)
+{
+ if ( pReq )
+ return pReq->m_pRequestMethod;
+ return NULL;
+}
+
+
+
+static inline int LSAPI_GetReqBodyLen_r( LSAPI_Request * pReq )
+{
+ if ( pReq )
+ return pReq->m_pHeader->m_reqBodyLen;
+ return -1;
+}
+
+static inline int LSAPI_GetReqBodyRemain_r( LSAPI_Request * pReq )
+{
+ if ( pReq )
+ return pReq->m_pHeader->m_reqBodyLen - pReq->m_reqBodyRead;
+ return -1;
+}
+
+
+int LSAPI_Is_Listen(void);
+
+static inline int LSAPI_Accept( void )
+{ return LSAPI_Accept_r( &g_req ); }
+
+static inline int LSAPI_Finish(void)
+{ return LSAPI_Finish_r( &g_req ); }
+
+static inline char * LSAPI_GetHeader( int headerIndex )
+{ return LSAPI_GetHeader_r( &g_req, headerIndex ); }
+
+static inline int LSAPI_ForeachHeader( LSAPI_CB_EnvHandler fn, void * arg )
+{ return LSAPI_ForeachHeader_r( &g_req, fn, arg ); }
+
+static inline int LSAPI_ForeachOrgHeader(
+ LSAPI_CB_EnvHandler fn, void * arg )
+{ return LSAPI_ForeachOrgHeader_r( &g_req, fn, arg ); }
+
+static inline int LSAPI_ForeachEnv( LSAPI_CB_EnvHandler fn, void * arg )
+{ return LSAPI_ForeachEnv_r( &g_req, fn, arg ); }
+
+static inline int LSAPI_ForeachSpecialEnv( LSAPI_CB_EnvHandler fn, void * arg )
+{ return LSAPI_ForeachSpecialEnv_r( &g_req, fn, arg ); }
+
+static inline char * LSAPI_GetEnv( const char * name )
+{ return LSAPI_GetEnv_r( &g_req, name ); }
+
+static inline char * LSAPI_GetQueryString()
+{ return LSAPI_GetQueryString_r( &g_req ); }
+
+static inline char * LSAPI_GetScriptFileName()
+{ return LSAPI_GetScriptFileName_r( &g_req ); }
+
+static inline char * LSAPI_GetScriptName()
+{ return LSAPI_GetScriptName_r( &g_req ); }
+
+static inline char * LSAPI_GetRequestMethod()
+{ return LSAPI_GetRequestMethod_r( &g_req ); }
+
+static inline int LSAPI_GetReqBodyLen()
+{ return LSAPI_GetReqBodyLen_r( &g_req ); }
+
+static inline int LSAPI_GetReqBodyRemain()
+{ return LSAPI_GetReqBodyRemain_r( &g_req ); }
+
+static inline int LSAPI_ReadReqBody( char * pBuf, int len )
+{ return LSAPI_ReadReqBody_r( &g_req, pBuf, len ); }
+
+static inline int LSAPI_ReqBodyGetChar()
+{ return LSAPI_ReqBodyGetChar_r( &g_req ); }
+
+static inline int LSAPI_ReqBodyGetLine( char * pBuf, int len, int *getLF )
+{ return LSAPI_ReqBodyGetLine_r( &g_req, pBuf, len, getLF ); }
+
+
+
+static inline int LSAPI_FinalizeRespHeaders(void)
+{ return LSAPI_FinalizeRespHeaders_r( &g_req ); }
+
+static inline int LSAPI_Write( const char * pBuf, int len )
+{ return LSAPI_Write_r( &g_req, pBuf, len ); }
+
+static inline int LSAPI_Write_Stderr( const char * pBuf, int len )
+{ return LSAPI_Write_Stderr_r( &g_req, pBuf, len ); }
+
+static inline int LSAPI_Flush()
+{ return LSAPI_Flush_r( &g_req ); }
+
+static inline int LSAPI_AppendRespHeader( char * pBuf, int len )
+{ return LSAPI_AppendRespHeader_r( &g_req, pBuf, len ); }
+
+static inline int LSAPI_SetRespStatus( int code )
+{ return LSAPI_SetRespStatus_r( &g_req, code ); }
+
+int LSAPI_IsRunning(void);
+
+int LSAPI_CreateListenSock( const char * pBind, int backlog );
+
+typedef int (*fn_select_t)( int, fd_set *, fd_set *, fd_set *, struct timeval * );
+
+int LSAPI_Init_Prefork_Server( int max_children, fn_select_t fp, int avoidFork );
+
+void LSAPI_Set_Server_fd( int fd );
+
+int LSAPI_Prefork_Accept_r( LSAPI_Request * pReq );
+
+void LSAPI_Set_Max_Reqs( int reqs );
+
+void LSAPI_Set_Max_Idle( int secs );
+
+void LSAPI_Set_Max_Children( int maxChildren );
+
+void LSAPI_Set_Max_Idle_Children( int maxIdleChld );
+
+void LSAPI_Set_Server_Max_Idle_Secs( int serverMaxIdle );
+
+void LSAPI_Set_Max_Process_Time( int secs );
+
+void LSAPI_Init_Env_Parameters( fn_select_t fp );
+
+void LSAPI_Set_Slow_Req_Msecs( int msecs );
+
+int LSAPI_Get_Slow_Req_Msecs( );
+
+
+#if defined (c_plusplus) || defined (__cplusplus)
+}
+#endif
+
+
+#endif
+
+
+
+
+
+
+
diff --git a/sapi/milter/CREDITS b/sapi/milter/CREDITS
new file mode 100644
index 0000000..cd00d67
--- /dev/null
+++ b/sapi/milter/CREDITS
@@ -0,0 +1,2 @@
+Sendmail Milter
+Harald Radi
diff --git a/sapi/milter/EXPERIMENTAL b/sapi/milter/EXPERIMENTAL
new file mode 100644
index 0000000..293159a
--- /dev/null
+++ b/sapi/milter/EXPERIMENTAL
@@ -0,0 +1,5 @@
+this module is experimental,
+its functions may change their names
+or move to extension all together
+so do not rely to much on them
+you have been warned!
diff --git a/sapi/milter/Makefile.frag b/sapi/milter/Makefile.frag
new file mode 100644
index 0000000..26200a1
--- /dev/null
+++ b/sapi/milter/Makefile.frag
@@ -0,0 +1,8 @@
+milter: $(SAPI_MILTER_PATH)
+
+$(SAPI_MILTER_PATH): $(PHP_GLOBAL_OBJS) $(PHP_BINARY_OBJS) $(PHP_SAPI_OBJS)
+ $(BUILD_MILTER)
+
+install-milter: $(SAPI_MILTER_PATH)
+ @$(INSTALL) -m 0755 $(SAPI_MILTER_PATH) $(bindir)/php-milter
+
diff --git a/sapi/milter/TODO b/sapi/milter/TODO
new file mode 100644
index 0000000..4a427ea
--- /dev/null
+++ b/sapi/milter/TODO
@@ -0,0 +1,5 @@
+threaded version still leaks mem, don't know why
+extensions aren't loaded
+stdout to syslog
+testing
+documentation \ No newline at end of file
diff --git a/sapi/milter/config.m4 b/sapi/milter/config.m4
new file mode 100644
index 0000000..48c7a5d
--- /dev/null
+++ b/sapi/milter/config.m4
@@ -0,0 +1,31 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(milter, for Milter support,
+[ --with-milter[=DIR] Build PHP as Milter application], no, no)
+
+if test "$PHP_MILTER" != "no"; then
+ if test "$PHP_MILTER" = "yes"; then
+ if test -f /usr/lib/libmilter.a ; then
+ MILTERPATH=/usr/lib
+ else
+ if test -f /usr/lib/libmilter/libmilter.a ; then
+ MILTERPATH=/usr/lib/libmilter
+ else
+ AC_MSG_ERROR([Unable to find libmilter.a])
+ fi
+ fi
+ else
+ MILTERPATH=$PHP_MILTER
+ fi
+
+ SAPI_MILTER_PATH=sapi/milter/php-milter
+ PHP_BUILD_THREAD_SAFE
+ PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/sapi/milter/Makefile.frag)
+ PHP_SELECT_SAPI(milter, program, php_milter.c getopt.c,,'$(SAPI_MILTER_PATH)')
+ PHP_ADD_LIBRARY_WITH_PATH(milter, $MILTERPATH,)
+ BUILD_MILTER="\$(LIBTOOL) --mode=link \$(CC) -export-dynamic \$(CFLAGS_CLEAN) \$(EXTRA_CFLAGS) \$(EXTRA_LDFLAGS) \$(LDFLAGS) \$(PHP_RPATHS) \$(PHP_GLOBAL_OBJS) \$(PHP_BINARY_OBJS) \$(PHP_MILTER_OBJS) \$(EXTRA_LIBS) \$(ZEND_EXTRA_LIBS) -o \$(SAPI_MILTER_PATH)"
+ PHP_SUBST(SAPI_MILTER_PATH)
+ PHP_SUBST(BUILD_MILTER)
+fi
diff --git a/sapi/milter/getopt.c b/sapi/milter/getopt.c
new file mode 100644
index 0000000..f5874d5
--- /dev/null
+++ b/sapi/milter/getopt.c
@@ -0,0 +1,173 @@
+/* Borrowed from Apache NT Port */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "php_getopt.h"
+#define OPTERRCOLON (1)
+#define OPTERRNF (2)
+#define OPTERRARG (3)
+
+
+char *ap_php_optarg;
+int ap_php_optind = 1;
+static int ap_php_opterr = 1;
+
+static int
+ap_php_optiserr(int argc, char * const *argv, int oint, const char *optstr,
+ int optchr, int err)
+{
+ if (ap_php_opterr)
+ {
+ fprintf(stderr, "Error in argument %d, char %d: ", oint, optchr+1);
+ switch(err)
+ {
+ case OPTERRCOLON:
+ fprintf(stderr, ": in flags\n");
+ break;
+ case OPTERRNF:
+ fprintf(stderr, "option not found %c\n", argv[oint][optchr]);
+ break;
+ case OPTERRARG:
+ fprintf(stderr, "no argument for option %c\n", argv[oint][optchr]);
+ break;
+ default:
+ fprintf(stderr, "unknown\n");
+ break;
+ }
+ }
+ return('?');
+}
+
+int ap_php_getopt(int argc, char* const *argv, const char *optstr)
+{
+ static int optchr = 0;
+ static int dash = 0; /* have already seen the - */
+
+ char *cp;
+
+ if (ap_php_optind >= argc)
+ return(EOF);
+ if (!dash && (argv[ap_php_optind][0] != '-'))
+ return(EOF);
+ if (!dash && (argv[ap_php_optind][0] == '-') && !argv[ap_php_optind][1])
+ {
+ /*
+ * use to specify stdin. Need to let pgm process this and
+ * the following args
+ */
+ return(EOF);
+ }
+ if ((argv[ap_php_optind][0] == '-') && (argv[ap_php_optind][1] == '-'))
+ {
+ /* -- indicates end of args */
+ ap_php_optind++;
+ return(EOF);
+ }
+ if (!dash)
+ {
+ assert((argv[ap_php_optind][0] == '-') && argv[ap_php_optind][1]);
+ dash = 1;
+ optchr = 1;
+ }
+
+ /* Check if the guy tries to do a -: kind of flag */
+ assert(dash);
+ if (argv[ap_php_optind][optchr] == ':')
+ {
+ dash = 0;
+ ap_php_optind++;
+ return(ap_php_optiserr(argc, argv, ap_php_optind-1, optstr, optchr, OPTERRCOLON));
+ }
+ if (!(cp = strchr(optstr, argv[ap_php_optind][optchr])))
+ {
+ int errind = ap_php_optind;
+ int errchr = optchr;
+
+ if (!argv[ap_php_optind][optchr+1])
+ {
+ dash = 0;
+ ap_php_optind++;
+ }
+ else
+ optchr++;
+ return(ap_php_optiserr(argc, argv, errind, optstr, errchr, OPTERRNF));
+ }
+ if (cp[1] == ':')
+ {
+ /* Check for cases where the value of the argument
+ is in the form -<arg> <val> or in the form -<arg><val> */
+ dash = 0;
+ if(!argv[ap_php_optind][2]) {
+ ap_php_optind++;
+ if (ap_php_optind == argc)
+ return(ap_php_optiserr(argc, argv, ap_php_optind-1, optstr, optchr, OPTERRARG));
+ ap_php_optarg = argv[ap_php_optind++];
+ }
+ else
+ {
+ ap_php_optarg = &argv[ap_php_optind][2];
+ ap_php_optind++;
+ }
+ return(*cp);
+ }
+ else
+ {
+ if (!argv[ap_php_optind][optchr+1])
+ {
+ dash = 0;
+ ap_php_optind++;
+ }
+ else
+ optchr++;
+ return(*cp);
+ }
+ assert(0);
+ return(0); /* never reached */
+}
+
+#ifdef TESTGETOPT
+int
+ main (int argc, char **argv)
+ {
+ int c;
+ extern char *ap_php_optarg;
+ extern int ap_php_optind;
+ int aflg = 0;
+ int bflg = 0;
+ int errflg = 0;
+ char *ofile = NULL;
+
+ while ((c = ap_php_getopt(argc, argv, "abo:")) != EOF)
+ switch (c) {
+ case 'a':
+ if (bflg)
+ errflg++;
+ else
+ aflg++;
+ break;
+ case 'b':
+ if (aflg)
+ errflg++;
+ else
+ bflg++;
+ break;
+ case 'o':
+ ofile = ap_php_optarg;
+ (void)printf("ofile = %s\n", ofile);
+ break;
+ case '?':
+ errflg++;
+ }
+ if (errflg) {
+ (void)fprintf(stderr,
+ "usage: cmd [-a|-b] [-o <filename>] files...\n");
+ exit (2);
+ }
+ for ( ; ap_php_optind < argc; ap_php_optind++)
+ (void)printf("%s\n", argv[ap_php_optind]);
+ return 0;
+ }
+
+#endif /* TESTGETOPT */
diff --git a/sapi/milter/milter.php b/sapi/milter/milter.php
new file mode 100644
index 0000000..0878f2a
--- /dev/null
+++ b/sapi/milter/milter.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * example milter script
+ *
+ * run: php-milter -D -p /path/to/sock milter.php
+ *
+ * for details on how to set up sendmail and configure the milter see
+ * http://www.sendmail.com/partner/resources/development/milter_api/
+ *
+ * for api details see
+ * http://www.sendmail.com/partner/resources/development/milter_api/api.html
+ *
+ * below is a list of all callbacks, that are available through the milter sapi,
+ * if you leave one or more out they simply won't get called (e.g. if you secify an
+ * empty php file, the milter would do nothing :)
+ */
+
+/**
+ * this function is called once on sapi startup,
+ * here you can specify the actions the filter may take
+ *
+ * see http://www.sendmail.com/partner/resources/development/milter_api/smfi_register.html#flags
+ */
+
+function milter_log($msg)
+{
+ $GLOBALS['log'] = fopen("/tmp/milter.log", "a");
+ fwrite($GLOBALS['log'], date("[H:i:s d.m.Y]") . "\t{$msg}\n");
+ fclose($GLOBALS['log']);
+}
+
+function milter_init() {
+ milter_log("-- startup --");
+ milter_log("milter_init()");
+ smfi_setflags(SMFIF_ADDHDRS);
+}
+
+/**
+ * is called once, at the start of each SMTP connection
+ */
+function milter_connect($connect)
+{
+ milter_log("milter_connect('$connect')");
+}
+
+/**
+ * is called whenever the client sends a HELO/EHLO command.
+ * It may therefore be called between zero and three times.
+ */
+function milter_helo($helo)
+{
+ milter_log("milter_helo('$helo')");
+}
+
+/**
+ * is called once at the beginning of each message,
+ * before milter_envrcpt.
+ */
+function milter_envfrom($args)
+{
+ milter_log("milter_envfrom(args[])");
+ foreach ($args as $ix => $arg) {
+ milter_log("\targs[$ix] = $arg");
+ }
+}
+
+/**
+ * is called once per recipient, hence one or more times per message,
+ * immediately after milter_envfrom
+ */
+function milter_envrcpt($args)
+{
+ milter_log("milter_envrcpt(args[])");
+ foreach ($args as $ix => $arg) {
+ milter_log("\targs[$ix] = $arg");
+ }
+}
+
+/**
+ * is called zero or more times between milter_envrcpt and milter_eoh,
+ * once per message header
+ */
+function milter_header($header, $value)
+{
+ milter_log("milter_header('$header', '$value')");
+}
+
+/**
+ * is called once after all headers have been sent and processed.
+ */
+function milter_eoh()
+{
+ milter_log("milter_eoh()");
+}
+
+/**
+ * is called zero or more times between milter_eoh and milter_eom.
+ */
+function milter_body($bodypart)
+{
+ milter_log("milter_body('$bodypart')");
+}
+
+/**
+ * is called once after all calls to milter_body for a given message.
+ * most of the api functions, that alter the message can only be called
+ * within this callback.
+ */
+function milter_eom()
+{
+ milter_log("milter_eom()");
+ /* add PHP header to the message */
+ smfi_addheader("X-PHP", phpversion());
+}
+
+/**
+ * may be called at any time during message processing
+ * (i.e. between some message-oriented routine and milter_eom).
+ */
+function milter_abort()
+{
+ milter_log("milter_abort()");
+}
+
+/**
+ * is always called once at the end of each connection.
+ */
+function milter_close()
+{
+ milter_log("milter_close()");
+}
+?>
diff --git a/sapi/milter/php_getopt.h b/sapi/milter/php_getopt.h
new file mode 100644
index 0000000..40da432
--- /dev/null
+++ b/sapi/milter/php_getopt.h
@@ -0,0 +1,7 @@
+/* Borrowed from Apache NT Port */
+#include "php.h"
+
+extern char *ap_php_optarg;
+extern int ap_php_optind;
+
+int ap_php_getopt(int argc, char* const *argv, const char *optstr);
diff --git a/sapi/milter/php_milter.c b/sapi/milter/php_milter.c
new file mode 100644
index 0000000..6856c07
--- /dev/null
+++ b/sapi/milter/php_milter.c
@@ -0,0 +1,1209 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Harald Radi <phanto@php.net> |
+ | Parts based on CGI SAPI Module by |
+ | Rasmus Lerdorf, Stig Bakken and Zeev Suraski |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "php.h"
+#include "php_globals.h"
+#include "php_variables.h"
+#include "zend_modules.h"
+
+#ifndef ZTS
+#error SRM sapi module is only useable in thread-safe mode
+#endif
+
+#include "SAPI.h"
+
+#include <stdio.h>
+#include "php.h"
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#if HAVE_SETLOCALE
+#include <locale.h>
+#endif
+#include "zend.h"
+#include "zend_extensions.h"
+#include "php_ini.h"
+#include "php_globals.h"
+#include "php_main.h"
+#include "fopen_wrappers.h"
+#include "ext/standard/php_standard.h"
+
+#ifdef __riscos__
+#include <unixlib/local.h>
+#endif
+
+#include "zend_compile.h"
+#include "zend_execute.h"
+#include "zend_highlight.h"
+#include "zend_indent.h"
+
+#include "libmilter/mfapi.h"
+
+#include "php_getopt.h"
+
+#define OPTSTRING "ac:d:Def:hnp:vVz:?"
+#define MG(v) TSRMG(milter_globals_id, zend_milter_globals *, v)
+
+#define IS_NONE "%s(): This function must not be called outside of a milter callback function's scope"
+#define NOT_EOM "%s(): This function can only be used inside the milter_eom callback's scope"
+#define NOT_INIT "%s(): This function can only be used inside the milter_init callback's scope"
+
+#define MLFI_NONE 0
+#define MLFI_CONNECT 1
+#define MLFI_HELO 2
+#define MLFI_ENVFROM 3
+#define MLFI_ENVRCPT 4
+#define MLFI_HEADER 5
+#define MLFI_EOH 6
+#define MLFI_BODY 7
+#define MLFI_EOM 8
+#define MLFI_ABORT 9
+#define MLFI_CLOSE 10
+#define MLFI_INIT 11
+
+/* {{{ globals
+ */
+extern char *ap_php_optarg;
+extern int ap_php_optind;
+
+static int flag_debug=0;
+static char *filename = NULL;
+
+/* per thread */
+ZEND_BEGIN_MODULE_GLOBALS(milter)
+ SMFICTX *ctx;
+ int state;
+ int initialized;
+ZEND_END_MODULE_GLOBALS(milter)
+
+ZEND_DECLARE_MODULE_GLOBALS(milter)
+/* }}} */
+
+/* this method is called only once when the milter starts */
+/* {{{ Init Milter
+*/
+static int mlfi_init()
+{
+ int ret = 0;
+ zend_file_handle file_handle;
+ zval function_name, retval;
+ int status;
+ TSRMLS_FETCH();
+
+ /* request startup */
+ if (php_request_startup(TSRMLS_C)==FAILURE) {
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+ php_request_shutdown((void *) 0);
+
+ return -1;
+ }
+
+ /* disable headers */
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+
+ if (filename == NULL) {
+ php_printf("No input file specified");
+ return SMFIS_TEMPFAIL;
+ }
+
+ if (!(file_handle.handle.fp = VCWD_FOPEN(filename, "rb"))) {
+ php_printf("Could not open input file: %s\n", filename);
+ return SMFIS_TEMPFAIL;
+ }
+
+ file_handle.type = ZEND_HANDLE_FP;
+ file_handle.filename = filename;
+ file_handle.free_filename = 0;
+ file_handle.opened_path = NULL;
+
+ php_execute_script(&file_handle TSRMLS_CC);
+
+ /* call userland */
+ INIT_ZVAL(function_name);
+
+ ZVAL_STRING(&function_name, "milter_init", 0);
+
+ /* set the milter context for possible use in API functions */
+ MG(state) = MLFI_INIT;
+
+ status = call_user_function(CG(function_table), NULL, &function_name, &retval, 0, NULL TSRMLS_CC);
+
+ MG(state) = MLFI_NONE;
+ MG(initialized) = 1;
+
+ if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
+ ret = Z_LVAL(retval);
+ }
+
+ php_request_shutdown((void *) 0);
+
+ return ret;
+}
+/* }}} */
+
+/* {{{ Milter callback functions
+ */
+
+/* connection info filter, is called whenever sendmail connects to the milter */
+/* {{{ mlfi_connect()
+*/
+static sfsistat mlfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr)
+{
+ zend_file_handle file_handle;
+ zval function_name, retval, *param[1];
+ int status;
+ TSRMLS_FETCH();
+
+ /* request startup */
+ if (php_request_startup(TSRMLS_C)==FAILURE) {
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+ php_request_shutdown((void *) 0);
+
+ return SMFIS_TEMPFAIL;
+ }
+
+ /* disable headers */
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+
+ if (filename == NULL) {
+ php_printf("No input file specified");
+ return SMFIS_TEMPFAIL;
+ }
+
+ if (!(file_handle.handle.fp = VCWD_FOPEN(filename, "rb"))) {
+ php_printf("Could not open input file: %s\n", filename);
+ return SMFIS_TEMPFAIL;
+ }
+
+ file_handle.type = ZEND_HANDLE_FP;
+ file_handle.filename = filename;
+ file_handle.free_filename = 0;
+ file_handle.opened_path = NULL;
+
+ php_execute_script(&file_handle TSRMLS_CC);
+
+ /* call userland */
+ INIT_ZVAL(function_name);
+
+ ALLOC_ZVAL(param[0]);
+ INIT_PZVAL(param[0]);
+
+ ZVAL_STRING(&function_name, "milter_connect", 0);
+ ZVAL_STRING(param[0], hostname, 1);
+
+ /* set the milter context for possible use in API functions */
+ MG(ctx) = ctx;
+ MG(state) = MLFI_CONNECT;
+
+ status = call_user_function(CG(function_table), NULL, &function_name, &retval, 1, param TSRMLS_CC);
+
+ MG(state) = MLFI_NONE;
+ zval_ptr_dtor(param);
+ if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
+ return Z_LVAL(retval);
+ }
+
+ return SMFIS_CONTINUE;
+}
+/* }}} */
+
+/* SMTP HELO command filter */
+/* {{{ mlfi_helo()
+*/
+static sfsistat mlfi_helo(SMFICTX *ctx, char *helohost)
+{
+ zval function_name, retval, *param[1];
+ int status;
+ TSRMLS_FETCH();
+
+ /* call userland */
+ INIT_ZVAL(function_name);
+
+ ALLOC_ZVAL(param[0]);
+ INIT_PZVAL(param[0]);
+
+ ZVAL_STRING(&function_name, "milter_helo", 0);
+ ZVAL_STRING(param[0], helohost, 1);
+
+ /* set the milter context for possible use in API functions */
+ MG(ctx) = ctx;
+ MG(state) = MLFI_HELO;
+
+ status = call_user_function(CG(function_table), NULL, &function_name, &retval, 1, param TSRMLS_CC);
+
+ MG(state) = MLFI_NONE;
+ zval_ptr_dtor(param);
+
+ if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
+ return Z_LVAL(retval);
+ }
+
+ return SMFIS_CONTINUE;
+}
+/* }}} */
+
+/* envelope sender filter */
+/* {{{ mlfi_envform()
+*/
+static sfsistat mlfi_envfrom(SMFICTX *ctx, char **argv)
+{
+ zval function_name, retval, *param[1];
+ int status;
+ TSRMLS_FETCH();
+
+ /* call userland */
+ INIT_ZVAL(function_name);
+
+ ALLOC_ZVAL(param[0]);
+ INIT_PZVAL(param[0]);
+
+ ZVAL_STRING(&function_name, "milter_envfrom", 0);
+ array_init(param[0]);
+
+ while (*argv) {
+ add_next_index_string(param[0], *argv, 1);
+ argv++;
+ }
+
+ /* set the milter context for possible use in API functions */
+ MG(ctx) = ctx;
+ MG(state) = MLFI_ENVFROM;
+
+ status = call_user_function(CG(function_table), NULL, &function_name, &retval, 1, param TSRMLS_CC);
+
+ MG(state) = MLFI_NONE;
+ zval_ptr_dtor(param);
+
+ if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
+ return Z_LVAL(retval);
+ }
+
+ return SMFIS_CONTINUE;
+}
+/* }}} */
+
+/* envelope recipient filter */
+/* {{{ mlfi_envrcpt()
+*/
+static sfsistat mlfi_envrcpt(SMFICTX *ctx, char **argv)
+{
+ zval function_name, retval, *param[1];
+ int status;
+ TSRMLS_FETCH();
+
+ /* call userland */
+ INIT_ZVAL(function_name);
+
+ ALLOC_ZVAL(param[0]);
+ INIT_PZVAL(param[0]);
+
+ ZVAL_STRING(&function_name, "milter_envrcpt", 0);
+ array_init(param[0]);
+
+ while (*argv) {
+ add_next_index_string(param[0], *argv, 1);
+ argv++;
+ }
+
+ /* set the milter context for possible use in API functions */
+ MG(ctx) = ctx;
+ MG(state) = MLFI_ENVRCPT;
+
+ status = call_user_function(CG(function_table), NULL, &function_name, &retval, 1, param TSRMLS_CC);
+
+ MG(state) = MLFI_NONE;
+
+ zval_ptr_dtor(param);
+
+ if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
+ return Z_LVAL(retval);
+ }
+
+ return SMFIS_CONTINUE;
+}
+/* }}} */
+
+/* header filter */
+/* {{{ mlfi_header()
+*/
+static sfsistat mlfi_header(SMFICTX *ctx, char *headerf, char *headerv)
+{
+ zval function_name, retval, *param[2];
+ int status;
+ TSRMLS_FETCH();
+
+ /* call userland */
+ INIT_ZVAL(function_name);
+
+ ALLOC_ZVAL(param[0]);
+ ALLOC_ZVAL(param[1]);
+ INIT_PZVAL(param[0]);
+ INIT_PZVAL(param[1]);
+
+ ZVAL_STRING(&function_name, "milter_header", 0);
+ ZVAL_STRING(param[0], headerf, 1);
+ ZVAL_STRING(param[1], headerv, 1);
+
+ /* set the milter context for possible use in API functions */
+ MG(ctx) = ctx;
+ MG(state) = MLFI_HEADER;
+
+ status = call_user_function(CG(function_table), NULL, &function_name, &retval, 2, param TSRMLS_CC);
+
+ MG(state) = MLFI_NONE;
+
+ zval_ptr_dtor(&param[0]);
+ zval_ptr_dtor(&param[1]);
+
+ if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
+ return Z_LVAL(retval);
+ }
+
+ return SMFIS_CONTINUE;
+}
+/* }}} */
+
+/* end of header */
+/* {{{ mlfi_eoh()
+*/
+static sfsistat mlfi_eoh(SMFICTX *ctx)
+{
+ zval function_name, retval;
+ int status;
+ TSRMLS_FETCH();
+
+ /* call userland */
+ INIT_ZVAL(function_name);
+ ZVAL_STRING(&function_name, "milter_eoh", 0);
+
+ /* set the milter context for possible use in API functions */
+ MG(ctx) = ctx;
+ MG(state) = MLFI_EOH;
+
+ status = call_user_function(CG(function_table), NULL, &function_name, &retval, 0, NULL TSRMLS_CC);
+
+ MG(state) = MLFI_NONE;
+
+ if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
+ return Z_LVAL(retval);
+ }
+
+ return SMFIS_CONTINUE;
+}
+/* }}} */
+
+/* body block */
+/* {{{ mlfi_body()
+*/
+static sfsistat mlfi_body(SMFICTX *ctx, u_char *bodyp, size_t len)
+{
+ zval function_name, retval, *param[1];
+ int status;
+ TSRMLS_FETCH();
+
+ /* call userland */
+ INIT_ZVAL(function_name);
+
+ ALLOC_ZVAL(param[0]);
+ INIT_PZVAL(param[0]);
+
+ ZVAL_STRING(&function_name, "milter_body", 0);
+ ZVAL_STRINGL(param[0], (char*)bodyp, len, 1); /*alex*/
+
+ /* set the milter context for possible use in API functions */
+ MG(ctx) = ctx;
+ MG(state) = MLFI_BODY;
+
+ status = call_user_function(CG(function_table), NULL, &function_name, &retval, 1, param TSRMLS_CC);
+
+ MG(state) = MLFI_NONE;
+
+ zval_ptr_dtor(param);
+
+ if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
+ return Z_LVAL(retval);
+ }
+
+ return SMFIS_CONTINUE;
+}
+/* }}} */
+
+/* end of message */
+/* {{{ mlfi_eom()
+*/
+static sfsistat mlfi_eom(SMFICTX *ctx)
+{
+ zval function_name, retval;
+ int status;
+ TSRMLS_FETCH();
+
+ /* call userland */
+ INIT_ZVAL(function_name);
+ ZVAL_STRING(&function_name, "milter_eom", 0);
+
+ /* set the milter context for possible use in API functions */
+ MG(ctx) = ctx;
+ MG(state) = MLFI_EOM;
+
+ status = call_user_function(CG(function_table), NULL, &function_name, &retval, 0, NULL TSRMLS_CC);
+
+ MG(state) = MLFI_NONE;
+
+ if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
+ return Z_LVAL(retval);
+ }
+
+ return SMFIS_CONTINUE;
+}
+/* }}} */
+
+/* message aborted */
+/* {{{ mlfi_abort()
+*/
+static sfsistat mlfi_abort(SMFICTX *ctx)
+{
+ zval function_name, retval;
+ int status;
+ TSRMLS_FETCH();
+
+ /* call userland */
+ INIT_ZVAL(function_name);
+ ZVAL_STRING(&function_name, "milter_abort", 0);
+
+ /* set the milter context for possible use in API functions */
+ MG(ctx) = ctx;
+ MG(state) = MLFI_ABORT;
+
+ status = call_user_function(CG(function_table), NULL, &function_name, &retval, 0, NULL TSRMLS_CC);
+
+ MG(state) = MLFI_NONE;
+
+ if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
+ return Z_LVAL(retval);
+ }
+
+ return SMFIS_CONTINUE;
+}
+/* }}} */
+
+/* connection cleanup */
+/* {{{ mlfi_close()
+*/
+static sfsistat mlfi_close(SMFICTX *ctx)
+{
+ int ret = SMFIS_CONTINUE;
+ zval function_name, retval;
+ int status;
+ TSRMLS_FETCH();
+
+ /* call userland */
+ INIT_ZVAL(function_name);
+ ZVAL_STRING(&function_name, "milter_close", 0);
+
+ /* set the milter context for possible use in API functions */
+ MG(ctx) = ctx;
+ MG(state) = MLFI_CLOSE;
+
+ status = call_user_function(CG(function_table), NULL, &function_name, &retval, 0, NULL TSRMLS_CC);
+
+ MG(state) = MLFI_NONE;
+
+ if (status == SUCCESS && Z_TYPE(retval) == IS_LONG) {
+ ret = Z_LVAL(retval);
+ }
+
+ php_request_shutdown((void *) 0);
+
+ return ret;
+}
+/* }}} */
+/* }}} */
+
+/* {{{ Milter entry struct
+ */
+struct smfiDesc smfilter = {
+ "php-milter", /* filter name */
+ SMFI_VERSION, /* version code -- leave untouched */
+ 0, /* flags */
+ mlfi_connect, /* info filter callback */
+ mlfi_helo, /* HELO filter callback */
+ mlfi_envfrom, /* envelope filter callback */
+ mlfi_envrcpt, /* envelope recipient filter callback */
+ mlfi_header, /* header filter callback */
+ mlfi_eoh, /* end of header callback */
+ mlfi_body, /* body filter callback */
+ mlfi_eom, /* end of message callback */
+ mlfi_abort, /* message aborted callback */
+ mlfi_close, /* connection cleanup callback */
+};
+/* }}} */
+
+/* {{{ PHP Milter API
+ */
+
+/* {{{ proto void smfi_setflags(long flags)
+ Sets the flags describing the actions the filter may take. */
+PHP_FUNCTION(smfi_setflags)
+{
+ long flags;
+
+ /* valid only in the init callback */
+ if (MG(state) != MLFI_INIT) {
+ php_error(E_WARNING, NOT_INIT, get_active_function_name(TSRMLS_C));
+ } else if (zend_parse_parameters(1 TSRMLS_CC, "l", &flags) == SUCCESS) {
+ flags = flags & (SMFIF_ADDHDRS|SMFIF_CHGHDRS|SMFIF_CHGBODY|SMFIF_ADDRCPT|SMFIF_DELRCPT);
+ smfilter.xxfi_flags = flags;
+ }
+}
+/* }}} */
+
+/* {{{ proto void smfi_settimeout(long timeout)
+ Sets the number of seconds libmilter will wait for an MTA connection before timing out a socket. */
+PHP_FUNCTION(smfi_settimeout)
+{
+ long timeout;
+
+ /* valid only in the init callback */
+ if (MG(state) != MLFI_INIT) {
+ php_error(E_WARNING, NOT_INIT, get_active_function_name(TSRMLS_C));
+ } else if (zend_parse_parameters(1 TSRMLS_CC, "l", &timeout) == SUCCESS) {
+ smfi_settimeout(timeout);
+ }
+}
+/* }}} */
+
+/* {{{ proto string smfi_getsymval(string macro)
+ Returns the value of the given macro or NULL if the macro is not defined. */
+PHP_FUNCTION(smfi_getsymval)
+{
+ char *symname, *ret;
+ int len;
+
+ /* valid in any callback */
+ if (MG(state) == MLFI_NONE) {
+ php_error(E_WARNING, IS_NONE, get_active_function_name(TSRMLS_C));
+ } else if (zend_parse_parameters(1 TSRMLS_CC, "s", &symname, &len) == SUCCESS) {
+ if ((ret = smfi_getsymval(MG(ctx), symname)) != NULL) {
+ RETURN_STRING(ret, 1);
+ }
+ }
+
+ RETURN_NULL();
+}
+/* }}} */
+
+/* {{{ proto bool smfi_setreply(string rcode, string xcode, string message)
+ Directly set the SMTP error reply code for this connection.
+ This code will be used on subsequent error replies resulting from actions taken by this filter. */
+PHP_FUNCTION(smfi_setreply)
+{
+ char *rcode, *xcode, *message;
+ int len;
+
+ /* valid in any callback */
+ if (MG(state) == MLFI_NONE) {
+ php_error(E_WARNING, IS_NONE, get_active_function_name(TSRMLS_C));
+ } else if (zend_parse_parameters(3 TSRMLS_CC, "sss", &rcode, &len, &xcode, &len, &message, &len) == SUCCESS) {
+ if (smfi_setreply(MG(ctx), rcode, xcode, message) == MI_SUCCESS) {
+ RETURN_TRUE;
+ }
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto bool smfi_addheader(string headerf, string headerv)
+ Adds a header to the current message. */
+PHP_FUNCTION(smfi_addheader)
+{
+ char *f, *v;
+ int len;
+
+ /* valid only in milter_eom */
+ if (MG(state) != MLFI_EOM) {
+ php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
+ } else if (zend_parse_parameters(2 TSRMLS_CC, "ss", &f, &len, &v, &len) == SUCCESS) {
+ if (smfi_addheader(MG(ctx), f, v) == MI_SUCCESS) {
+ RETURN_TRUE;
+ }
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto bool smfi_chgheader(string headerf, string headerv)
+ Changes a header's value for the current message. */
+PHP_FUNCTION(smfi_chgheader)
+{
+ char *f, *v;
+ long idx;
+ int len;
+
+ /* valid only in milter_eom */
+ if (MG(state) != MLFI_EOM) {
+ php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
+ } else if (zend_parse_parameters(3 TSRMLS_CC, "sls", &f, &len, &idx, &v, &len) == SUCCESS) {
+ if (smfi_chgheader(MG(ctx), f, idx, v) == MI_SUCCESS) {
+ RETURN_TRUE;
+ }
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto bool smfi_addrcpt(string rcpt)
+ Add a recipient to the message envelope. */
+PHP_FUNCTION(smfi_addrcpt)
+{
+ char *rcpt;
+ int len;
+
+ /* valid only in milter_eom */
+ if (MG(state) != MLFI_EOM) {
+ php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
+ } else if (zend_parse_parameters(1 TSRMLS_CC, "s", &rcpt, &len) == SUCCESS) {
+ if (smfi_addrcpt(MG(ctx), rcpt) == MI_SUCCESS) {
+ RETURN_TRUE;
+ }
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto bool smfi_delrcpt(string rcpt)
+ Removes the named recipient from the current message's envelope. */
+PHP_FUNCTION(smfi_delrcpt)
+{
+ char *rcpt;
+ int len;
+
+ /* valid only in milter_eom */
+ if (MG(state) != MLFI_EOM) {
+ php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
+ } else if (zend_parse_parameters(1 TSRMLS_CC, "s", &rcpt, &len) == SUCCESS) {
+ if (smfi_delrcpt(MG(ctx), rcpt) == MI_SUCCESS) {
+ RETURN_TRUE;
+ }
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ proto bool smfi_replacebody(string body)
+ Replaces the body of the current message. If called more than once,
+ subsequent calls result in data being appended to the new body. */
+PHP_FUNCTION(smfi_replacebody)
+{
+ char *body;
+ int len;
+
+ /* valid only in milter_eom */
+ if (MG(state) != MLFI_EOM) {
+ php_error(E_WARNING, NOT_EOM, get_active_function_name(TSRMLS_C));
+ } else if (zend_parse_parameters(1 TSRMLS_CC, "s", &body, &len) == SUCCESS) {
+ if (smfi_replacebody(MG(ctx), (u_char*)body, len) == MI_SUCCESS) {
+ RETURN_TRUE;
+ }
+ }
+
+ RETURN_FALSE;
+}
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+PHP_MINIT_FUNCTION(milter)
+{
+ REGISTER_LONG_CONSTANT("SMFIS_CONTINUE", SMFIS_CONTINUE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("SMFIS_REJECT", SMFIS_REJECT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("SMFIS_DISCARD", SMFIS_DISCARD, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("SMFIS_ACCEPT", SMFIS_ACCEPT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("SMFIS_TEMPFAIL", SMFIS_TEMPFAIL, CONST_CS | CONST_PERSISTENT);
+
+ REGISTER_LONG_CONSTANT("SMFIF_ADDHDRS", SMFIF_ADDHDRS, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("SMFIF_CHGHDRS", SMFIF_CHGHDRS, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("SMFIF_CHGBODY", SMFIF_CHGBODY, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("SMFIF_ADDRCPT", SMFIF_ADDRCPT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("SMFIF_DELRCPT", SMFIF_DELRCPT, CONST_CS | CONST_PERSISTENT);
+
+ ZEND_INIT_MODULE_GLOBALS(milter, NULL, NULL);
+
+ MG(state) = MLFI_NONE;
+ MG(initialized) = 0;
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(milter)
+{
+ php_info_print_table_start();
+ php_info_print_table_header(2, "Milter support", "enabled");
+ php_info_print_table_end();
+}
+/* }}} */
+/* }}} */
+
+/* {{{ arginfo */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_setflags, 0, 0, 1)
+ ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_settimeout, 0, 0, 1)
+ ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_getsymval, 0, 0, 1)
+ ZEND_ARG_INFO(0, macro)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_setreply, 0, 0, 3)
+ ZEND_ARG_INFO(0, rcode)
+ ZEND_ARG_INFO(0, xcode)
+ ZEND_ARG_INFO(0, message)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_addheader, 0, 0, 2)
+ ZEND_ARG_INFO(0, headerf)
+ ZEND_ARG_INFO(0, headerv)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_chgheader, 0, 0, 2)
+ ZEND_ARG_INFO(0, headerf)
+ ZEND_ARG_INFO(0, headerv)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_addrcpt, 0, 0, 1)
+ ZEND_ARG_INFO(0, rcpt)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_delrcpt, 0, 0, 1)
+ ZEND_ARG_INFO(0, rcpt)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_smfi_replacebody, 0, 0, 1)
+ ZEND_ARG_INFO(0, body)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+/* {{{ milter_functions[]
+*/
+const static zend_function_entry milter_functions[] = {
+ PHP_FE(smfi_setflags, arginfo_smfi_setflags)
+ PHP_FE(smfi_settimeout, arginfo_smfi_settimeout)
+ PHP_FE(smfi_getsymval, arginfo_smfi_getsymval)
+ PHP_FE(smfi_setreply, arginfo_smfi_setreply)
+ PHP_FE(smfi_addheader, arginfo_smfi_addheader)
+ PHP_FE(smfi_chgheader, arginfo_smfi_chgheader)
+ PHP_FE(smfi_addrcpt, arginfo_smfi_addrcpt)
+ PHP_FE(smfi_delrcpt, arginfo_smfi_delrcpt)
+ PHP_FE(smfi_replacebody, arginfo_smfi_replacebody)
+ PHP_FE_END
+};
+/* }}} */
+
+/* {{{ Zend module entry
+*/
+static zend_module_entry php_milter_module = {
+ STANDARD_MODULE_HEADER,
+ "Milter",
+ milter_functions,
+ PHP_MINIT(milter),
+ NULL,
+ NULL,
+ NULL,
+ PHP_MINFO(milter),
+ "0.1.0",
+ STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+/* {{{ Milter SAPI
+*/
+static int sapi_milter_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ return str_length;
+}
+
+static void sapi_milter_flush(void *server_context)
+{
+}
+
+static void sapi_milter_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ php_register_variable ("SERVER_SOFTWARE", "Sendmail Milter", track_vars_array TSRMLS_CC);
+}
+
+static int sapi_milter_post_read(char *buf, uint count_bytes TSRMLS_DC)
+{
+ return 0;
+}
+
+static char* sapi_milter_read_cookies(TSRMLS_D)
+{
+ return NULL;
+}
+
+static int sapi_milter_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+static int php_milter_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &php_milter_module, 1) == FAILURE) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ sapi_module_struct milter_sapi_module
+*/
+static sapi_module_struct milter_sapi_module = {
+ "milter", /* name */
+ "Sendmail Milter SAPI", /* pretty name */
+
+ php_milter_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ sapi_milter_ub_write, /* unbuffered write */
+ sapi_milter_flush, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ NULL, /* header handler */
+ sapi_milter_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ sapi_milter_post_read, /* read POST data */
+ sapi_milter_read_cookies, /* read Cookies */
+
+ sapi_milter_register_variables, /* register server variables */
+ NULL, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ NULL, /* Block interruptions */
+ NULL, /* Unblock interruptions */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+/* }}} */
+
+/****
+* ripped from cli, has to be cleaned up !
+*/
+
+/* {{{ php_milter_usage
+*/
+static void php_milter_usage(char *argv0)
+{
+ char *prog;
+
+ prog = strrchr(argv0, '/');
+ if (prog) {
+ prog++;
+ } else {
+ prog = "php-milter";
+ }
+
+ printf( "Usage: %s [options] [-f] <file> [args...]\n"
+ " %s [options] [-- args...]\n"
+ " -a Run interactively\n"
+ " -c <path>|<file> Look for php.ini file in this directory\n"
+ " -n No php.ini file will be used\n"
+ " -d foo[=bar] Define INI entry foo with value 'bar'\n"
+ " -D run as daemon\n"
+ " -e Generate extended information for debugger/profiler\n"
+ " -f <file> Parse <file>.\n"
+ " -h This help\n"
+ " -p <socket> path to create socket\n"
+ " -v Version number\n"
+ " -V <n> set debug level to n (1 or 2).\n"
+ " -z <file> Load Zend extension <file>.\n"
+ " args... Arguments passed to script. Use -- args when first argument \n"
+ " starts with - or script is read from stdin\n"
+ , prog, prog);
+}
+/* }}} */
+
+static void define_command_line_ini_entry(char *arg) /* {{{ */
+{
+ char *name, *value;
+
+ name = arg;
+ value = strchr(arg, '=');
+ if (value) {
+ *value = 0;
+ value++;
+ } else {
+ value = "1";
+ }
+ zend_alter_ini_entry(name, strlen(name)+1, value, strlen(value), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+}
+/* }}} */
+
+/* {{{ main
+*/
+int main(int argc, char *argv[])
+{
+ char *sock = NULL;
+ int dofork = 0;
+
+ int exit_status = SUCCESS;
+ int c;
+/* temporary locals */
+ int orig_optind=ap_php_optind;
+ char *orig_optarg=ap_php_optarg;
+ int interactive=0;
+ char *param_error=NULL;
+/* end of temporary locals */
+
+ void ***tsrm_ls;
+
+#ifdef HAVE_SIGNAL_H
+#if defined(SIGPIPE) && defined(SIG_IGN)
+ signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE in standalone mode so
+ that sockets created via fsockopen()
+ don't kill PHP if the remote site
+ closes it. in apache|apxs mode apache
+ does that for us! thies@thieso.net
+ 20000419 */
+#endif
+#endif
+
+
+ tsrm_startup(1, 1, 0, NULL);
+ sapi_startup(&milter_sapi_module);
+
+ while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) {
+ switch (c) {
+ case 'c':
+ milter_sapi_module.php_ini_path_override = strdup(ap_php_optarg);
+ break;
+ case 'n':
+ milter_sapi_module.php_ini_ignore = 1;
+ break;
+ }
+ }
+ ap_php_optind = orig_optind;
+ ap_php_optarg = orig_optarg;
+
+ milter_sapi_module.executable_location = argv[0];
+
+ tsrm_ls = ts_resource(0);
+
+ sapi_module.startup(&milter_sapi_module);
+
+ zend_first_try {
+ while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) {
+ switch (c) {
+ case '?':
+ php_output_tearup();
+ SG(headers_sent) = 1;
+ php_milter_usage(argv[0]);
+ php_output_teardown();
+ exit(1);
+ break;
+ }
+ }
+ ap_php_optind = orig_optind;
+ ap_php_optarg = orig_optarg;
+
+ /* Set some CLI defaults */
+ SG(options) |= SAPI_OPTION_NO_CHDIR;
+ zend_alter_ini_entry("html_errors", 12, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+ zend_alter_ini_entry("max_execution_time", 19, "0", 1, PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
+
+ zend_uv.html_errors = 0; /* tell the engine we're in non-html mode */
+
+ while ((c = ap_php_getopt(argc, argv, OPTSTRING)) != -1) {
+ switch (c) {
+
+ case 'a': /* interactive mode */
+ printf("Interactive mode enabled\n\n");
+ interactive=1;
+ break;
+
+ case 'C': /* don't chdir to the script directory */
+ /* This is default so NOP */
+ break;
+ case 'd': /* define ini entries on command line */
+ define_command_line_ini_entry(ap_php_optarg);
+ break;
+
+ case 'D': /* daemon */
+ dofork = 1;
+ break;
+
+ case 'e': /* enable extended info output */
+ CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
+ break;
+
+ case 'f': /* parse file */
+ filename = ap_php_optarg;
+ break;
+
+ case 'h': /* help & quit */
+ case '?':
+ php_output_tearup();
+ SG(headers_sent) = 1;
+ php_milter_usage(argv[0]);
+ php_output_teardown();
+ exit(1);
+ break;
+
+ case 'p': /* socket */
+ sock = strdup(ap_php_optarg);
+ break;
+
+ case 'v': /* show php version & quit */
+ if (php_request_startup(TSRMLS_C)==FAILURE) {
+ zend_ini_deactivate(TSRMLS_C);
+ php_module_shutdown(TSRMLS_C);
+ sapi_shutdown();
+ tsrm_shutdown();
+
+ exit(1);
+ }
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+ php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2013 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
+ php_output_teardown();
+ exit(1);
+ break;
+
+ case 'V': /* verbose */
+ flag_debug = atoi(ap_php_optarg);
+ break;
+
+ case 'z': /* load extension file */
+ zend_load_extension(ap_php_optarg);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (param_error) {
+ SG(headers_sent) = 1;
+ SG(request_info).no_headers = 1;
+ PUTS(param_error);
+ exit(1);
+ }
+
+ CG(interactive) = interactive;
+
+ /* only set script_file if not set already and not in direct mode and not at end of parameter list */
+ if (argc > ap_php_optind && !filename) {
+ filename=argv[ap_php_optind];
+ ap_php_optind++;
+ }
+
+ /* check if file exists, exit else */
+
+ if (dofork) {
+ switch(fork()) {
+ case -1: /* Uh-oh, we have a problem forking. */
+ fprintf(stderr, "Uh-oh, couldn't fork!\n");
+ exit(errno);
+ break;
+ case 0: /* Child */
+ break;
+ default: /* Parent */
+ exit(0);
+ }
+ }
+
+ if (sock) {
+ struct stat junk;
+ if (stat(sock,&junk) == 0) unlink(sock);
+ }
+
+ openlog("php-milter", LOG_PID, LOG_MAIL);
+
+ if ((exit_status = mlfi_init())) {
+ syslog(1, "mlfi_init failed.");
+ closelog();
+ goto err;
+ }
+
+ smfi_setconn(sock);
+ if (smfi_register(smfilter) == MI_FAILURE) {
+ syslog(1, "smfi_register failed.");
+ fprintf(stderr, "smfi_register failed\n");
+ closelog();
+ goto err;
+ } else {
+ exit_status = smfi_main();
+ }
+
+ closelog();
+
+ if (milter_sapi_module.php_ini_path_override) {
+ free(milter_sapi_module.php_ini_path_override);
+ }
+
+ } zend_catch {
+ exit_status = EG(exit_status);
+ } zend_end_try();
+
+err:
+ php_module_shutdown(TSRMLS_C);
+ sapi_shutdown();
+ tsrm_shutdown();
+
+ exit(exit_status);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/milter/php_milter.h b/sapi/milter/php_milter.h
new file mode 100644
index 0000000..72d7ac5
--- /dev/null
+++ b/sapi/milter/php_milter.h
@@ -0,0 +1,31 @@
+#ifndef PHP_MILTER_H
+#define PHP_MILTER_H
+
+#include "libmilter/mfapi.h"
+
+#define MLFI_NONE 0
+#define MLFI_CONNECT 1
+#define MLFI_HELO 2
+#define MLFI_ENVFROM 3
+#define MLFI_ENVRCPT 4
+#define MLFI_HEADER 5
+#define MLFI_EOH 6
+#define MLFI_BODY 7
+#define MLFI_EOM 8
+#define MLFI_ABORT 9
+#define MLFI_CLOSE 10
+#define MLFI_INIT 11
+
+#define MG(v) TSRMG(milter_globals_id, zend_milter_globals *, v)
+
+typedef struct {
+ pthread_t thread;
+ MUTEX_T receiver;
+ MUTEX_T sender;
+ SMFICTX *ctx;
+ sfsistat retval;
+ int message;
+ void **args;
+} worker_thread;
+
+#endif
diff --git a/sapi/nsapi/CREDITS b/sapi/nsapi/CREDITS
new file mode 100644
index 0000000..2a05919
--- /dev/null
+++ b/sapi/nsapi/CREDITS
@@ -0,0 +1,2 @@
+NSAPI
+Jayakumar Muthukumarasamy, Uwe Schindler
diff --git a/sapi/nsapi/config.m4 b/sapi/nsapi/config.m4
new file mode 100644
index 0000000..8923f53
--- /dev/null
+++ b/sapi/nsapi/config.m4
@@ -0,0 +1,39 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(nsapi, for NSAPI support,
+[ --with-nsapi=DIR Build PHP as NSAPI module for Netscape/iPlanet/Sun Webserver], no, no)
+
+if test "$PHP_NSAPI" != "no"; then
+ if test ! -d $PHP_NSAPI/bin ; then
+ AC_MSG_ERROR(Please specify the path to the root of your Netscape/iPlanet/Sun Webserver using --with-nsapi=DIR)
+ fi
+ AC_MSG_CHECKING([for NSAPI include files])
+ if test -d $PHP_NSAPI/include ; then
+ NSAPI_INC_DIR="$PHP_NSAPI/include"
+ AC_MSG_RESULT([Netscape 3.x / Sun 7.x style])
+ AC_CHECK_HEADERS([$NSAPI_INC_DIR/nsapi.h])
+ NSAPI_INCLUDE="-I$NSAPI_INC_DIR"
+ fi
+ if test -d $PHP_NSAPI/plugins/include ; then
+ NSAPI_INC_DIR="$PHP_NSAPI/plugins/include"
+ AC_MSG_RESULT([iPlanet 4.x / Sun 6.x style])
+ AC_CHECK_HEADERS([$NSAPI_INC_DIR/nsapi.h])
+ NSAPI_INCLUDE="$NSAPI_INCLUDE -I$NSAPI_INC_DIR"
+ fi
+ if test -z "$NSAPI_INCLUDE"; then
+ AC_MSG_ERROR([Please check you have nsapi.h in either $PHP_NSAPI/include or $PHP_NSAPI/plugins/include])
+ fi
+
+ PHP_EVAL_INCLINE($NSAPI_INCLUDE)
+ PHP_BUILD_THREAD_SAFE
+ AC_DEFINE(HAVE_NSAPI, 1, [Whether you have a Netscape/iPlanet/Sun Webserver])
+ PHP_SELECT_SAPI(nsapi, shared, nsapi.c)
+ INSTALL_IT="\$(INSTALL) -m 0755 $SAPI_SHARED \$(INSTALL_ROOT)$PHP_NSAPI/bin/"
+fi
+
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/nsapi/config.w32 b/sapi/nsapi/config.w32
new file mode 100644
index 0000000..17b86d2
--- /dev/null
+++ b/sapi/nsapi/config.w32
@@ -0,0 +1,20 @@
+// vim:ft=javascript
+// $Id$
+
+ARG_ENABLE('nsapi', 'Build NSAPI for Netscape/iPlanet/SunONE webservers', 'no');
+
+ARG_WITH('nsapi-includes', 'Where to find NSAPI headers', null);
+ARG_WITH('nsapi-libs', 'Where to find NSAPI libraries', null);
+
+if (PHP_NSAPI != "no") {
+ if (PHP_ZTS == "no") {
+ WARNING("NSAPI module requires an --enable-zts build of PHP");
+ PHP_NSAPI = "no"
+ } else if (CHECK_HEADER_ADD_INCLUDE("nsapi.h", "CFLAGS_NSAPI",
+ PHP_NSAPI + ';' + PHP_NSAPI_INCLUDES) &&
+ CHECK_LIB("ns-httpd*.lib", "nsapi", PHP_NSAPI + ";" + PHP_NSAPI_LIBS)) {
+ SAPI('nsapi', 'nsapi.c', 'php' + PHP_VERSION + 'nsapi.dll', '/D XP_WIN32 ');
+ } else {
+ WARNING("Could not find NSAPI headers/libraries");
+ }
+}
diff --git a/sapi/nsapi/nsapi-readme.txt b/sapi/nsapi/nsapi-readme.txt
new file mode 100644
index 0000000..54980bf
--- /dev/null
+++ b/sapi/nsapi/nsapi-readme.txt
@@ -0,0 +1,154 @@
+Configuration of your Netscape/iPlanet/Sun Webserver for PHP5
+-----------------------------------------------------------------
+
+These instructions are targetted at Netscape Enterprise Web Server and
+SUN/Netscape Alliance iPlanet Web Server and the new Sun Java System Webserver.
+On other web servers your milage may vary.
+
+Firstly you may need to add some paths to the LD_LIBRARY_PATH
+environment for Netscape to find all the shared libs. This is best done
+in the start script for your Netscape server. Windows users can
+probably skip this step. The start script is located in:
+
+ <path-to-netscape-server>/https-servername/start
+
+
+Netscape/iPlanet/Sun config files are located in:
+
+ <path-to-server>/https-servername/config
+
+
+Add the following line to mime.types (you can do that by the administration server):
+
+ type=magnus-internal/x-httpd-php exts=php
+
+
+Place the following two lines after mime.types init in
+<path-to-server>/https-servername/config/obj.conf (for servers < 6) or
+for iPlanet/Sun Webserver 6.0 and above however at the end of the
+<path-to-server>/https-servername/config/magnus.conf file:
+
+ Init fn="load-modules" funcs="php5_init,php5_execute,php5_auth_trans" shlib="/path/to/phplibrary"
+ Init fn=php5_init errorString="Failed to initialize PHP!" [php_ini="/path/to/php.ini"]
+
+The "shlib" will vary depending on your OS:
+
+ Unix: "<path-to-server>/bin/libphp5.so".
+ Windows: "c:/path/to/php5/php5nsapi.dll"
+
+
+In obj.conf (for virtual server classes [Sun 6.0+] in their vserver.obj.conf):
+
+ <Object name="default">
+ .
+ .
+ .
+ # NOTE this next line should happen after all 'ObjectType' and before
+ # all 'AddLog' lines
+ # You can modify some entries in php.ini request specific by adding it to the Service
+ # directive, e.g. doc_root="/path"
+ # For boolean ini-keys please use 0/1 as value, NOT "On","Off",... (this will not work
+ # correctly), e.g. zlib.output_compression=1 instead of zlib.output_compression="On"
+
+ Service fn="php5_execute" type="magnus-internal/x-httpd-php" [inikey=value ...]
+ .
+ .
+ .
+ </Object>
+
+This is only needed if you want to configure a directory that only consists of
+PHP scripts (same like a cgi-bin directory):
+
+ <Object name="x-httpd-php">
+ ObjectType fn="force-type" type="magnus-internal/x-httpd-php"
+ Service fn="php5_execute" [inikey=value ...]
+ </Object>
+
+After that you can configure a directory in the Administration server and assign it
+the style "x-httpd-php". All files in it will get executed as PHP. This is nice to
+hide PHP usage by renaming files to .html
+
+Note: The stacksize that PHP uses depends on the configuration of the webserver. If you get
+crashes with very large PHP scripts, it is recommended to raise it with the Admin Server
+(in the section "MAGNUS EDITOR").
+
+
+Authentication configuration
+----------------------------
+
+PHP authentication cannot be used with any other authentication. ALL
+AUTHENTICATION IS PASSED TO YOUR PHP SCRIPT. To configure PHP
+Authentication for the entire server, add the following line:
+
+ <Object name="default">
+ AuthTrans fn=php5_auth_trans
+ .
+ .
+ .
+ .
+ </Object>
+
+
+To use PHP Authentication on a single directory, add the following:
+
+ <Object ppath="d:\path\to\authenticated\dir\*">
+ AuthTrans fn=php5_auth_trans
+ </Object>
+
+
+Special use for error pages or self-made directory listings
+-----------------------------------------------------------
+
+You can use PHP to generate the error pages for "404 Not Found"
+or similar. Add the following line to the object in obj.conf for
+every error page you want to overwrite:
+
+ Error fn="php5_execute" code=XXX script="/path/to/script.php" [inikey=value inikey=value...]
+
+where XXX ist the HTTP error code. Please delete any other Error
+directives which could interfere with yours.
+If you want to place a page for all errors that could exist, leave
+the "code" parameter out. Your script can get the HTTP status code
+with $_SERVER['ERROR_TYPE'].
+
+Another posibility is to generate self-made directory listings.
+Just generate a PHP script which displays a directory listing and
+replace the corresponding default Service line for
+type="magnus-internal/directory" in obj.conf with the following:
+
+ Service fn="php5_execute" type="magnus-internal/directory" script="/path/to/script.php" [inikey=value inikey=value...]
+
+For both error and directory listing pages the original URI and
+translated URI are in the variables $_SERVER['PATH_INFO'] and
+$_SERVER['PATH_TRANSLATED'].
+
+
+Note about nsapi_virtual() and subrequests
+------------------------------------------
+
+The NSAPI module now supports the nsapi_virtual() function (alias: virtual())
+to make subrequests on the webserver and insert the result in the webpage.
+The problem is, that this function uses some undocumented features from
+the NSAPI library.
+
+Under Unix this is not a problem, because the module automatically looks
+for the needed functions and uses them if available. If not, nsapi_virtual()
+is disabled.
+
+Under Windows limitations in the DLL handling need the use of a automatic
+detection of the most recent ns-httpdXX.dll file. This is tested for servers
+till version 6.1. If a newer version of the Sun server is used, the detection
+fails and nsapi_virtual() is disabled.
+
+If this is the case, try the following:
+Add the following parameter to php5_init in magnus.conf:
+
+ Init fn=php5_init ... server_lib="ns-httpdXX.dll"
+
+where XX is the correct DLL version number. To get it, look in the server-root
+for the correct DLL name. The DLL with the biggest filesize is the right one.
+
+But be warned: SUPPORT FOR nsapi_virtual() IS EXPERIMENTAL !!!
+
+
+$Id$
diff --git a/sapi/nsapi/nsapi.c b/sapi/nsapi/nsapi.c
new file mode 100644
index 0000000..66260e7
--- /dev/null
+++ b/sapi/nsapi/nsapi.c
@@ -0,0 +1,1104 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Jayakumar Muthukumarasamy <jk@kasenna.com> |
+ | Uwe Schindler <uwe@thetaphi.de> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: 1e6a680c91a69e9b9e4f91782d2582c3fa16939d $ */
+
+/*
+ * PHP includes
+ */
+#define NSAPI 1
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_variables.h"
+#include "ext/standard/info.h"
+#include "php_ini.h"
+#include "php_globals.h"
+#include "SAPI.h"
+#include "php_main.h"
+#include "php_version.h"
+#include "TSRM.h"
+#include "ext/standard/php_standard.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef RTLD_DEFAULT
+#define RTLD_DEFAULT NULL
+#endif
+
+/*
+ * If neither XP_UNIX not XP_WIN32 is defined use PHP_WIN32
+ */
+#if !defined(XP_UNIX) && !defined(XP_WIN32)
+#ifdef PHP_WIN32
+#define XP_WIN32
+#else
+#define XP_UNIX
+#endif
+#endif
+
+/*
+ * The manual define of HPUX is to fix bug #46020, nsapi.h needs this to detect HPUX
+ */
+#ifdef __hpux
+#define HPUX
+#endif
+
+/*
+ * NSAPI includes
+ */
+#include "nsapi.h"
+
+/* fix for gcc4 visibility issue */
+#ifndef PHP_WIN32
+# undef NSAPI_PUBLIC
+# define NSAPI_PUBLIC PHPAPI
+#endif
+
+#define NSLS_D struct nsapi_request_context *request_context
+#define NSLS_DC , NSLS_D
+#define NSLS_C request_context
+#define NSLS_CC , NSLS_C
+#define NSG(v) (request_context->v)
+
+/*
+ * ZTS needs to be defined for NSAPI to work
+ */
+#if !defined(ZTS)
+#error "NSAPI module needs ZTS to be defined"
+#endif
+
+/*
+ * Structure to encapsulate the NSAPI request in SAPI
+ */
+typedef struct nsapi_request_context {
+ pblock *pb;
+ Session *sn;
+ Request *rq;
+ int read_post_bytes;
+ char *path_info;
+ int fixed_script; /* 0 if script is from URI, 1 if script is from "script" parameter */
+ short http_error; /* 0 in normal mode; for errors the HTTP error code */
+} nsapi_request_context;
+
+/*
+ * Mappings between NSAPI names and environment variables. This
+ * mapping was obtained from the sample programs at the iplanet
+ * website.
+ */
+typedef struct nsapi_equiv {
+ const char *env_var;
+ const char *nsapi_eq;
+} nsapi_equiv;
+
+static nsapi_equiv nsapi_reqpb[] = {
+ { "QUERY_STRING", "query" },
+ { "REQUEST_LINE", "clf-request" },
+ { "REQUEST_METHOD", "method" },
+ { "PHP_SELF", "uri" },
+ { "SERVER_PROTOCOL", "protocol" }
+};
+static size_t nsapi_reqpb_size = sizeof(nsapi_reqpb)/sizeof(nsapi_reqpb[0]);
+
+static nsapi_equiv nsapi_vars[] = {
+ { "AUTH_TYPE", "auth-type" },
+ { "CLIENT_CERT", "auth-cert" },
+ { "REMOTE_USER", "auth-user" }
+};
+static size_t nsapi_vars_size = sizeof(nsapi_vars)/sizeof(nsapi_vars[0]);
+
+static nsapi_equiv nsapi_client[] = {
+ { "HTTPS_KEYSIZE", "keysize" },
+ { "HTTPS_SECRETSIZE", "secret-keysize" },
+ { "REMOTE_ADDR", "ip" },
+ { "REMOTE_HOST", "ip" }
+};
+static size_t nsapi_client_size = sizeof(nsapi_client)/sizeof(nsapi_client[0]);
+
+/* this parameters to "Service"/"Error" are NSAPI ones which should not be php.ini keys and are excluded */
+static char *nsapi_exclude_from_ini_entries[] = { "fn", "type", "method", "directive", "code", "reason", "script", "bucket", NULL };
+
+static void nsapi_free(void *addr)
+{
+ if (addr != NULL) {
+ FREE(addr);
+ }
+}
+
+
+/*******************/
+/* PHP module part */
+/*******************/
+
+PHP_MINIT_FUNCTION(nsapi);
+PHP_MSHUTDOWN_FUNCTION(nsapi);
+PHP_RINIT_FUNCTION(nsapi);
+PHP_RSHUTDOWN_FUNCTION(nsapi);
+PHP_MINFO_FUNCTION(nsapi);
+
+PHP_FUNCTION(nsapi_virtual);
+PHP_FUNCTION(nsapi_request_headers);
+PHP_FUNCTION(nsapi_response_headers);
+
+ZEND_BEGIN_MODULE_GLOBALS(nsapi)
+ long read_timeout;
+ZEND_END_MODULE_GLOBALS(nsapi)
+
+ZEND_DECLARE_MODULE_GLOBALS(nsapi)
+
+#define NSAPI_G(v) TSRMG(nsapi_globals_id, zend_nsapi_globals *, v)
+
+
+/* {{{ arginfo */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_nsapi_virtual, 0, 0, 1)
+ ZEND_ARG_INFO(0, uri)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_nsapi_request_headers, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_nsapi_response_headers, 0)
+ZEND_END_ARG_INFO()
+/* }}} */
+
+/* {{{ nsapi_functions[]
+ *
+ * Every user visible function must have an entry in nsapi_functions[].
+ */
+const zend_function_entry nsapi_functions[] = {
+ PHP_FE(nsapi_virtual, arginfo_nsapi_virtual) /* Make subrequest */
+ PHP_FALIAS(virtual, nsapi_virtual, arginfo_nsapi_virtual) /* compatibility */
+ PHP_FE(nsapi_request_headers, arginfo_nsapi_request_headers) /* get request headers */
+ PHP_FALIAS(getallheaders, nsapi_request_headers, arginfo_nsapi_request_headers) /* compatibility */
+ PHP_FALIAS(apache_request_headers, nsapi_request_headers, arginfo_nsapi_request_headers) /* compatibility */
+ PHP_FE(nsapi_response_headers, arginfo_nsapi_response_headers) /* get response headers */
+ PHP_FALIAS(apache_response_headers, nsapi_response_headers, arginfo_nsapi_response_headers) /* compatibility */
+ {NULL, NULL, NULL}
+};
+/* }}} */
+
+/* {{{ nsapi_module_entry
+ */
+zend_module_entry nsapi_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "nsapi",
+ nsapi_functions,
+ PHP_MINIT(nsapi),
+ PHP_MSHUTDOWN(nsapi),
+ NULL,
+ NULL,
+ PHP_MINFO(nsapi),
+ NO_VERSION_YET,
+ STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+/* {{{ PHP_INI
+ */
+PHP_INI_BEGIN()
+ STD_PHP_INI_ENTRY("nsapi.read_timeout", "60", PHP_INI_ALL, OnUpdateLong, read_timeout, zend_nsapi_globals, nsapi_globals)
+PHP_INI_END()
+/* }}} */
+
+/* newer servers hide this functions from the programmer so redefine the functions dynamically
+ thanks to Chris Elving from Sun for the function declarations */
+typedef int (*nsapi_servact_prototype)(Session *sn, Request *rq);
+nsapi_servact_prototype nsapi_servact_uri2path = NULL;
+nsapi_servact_prototype nsapi_servact_pathchecks = NULL;
+nsapi_servact_prototype nsapi_servact_fileinfo = NULL;
+nsapi_servact_prototype nsapi_servact_service = NULL;
+
+#ifdef PHP_WIN32
+/* The following dll-names for nsapi are in use at this time. The undocumented
+ * servact_* functions are always in the newest one, older ones are supported by
+ * the server only by wrapping the function table nothing else. So choose
+ * the newest one found in process space for dynamic linking */
+static char *nsapi_dlls[] = { "ns-httpd40.dll", "ns-httpd36.dll", "ns-httpd35.dll", "ns-httpd30.dll", NULL };
+/* if user specifies an other dll name by server_lib parameter
+ * it is placed in the following variable and only this DLL is
+ * checked for the servact_* functions */
+char *nsapi_dll = NULL;
+#endif
+
+/* {{{ php_nsapi_init_dynamic_symbols
+ */
+static void php_nsapi_init_dynamic_symbols(void)
+{
+ /* find address of internal NSAPI functions */
+#ifdef PHP_WIN32
+ register int i;
+ DL_HANDLE module = NULL;
+ if (nsapi_dll) {
+ /* try user specified server_lib */
+ module = GetModuleHandle(nsapi_dll);
+ if (!module) {
+ log_error(LOG_WARN, "php5_init", NULL, NULL, "Cannot find DLL specified by server_lib parameter: %s", nsapi_dll);
+ }
+ } else {
+ /* find a LOADED dll module from nsapi_dlls */
+ for (i=0; nsapi_dlls[i]; i++) {
+ if (module = GetModuleHandle(nsapi_dlls[i])) {
+ break;
+ }
+ }
+ }
+ if (!module) return;
+#else
+ DL_HANDLE module = RTLD_DEFAULT;
+#endif
+ nsapi_servact_uri2path = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_uri2path");
+ nsapi_servact_pathchecks = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_pathchecks");
+ nsapi_servact_fileinfo = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_fileinfo");
+ nsapi_servact_service = (nsapi_servact_prototype)DL_FETCH_SYMBOL(module, "INTservact_service");
+ if (!(nsapi_servact_uri2path && nsapi_servact_pathchecks && nsapi_servact_fileinfo && nsapi_servact_service)) {
+ /* not found - could be cause they are undocumented */
+ nsapi_servact_uri2path = NULL;
+ nsapi_servact_pathchecks = NULL;
+ nsapi_servact_fileinfo = NULL;
+ nsapi_servact_service = NULL;
+ }
+}
+/* }}} */
+
+/* {{{ php_nsapi_init_globals
+ */
+static void php_nsapi_init_globals(zend_nsapi_globals *nsapi_globals)
+{
+ nsapi_globals->read_timeout = 60;
+}
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+PHP_MINIT_FUNCTION(nsapi)
+{
+ php_nsapi_init_dynamic_symbols();
+ ZEND_INIT_MODULE_GLOBALS(nsapi, php_nsapi_init_globals, NULL);
+ REGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+PHP_MSHUTDOWN_FUNCTION(nsapi)
+{
+ UNREGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(nsapi)
+{
+ php_info_print_table_start();
+ php_info_print_table_row(2, "NSAPI Module Revision", "$Id: 1e6a680c91a69e9b9e4f91782d2582c3fa16939d $");
+ php_info_print_table_row(2, "Server Software", system_version());
+ php_info_print_table_row(2, "Sub-requests with nsapi_virtual()",
+ (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" );
+ php_info_print_table_end();
+
+ DISPLAY_INI_ENTRIES();
+}
+/* }}} */
+
+/* {{{ proto bool nsapi_virtual(string uri)
+ Perform an NSAPI sub-request */
+/* This function is equivalent to <!--#include virtual...-->
+ * in SSI. It does an NSAPI sub-request. It is useful
+ * for including CGI scripts or .shtml files, or anything else
+ * that you'd parse through webserver.
+ */
+PHP_FUNCTION(nsapi_virtual)
+{
+ int uri_len,rv;
+ char *uri,*value;
+ Request *rq;
+ nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &uri, &uri_len) == FAILURE) {
+ return;
+ }
+
+ if (!nsapi_servact_service) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - Sub-requests not supported on this platform", uri);
+ RETURN_FALSE;
+ } else if (zend_ini_long("zlib.output_compression", sizeof("zlib.output_compression"), 0)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - Sub-requests do not work with zlib.output_compression", uri);
+ RETURN_FALSE;
+ } else {
+ php_output_end_all(TSRMLS_C);
+ php_header(TSRMLS_C);
+
+ /* do the sub-request */
+ /* thanks to Chris Elving from Sun for this code sniplet */
+ if ((rq = request_restart_internal(uri, NULL)) == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - Internal request creation failed", uri);
+ RETURN_FALSE;
+ }
+
+ /* insert host of current request to get page from same vhost */
+ param_free(pblock_remove("host", rq->headers));
+ if (value = pblock_findval("host", rc->rq->headers)) {
+ pblock_nvinsert("host", value, rq->headers);
+ }
+
+ /* go through the normal request stages as given in obj.conf,
+ but leave out the logging/error section */
+ do {
+ rv = (*nsapi_servact_uri2path)(rc->sn, rq);
+ if (rv != REQ_PROCEED) {
+ continue;
+ }
+
+ rv = (*nsapi_servact_pathchecks)(rc->sn, rq);
+ if (rv != REQ_PROCEED) {
+ continue;
+ }
+
+ rv = (*nsapi_servact_fileinfo)(rc->sn, rq);
+ if (rv != REQ_PROCEED) {
+ continue;
+ }
+
+ rv = (*nsapi_servact_service)(rc->sn, rq);
+ } while (rv == REQ_RESTART);
+
+ if (rq->status_num != 200) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to include uri '%s' - HTTP status code %d during subrequest", uri, rq->status_num);
+ request_free(rq);
+ RETURN_FALSE;
+ }
+
+ request_free(rq);
+
+ RETURN_TRUE;
+ }
+}
+/* }}} */
+
+/* {{{ proto array nsapi_request_headers(void)
+ Get all headers from the request */
+PHP_FUNCTION(nsapi_request_headers)
+{
+ register int i;
+ struct pb_entry *entry;
+ nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ array_init(return_value);
+
+ for (i=0; i < rc->rq->headers->hsize; i++) {
+ entry=rc->rq->headers->ht[i];
+ while (entry) {
+ add_assoc_string(return_value, entry->param->name, entry->param->value, 1);
+ entry=entry->next;
+ }
+ }
+}
+/* }}} */
+
+/* {{{ proto array nsapi_response_headers(void)
+ Get all headers from the response */
+PHP_FUNCTION(nsapi_response_headers)
+{
+ register int i;
+ struct pb_entry *entry;
+ nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
+
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ array_init(return_value);
+
+ for (i=0; i < rc->rq->srvhdrs->hsize; i++) {
+ entry=rc->rq->srvhdrs->ht[i];
+ while (entry) {
+ add_assoc_string(return_value, entry->param->name, entry->param->value, 1);
+ entry=entry->next;
+ }
+ }
+}
+/* }}} */
+
+
+/*************/
+/* SAPI part */
+/*************/
+
+static int sapi_nsapi_ub_write(const char *str, unsigned int str_length TSRMLS_DC)
+{
+ int retval;
+ nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
+
+ if (!SG(headers_sent)) {
+ sapi_send_headers(TSRMLS_C);
+ }
+
+ retval = net_write(rc->sn->csd, (char *)str, str_length);
+ if (retval == IO_ERROR /* -1 */ || retval == IO_EOF /* 0 */) {
+ php_handle_aborted_connection();
+ }
+ return retval;
+}
+
+/* modified version of apache2 */
+static void sapi_nsapi_flush(void *server_context)
+{
+ nsapi_request_context *rc = (nsapi_request_context *)server_context;
+ TSRMLS_FETCH();
+
+ if (!rc) {
+ /* we have no context, so no flushing needed. This fixes a SIGSEGV on shutdown */
+ return;
+ }
+
+ if (!SG(headers_sent)) {
+ sapi_send_headers(TSRMLS_C);
+ }
+
+ /* flushing is only supported in iPlanet servers from version 6.1 on, make it conditional */
+#if NSAPI_VERSION >= 302
+ if (net_flush(rc->sn->csd) < 0) {
+ php_handle_aborted_connection();
+ }
+#endif
+}
+
+/* callback for zend_llist_apply on SAPI_HEADER_DELETE_ALL operation */
+static int php_nsapi_remove_header(sapi_header_struct *sapi_header TSRMLS_DC)
+{
+ char *header_name, *p;
+ nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
+
+ /* copy the header, because NSAPI needs reformatting and we do not want to change the parameter */
+ header_name = pool_strdup(rc->sn->pool, sapi_header->header);
+
+ /* extract name, this works, if only the header without ':' is given, too */
+ if (p = strchr(header_name, ':')) {
+ *p = 0;
+ }
+
+ /* header_name to lower case because NSAPI reformats the headers and wants lowercase */
+ for (p=header_name; *p; p++) {
+ *p=tolower(*p);
+ }
+
+ /* remove the header */
+ param_free(pblock_remove(header_name, rc->rq->srvhdrs));
+ pool_free(rc->sn->pool, header_name);
+
+ return ZEND_HASH_APPLY_KEEP;
+}
+
+static int sapi_nsapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ char *header_name, *header_content, *p;
+ nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
+
+ switch(op) {
+ case SAPI_HEADER_DELETE_ALL:
+ /* this only deletes headers set or overwritten by PHP, headers previously set by NSAPI are left intact */
+ zend_llist_apply(&sapi_headers->headers, (llist_apply_func_t) php_nsapi_remove_header TSRMLS_CC);
+ return 0;
+
+ case SAPI_HEADER_DELETE:
+ /* reuse the zend_llist_apply callback function for this, too */
+ php_nsapi_remove_header(sapi_header TSRMLS_CC);
+ return 0;
+
+ case SAPI_HEADER_ADD:
+ case SAPI_HEADER_REPLACE:
+ /* copy the header, because NSAPI needs reformatting and we do not want to change the parameter */
+ header_name = pool_strdup(rc->sn->pool, sapi_header->header);
+
+ /* split header and align pointer for content */
+ header_content = strchr(header_name, ':');
+ if (header_content) {
+ *header_content = 0;
+ do {
+ header_content++;
+ } while (*header_content==' ');
+
+ /* header_name to lower case because NSAPI reformats the headers and wants lowercase */
+ for (p=header_name; *p; p++) {
+ *p=tolower(*p);
+ }
+
+ /* if REPLACE, remove first. "Content-type" is always removed, as SAPI has a bug according to this */
+ if (op==SAPI_HEADER_REPLACE || strcmp(header_name, "content-type")==0) {
+ param_free(pblock_remove(header_name, rc->rq->srvhdrs));
+ }
+ /* ADD header to nsapi table */
+ pblock_nvinsert(header_name, header_content, rc->rq->srvhdrs);
+ }
+
+ pool_free(rc->sn->pool, header_name);
+ return SAPI_HEADER_ADD;
+
+ default:
+ return 0;
+ }
+}
+
+static int sapi_nsapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ int retval;
+ nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
+
+ if (SG(sapi_headers).send_default_content_type) {
+ char *hd;
+ param_free(pblock_remove("content-type", rc->rq->srvhdrs));
+ hd = sapi_get_default_content_type(TSRMLS_C);
+ pblock_nvinsert("content-type", hd, rc->rq->srvhdrs);
+ efree(hd);
+ }
+
+ protocol_status(rc->sn, rc->rq, SG(sapi_headers).http_response_code, NULL);
+ retval = protocol_start_response(rc->sn, rc->rq);
+
+ if (retval == REQ_PROCEED || retval == REQ_NOACTION) {
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+ } else {
+ return SAPI_HEADER_SEND_FAILED;
+ }
+}
+
+static int sapi_nsapi_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
+ char *read_ptr = buffer, *content_length_str = NULL;
+ uint bytes_read = 0;
+ int length, content_length = 0;
+ netbuf *nbuf = rc->sn->inbuf;
+
+ /*
+ * Yesss!
+ */
+ count_bytes = MIN(count_bytes, SG(request_info).content_length-rc->read_post_bytes);
+ content_length = SG(request_info).content_length;
+
+ if (content_length <= 0) {
+ return 0;
+ }
+
+ /*
+ * Gobble any pending data in the netbuf.
+ */
+ length = nbuf->cursize - nbuf->pos;
+ length = MIN(count_bytes, length);
+ if (length > 0) {
+ memcpy(read_ptr, nbuf->inbuf + nbuf->pos, length);
+ bytes_read += length;
+ read_ptr += length;
+ content_length -= length;
+ nbuf->pos += length;
+ }
+
+ /*
+ * Read the remaining from the socket.
+ */
+ while (content_length > 0 && bytes_read < count_bytes) {
+ int bytes_to_read = count_bytes - bytes_read;
+
+ if (content_length < bytes_to_read) {
+ bytes_to_read = content_length;
+ }
+
+ length = net_read(rc->sn->csd, read_ptr, bytes_to_read, NSAPI_G(read_timeout));
+
+ if (length == IO_ERROR || length == IO_EOF) {
+ break;
+ }
+
+ bytes_read += length;
+ read_ptr += length;
+ content_length -= length;
+ }
+
+ if ( bytes_read > 0 ) {
+ rc->read_post_bytes += bytes_read;
+ }
+ return bytes_read;
+}
+
+static char *sapi_nsapi_read_cookies(TSRMLS_D)
+{
+ char *cookie_string;
+ nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
+
+ cookie_string = pblock_findval("cookie", rc->rq->headers);
+ return cookie_string;
+}
+
+static void sapi_nsapi_register_server_variables(zval *track_vars_array TSRMLS_DC)
+{
+ nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
+ register size_t i;
+ int pos;
+ char *value,*p;
+ char buf[32];
+ struct pb_entry *entry;
+
+ for (i = 0; i < nsapi_reqpb_size; i++) {
+ value = pblock_findval(nsapi_reqpb[i].nsapi_eq, rc->rq->reqpb);
+ if (value) {
+ php_register_variable((char *)nsapi_reqpb[i].env_var, value, track_vars_array TSRMLS_CC);
+ }
+ }
+
+ for (i=0; i < rc->rq->headers->hsize; i++) {
+ entry=rc->rq->headers->ht[i];
+ while (entry) {
+ if (strcasecmp(entry->param->name, "content-length")==0 || strcasecmp(entry->param->name, "content-type")==0) {
+ value=estrdup(entry->param->name);
+ pos = 0;
+ } else {
+ spprintf(&value, 0, "HTTP_%s", entry->param->name);
+ pos = 5;
+ }
+ if (value) {
+ for(p = value + pos; *p; p++) {
+ *p = toupper(*p);
+ if (!isalnum(*p)) {
+ *p = '_';
+ }
+ }
+ php_register_variable(value, entry->param->value, track_vars_array TSRMLS_CC);
+ efree(value);
+ }
+ entry=entry->next;
+ }
+ }
+
+ for (i = 0; i < nsapi_vars_size; i++) {
+ value = pblock_findval(nsapi_vars[i].nsapi_eq, rc->rq->vars);
+ if (value) {
+ php_register_variable((char *)nsapi_vars[i].env_var, value, track_vars_array TSRMLS_CC);
+ }
+ }
+
+ for (i = 0; i < nsapi_client_size; i++) {
+ value = pblock_findval(nsapi_client[i].nsapi_eq, rc->sn->client);
+ if (value) {
+ php_register_variable((char *)nsapi_client[i].env_var, value, track_vars_array TSRMLS_CC);
+ }
+ }
+
+ if (value = session_dns(rc->sn)) {
+ php_register_variable("REMOTE_HOST", value, track_vars_array TSRMLS_CC);
+ nsapi_free(value);
+ }
+
+ slprintf(buf, sizeof(buf), "%d", conf_getglobals()->Vport);
+ php_register_variable("SERVER_PORT", buf, track_vars_array TSRMLS_CC);
+ php_register_variable("SERVER_NAME", conf_getglobals()->Vserver_hostname, track_vars_array TSRMLS_CC);
+
+ value = http_uri2url_dynamic("", "", rc->sn, rc->rq);
+ php_register_variable("SERVER_URL", value, track_vars_array TSRMLS_CC);
+ nsapi_free(value);
+
+ php_register_variable("SERVER_SOFTWARE", system_version(), track_vars_array TSRMLS_CC);
+ if (security_active) {
+ php_register_variable("HTTPS", "ON", track_vars_array TSRMLS_CC);
+ }
+ php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC);
+
+ /* DOCUMENT_ROOT */
+ if (value = request_translate_uri("/", rc->sn)) {
+ pos = strlen(value);
+ php_register_variable_safe("DOCUMENT_ROOT", value, pos-1, track_vars_array TSRMLS_CC);
+ nsapi_free(value);
+ }
+
+ /* PATH_INFO / PATH_TRANSLATED */
+ if (rc->path_info) {
+ if (value = request_translate_uri(rc->path_info, rc->sn)) {
+ php_register_variable("PATH_TRANSLATED", value, track_vars_array TSRMLS_CC);
+ nsapi_free(value);
+ }
+ php_register_variable("PATH_INFO", rc->path_info, track_vars_array TSRMLS_CC);
+ }
+
+ /* Create full Request-URI & Script-Name */
+ if (SG(request_info).request_uri) {
+ pos = strlen(SG(request_info).request_uri);
+
+ if (SG(request_info).query_string) {
+ spprintf(&value, 0, "%s?%s", SG(request_info).request_uri, SG(request_info).query_string);
+ if (value) {
+ php_register_variable("REQUEST_URI", value, track_vars_array TSRMLS_CC);
+ efree(value);
+ }
+ } else {
+ php_register_variable_safe("REQUEST_URI", SG(request_info).request_uri, pos, track_vars_array TSRMLS_CC);
+ }
+
+ if (rc->path_info) {
+ pos -= strlen(rc->path_info);
+ if (pos<0) {
+ pos = 0;
+ }
+ }
+ php_register_variable_safe("SCRIPT_NAME", SG(request_info).request_uri, pos, track_vars_array TSRMLS_CC);
+ }
+ php_register_variable("SCRIPT_FILENAME", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
+
+ /* special variables in error mode */
+ if (rc->http_error) {
+ slprintf(buf, sizeof(buf), "%d", rc->http_error);
+ php_register_variable("ERROR_TYPE", buf, track_vars_array TSRMLS_CC);
+ }
+}
+
+static void nsapi_log_message(char *message TSRMLS_DC)
+{
+ nsapi_request_context *rc = (nsapi_request_context *)SG(server_context);
+
+ if (rc) {
+ log_error(LOG_INFORM, pblock_findval("fn", rc->pb), rc->sn, rc->rq, "%s", message);
+ } else {
+ log_error(LOG_INFORM, "php5", NULL, NULL, "%s", message);
+ }
+}
+
+static double sapi_nsapi_get_request_time(TSRMLS_D)
+{
+ return REQ_TIME( ((nsapi_request_context *)SG(server_context))->rq );
+}
+
+static int php_nsapi_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &nsapi_module_entry, 1)==FAILURE) {
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static struct stat* sapi_nsapi_get_stat(TSRMLS_D)
+{
+ return request_stat_path(
+ SG(request_info).path_translated,
+ ((nsapi_request_context *)SG(server_context))->rq
+ );
+}
+
+static sapi_module_struct nsapi_sapi_module = {
+ "nsapi", /* name */
+ "NSAPI", /* pretty name */
+
+ php_nsapi_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ sapi_nsapi_ub_write, /* unbuffered write */
+ sapi_nsapi_flush, /* flush */
+ sapi_nsapi_get_stat, /* get uid/stat */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ sapi_nsapi_header_handler, /* header handler */
+ sapi_nsapi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ sapi_nsapi_read_post, /* read POST data */
+ sapi_nsapi_read_cookies, /* read Cookies */
+
+ sapi_nsapi_register_server_variables, /* register server variables */
+ nsapi_log_message, /* Log message */
+ sapi_nsapi_get_request_time, /* Get request time */
+ NULL, /* Child terminate */
+
+ NULL, /* Block interruptions */
+ NULL, /* Unblock interruptions */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+static void nsapi_php_ini_entries(NSLS_D TSRMLS_DC)
+{
+ struct pb_entry *entry;
+ register int i,j,ok;
+
+ for (i=0; i < NSG(pb)->hsize; i++) {
+ entry=NSG(pb)->ht[i];
+ while (entry) {
+ /* exclude standard entries given to "Service" which should not go into ini entries */
+ ok=1;
+ for (j=0; nsapi_exclude_from_ini_entries[j]; j++) {
+ ok&=(strcasecmp(entry->param->name, nsapi_exclude_from_ini_entries[j])!=0);
+ }
+
+ if (ok) {
+ /* change the ini entry */
+ if (zend_alter_ini_entry(entry->param->name, strlen(entry->param->name)+1,
+ entry->param->value, strlen(entry->param->value),
+ PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE)==FAILURE) {
+ 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);
+ }
+ }
+ entry=entry->next;
+ }
+ }
+}
+
+void NSAPI_PUBLIC php5_close(void *vparam)
+{
+ if (nsapi_sapi_module.shutdown) {
+ nsapi_sapi_module.shutdown(&nsapi_sapi_module);
+ }
+
+ if (nsapi_sapi_module.php_ini_path_override) {
+ free(nsapi_sapi_module.php_ini_path_override);
+ }
+
+#ifdef PHP_WIN32
+ if (nsapi_dll) {
+ free(nsapi_dll);
+ nsapi_dll = NULL;
+ }
+#endif
+
+ sapi_shutdown();
+ tsrm_shutdown();
+
+ log_error(LOG_INFORM, "php5_close", NULL, NULL, "Shutdown PHP Module");
+}
+
+/*********************************************************
+/ init SAF
+/
+/ Init fn="php5_init" [php_ini="/path/to/php.ini"] [server_lib="ns-httpdXX.dll"]
+/ Initialize the NSAPI module in magnus.conf
+/
+/ php_ini: gives path to php.ini file
+/ server_lib: (only Win32) gives name of DLL (without path) to look for
+/ servact_* functions
+/
+/*********************************************************/
+int NSAPI_PUBLIC php5_init(pblock *pb, Session *sn, Request *rq)
+{
+ php_core_globals *core_globals;
+ char *strval;
+ int threads=128; /* default for server */
+
+ /* fetch max threads from NSAPI and initialize TSRM with it */
+ threads=conf_getglobals()->Vpool_maxthreads;
+ if (threads<1) {
+ threads=128; /* default for server */
+ }
+ tsrm_startup(threads, 1, 0, NULL);
+
+ core_globals = ts_resource(core_globals_id);
+
+ /* look if php_ini parameter is given to php5_init */
+ if (strval = pblock_findval("php_ini", pb)) {
+ nsapi_sapi_module.php_ini_path_override = strdup(strval);
+ }
+
+#ifdef PHP_WIN32
+ /* look if server_lib parameter is given to php5_init
+ * (this disables the automatic search for the newest ns-httpdXX.dll) */
+ if (strval = pblock_findval("server_lib", pb)) {
+ nsapi_dll = strdup(strval);
+ }
+#endif
+
+ /* start SAPI */
+ sapi_startup(&nsapi_sapi_module);
+ nsapi_sapi_module.startup(&nsapi_sapi_module);
+
+ daemon_atrestart(&php5_close, NULL);
+
+ log_error(LOG_INFORM, pblock_findval("fn", pb), sn, rq, "Initialized PHP Module (%d threads expected)", threads);
+ return REQ_PROCEED;
+}
+
+/*********************************************************
+/ normal use in Service directive:
+/
+/ Service fn="php5_execute" type=... method=... [inikey=inivalue inikey=inivalue...]
+/
+/ use in Service for a directory to supply a php-made directory listing instead of server default:
+/
+/ Service fn="php5_execute" type="magnus-internal/directory" script="/path/to/script.php" [inikey=inivalue inikey=inivalue...]
+/
+/ use in Error SAF to display php script as error page:
+/
+/ Error fn="php5_execute" code=XXX script="/path/to/script.php" [inikey=inivalue inikey=inivalue...]
+/ Error fn="php5_execute" reason="Reason" script="/path/to/script.php" [inikey=inivalue inikey=inivalue...]
+/
+/*********************************************************/
+int NSAPI_PUBLIC php5_execute(pblock *pb, Session *sn, Request *rq)
+{
+ int retval;
+ nsapi_request_context *request_context;
+ zend_file_handle file_handle = {0};
+ struct stat *fst;
+
+ char *path_info;
+ char *query_string = pblock_findval("query", rq->reqpb);
+ char *uri = pblock_findval("uri", rq->reqpb);
+ char *request_method = pblock_findval("method", rq->reqpb);
+ char *content_type = pblock_findval("content-type", rq->headers);
+ char *content_length = pblock_findval("content-length", rq->headers);
+ char *directive = pblock_findval("Directive", pb);
+ int error_directive = (directive && !strcasecmp(directive, "error"));
+ int fixed_script = 1;
+
+ /* try to use script parameter -> Error or Service for directory listing */
+ char *path_translated = pblock_findval("script", pb);
+
+ TSRMLS_FETCH();
+
+ /* if script parameter is missing: normal use as Service SAF */
+ if (!path_translated) {
+ path_translated = pblock_findval("path", rq->vars);
+ path_info = pblock_findval("path-info", rq->vars);
+ fixed_script = 0;
+ if (error_directive) {
+ /* go to next error directive if script parameter is missing */
+ log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Missing 'script' parameter");
+ return REQ_NOACTION;
+ }
+ } else {
+ /* in error the path_info is the uri to the requested page */
+ path_info = pblock_findval("uri", rq->reqpb);
+ }
+
+ /* check if this uri was included in an other PHP script with nsapi_virtual()
+ by looking for a request context in the current thread */
+ if (SG(server_context)) {
+ /* send 500 internal server error */
+ log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Cannot make nesting PHP requests with nsapi_virtual()");
+ if (error_directive) {
+ return REQ_NOACTION;
+ } else {
+ protocol_status(sn, rq, 500, NULL);
+ return REQ_ABORTED;
+ }
+ }
+
+ request_context = (nsapi_request_context *)pool_malloc(sn->pool, sizeof(nsapi_request_context));
+ if (!request_context) {
+ log_error(LOG_CATASTROPHE, pblock_findval("fn", pb), sn, rq, "Insufficient memory to process PHP request!");
+ return REQ_ABORTED;
+ }
+ request_context->pb = pb;
+ request_context->sn = sn;
+ request_context->rq = rq;
+ request_context->read_post_bytes = 0;
+ request_context->fixed_script = fixed_script;
+ request_context->http_error = (error_directive) ? rq->status_num : 0;
+ request_context->path_info = path_info;
+
+ SG(server_context) = request_context;
+ SG(request_info).query_string = query_string;
+ SG(request_info).request_uri = uri;
+ SG(request_info).request_method = request_method;
+ SG(request_info).path_translated = path_translated;
+ SG(request_info).content_type = content_type;
+ SG(request_info).content_length = (content_length == NULL) ? 0 : strtoul(content_length, 0, 0);
+ SG(sapi_headers).http_response_code = (error_directive) ? rq->status_num : 200;
+
+ nsapi_php_ini_entries(NSLS_C TSRMLS_CC);
+
+ php_handle_auth_data(pblock_findval("authorization", rq->headers) TSRMLS_CC);
+
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.free_filename = 0;
+ file_handle.opened_path = NULL;
+
+ fst = request_stat_path(SG(request_info).path_translated, rq);
+ if (fst && S_ISREG(fst->st_mode)) {
+ if (php_request_startup(TSRMLS_C) == SUCCESS) {
+ php_execute_script(&file_handle TSRMLS_CC);
+ php_request_shutdown(NULL);
+ retval=REQ_PROCEED;
+ } else {
+ /* send 500 internal server error */
+ log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Cannot prepare PHP engine!");
+ if (error_directive) {
+ retval=REQ_NOACTION;
+ } else {
+ protocol_status(sn, rq, 500, NULL);
+ retval=REQ_ABORTED;
+ }
+ }
+ } else {
+ /* send 404 because file not found */
+ log_error(LOG_WARN, pblock_findval("fn", pb), sn, rq, "Cannot execute PHP script: %s (File not found)", SG(request_info).path_translated);
+ if (error_directive) {
+ retval=REQ_NOACTION;
+ } else {
+ protocol_status(sn, rq, 404, NULL);
+ retval=REQ_ABORTED;
+ }
+ }
+
+ pool_free(sn->pool, request_context);
+ SG(server_context) = NULL;
+
+ return retval;
+}
+
+/*********************************************************
+/ authentication
+/
+/ we have to make a 'fake' authenticator for netscape so it
+/ will pass authentication through to php, and allow us to
+/ check authentication with our scripts.
+/
+/ php5_auth_trans
+/ main function called from netscape server to authenticate
+/ a line in obj.conf:
+/ funcs=php5_auth_trans shlib="path/to/this/phpnsapi.dll"
+/ and:
+/ <Object ppath="path/to/be/authenticated/by/php/*">
+/ AuthTrans fn="php5_auth_trans"
+/*********************************************************/
+int NSAPI_PUBLIC php5_auth_trans(pblock * pb, Session * sn, Request * rq)
+{
+ /* This is a DO NOTHING function that allows authentication
+ * information
+ * to be passed through to PHP scripts.
+ */
+ return REQ_PROCEED;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/nsapi/php5nsapi.dsp b/sapi/nsapi/php5nsapi.dsp
new file mode 100644
index 0000000..6cd0079
--- /dev/null
+++ b/sapi/nsapi/php5nsapi.dsp
@@ -0,0 +1,135 @@
+# Microsoft Developer Studio Project File - Name="php5nsapi" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=php5nsapi - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "php5nsapi.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "php5nsapi.mak" CFG="php5nsapi - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "php5nsapi - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5nsapi - Win32 Release_TS_inline" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5nsapi - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "php5nsapi - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "php5nsapi___Win32_Release_TS"
+# PROP BASE Intermediate_Dir "php5nsapi___Win32_Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "php5nsapi_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /I "..\..\..\php_build\nsapi30\include\\" /I "..\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\main" /I "..\..\tsrm" /D ZEND_DEBUG=0 /D "NDEBUG" /D "XP_WIN32" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "php5nsapi_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 ns-httpd30.lib php5ts.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x62000000" /version:4.0 /dll /machine:I386 /libpath:"..\..\..\php_build\nsapi30\lib\\" /libpath:"..\..\Release_TS" /libpath:"..\..\TSRM\Release_TS" /libpath:"..\..\Zend\Release_TS"
+
+!ELSEIF "$(CFG)" == "php5nsapi - Win32 Release_TS_inline"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "php5nsapi___Win32_Release_TS_inline"
+# PROP BASE Intermediate_Dir "php5nsapi___Win32_Release_TS_inline"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS_inline"
+# PROP Intermediate_Dir "Release_TS_inline"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "php5nsapi_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /I "..\..\..\php_build\nsapi30\include\\" /I "..\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\main" /I "..\..\tsrm" /D ZEND_DEBUG=0 /D "ZEND_WIN32_FORCE_INLINE" /D "NDEBUG" /D "XP_WIN32" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "php5nsapi_EXPORTS" /D "WIN32" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 ns-httpd30.lib php5ts.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x62000000" /version:4.0 /dll /machine:I386 /libpath:"..\..\..\php_build\nsapi30\lib\\" /libpath:"..\..\Release_TS_inline" /libpath:"..\..\TSRM\Release_TS_inline" /libpath:"..\..\Zend\Release_TS_inline"
+
+!ELSEIF "$(CFG)" == "php5nsapi - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "php5nsapi___Win32_Debug_TS"
+# PROP BASE Intermediate_Dir "php5nsapi___Win32_Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\..\Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "php5nsapi_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "." /I "..\..\..\php_build\nsapi30\include\\" /I "..\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\..\bindlib_w32" /I "..\..\main" /I "..\..\tsrm" /D "_Debug_TS" /D ZEND_DEBUG=1 /D "_DEBUG" /D "XP_WIN32" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "_WINDOWS" /D "_USRDLL" /D "php5nsapi_EXPORTS" /D "WIN32" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 ns-httpd30.lib php5ts_debug.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x62000000" /version:4.0 /dll /debug /machine:I386 /pdbtype:sept /libpath:"..\..\..\php_build\nsapi30\lib\\" /libpath:"..\..\Debug_TS" /libpath:"..\..\TSRM\Debug_TS" /libpath:"..\..\Zend\Debug_TS"
+
+!ENDIF
+
+# Begin Target
+
+# Name "php5nsapi - Win32 Release_TS"
+# Name "php5nsapi - Win32 Release_TS_inline"
+# Name "php5nsapi - Win32 Debug_TS"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\nsapi.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/sapi/phttpd/CREDITS b/sapi/phttpd/CREDITS
new file mode 100644
index 0000000..134cc54
--- /dev/null
+++ b/sapi/phttpd/CREDITS
@@ -0,0 +1,2 @@
+phttpd
+Thies C. Arntzen
diff --git a/sapi/phttpd/README b/sapi/phttpd/README
new file mode 100644
index 0000000..cdb6f7c
--- /dev/null
+++ b/sapi/phttpd/README
@@ -0,0 +1,5 @@
+phttpd sapi module.
+
+THIS IS BY NO MEANS COMPLETE NOR USABLE RIGHT NOW!
+
+thies@thieso.net 03.01.2000
diff --git a/sapi/phttpd/config.m4 b/sapi/phttpd/config.m4
new file mode 100644
index 0000000..91339a5
--- /dev/null
+++ b/sapi/phttpd/config.m4
@@ -0,0 +1,21 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(phttpd, for PHTTPD support,
+[ --with-phttpd=DIR Build PHP as phttpd module], no, no)
+
+if test "$PHP_PHTTPD" != "no"; then
+ if test ! -d $PHP_PHTTPD ; then
+ AC_MSG_ERROR([You did not specify a directory])
+ fi
+ PHP_BUILD_THREAD_SAFE
+ PHP_ADD_INCLUDE($PHP_PHTTPD/include)
+ AC_DEFINE(HAVE_PHTTPD, 1, [Whether you have phttpd])
+ PHP_SELECT_SAPI(phttpd, shared, phttpd.c)
+ INSTALL_IT="\$(INSTALL) -m 0755 $SAPI_SHARED \$(INSTALL_ROOT)$PHP_PHTTPD/modules/"
+fi
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/phttpd/php.sym b/sapi/phttpd/php.sym
new file mode 100644
index 0000000..f10b883
--- /dev/null
+++ b/sapi/phttpd/php.sym
@@ -0,0 +1,4 @@
+pm_init
+pm_exit
+pm_request
+
diff --git a/sapi/phttpd/php_phttpd.h b/sapi/phttpd/php_phttpd.h
new file mode 100644
index 0000000..49089cf
--- /dev/null
+++ b/sapi/phttpd/php_phttpd.h
@@ -0,0 +1,24 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Thies C. Arntzen <thies@thieso.net> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_PHTTPD_H
+#define PHP_PHTTPD_H
+
+#include <phttpd.h>
+
+#endif
diff --git a/sapi/phttpd/phttpd.c b/sapi/phttpd/phttpd.c
new file mode 100644
index 0000000..c2e9172
--- /dev/null
+++ b/sapi/phttpd/phttpd.c
@@ -0,0 +1,301 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Thies C. Arntzen <thies@thieso.net> |
+ | Based on aolserver SAPI by Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "SAPI.h"
+#include "php_main.h"
+
+#ifdef HAVE_PHTTPD
+
+#include "ext/standard/info.h"
+
+#ifndef ZTS
+#error PHTTPD module is only useable in thread-safe mode
+#endif
+
+#include "php_phttpd.h"
+
+typedef struct {
+ struct connectioninfo *cip;
+ struct stat sb;
+} phttpd_globals_struct;
+
+static int ph_globals_id;
+
+#define PHG(v) TSRMG(ph_globals_id, phttpd_globals_struct *, v)
+
+static int
+php_phttpd_startup(sapi_module_struct *sapi_module)
+{
+ fprintf(stderr,"***php_phttpd_startup\n");
+
+ if (php_module_startup(sapi_module, NULL, 0)) {
+ return FAILURE;
+ } else {
+ return SUCCESS;
+ }
+}
+
+static int
+php_phttpd_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ int sent_bytes;
+
+ sent_bytes = fd_write(PHG(cip)->fd, str, str_length);
+
+ if (sent_bytes == -1) {
+ php_handle_aborted_connection();
+ }
+
+ return sent_bytes;
+}
+
+static int
+php_phttpd_sapi_header_handler(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ char *header_name, *header_content;
+ char *p;
+
+ http_sendheaders(PHG(cip)->fd, PHG(cip), SG(sapi_headers).http_response_code, NULL);
+
+ header_name = sapi_header->header;
+ header_content = p = strchr(header_name, ':');
+
+ if (p) {
+ *p = '\0';
+ do {
+ header_content++;
+ } while (*header_content == ' ');
+
+ fd_printf(PHG(cip)->fd,"%s: %s\n", header_name, header_content);
+
+ *p = ':';
+ }
+
+ sapi_free_header(sapi_header);
+
+ return 0;
+}
+
+static int
+php_phttpd_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ if (SG(sapi_headers).send_default_content_type) {
+ fd_printf(PHG(cip)->fd,"Content-Type: text/html\n");
+ }
+
+ fd_putc('\n', PHG(cip)->fd);
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+static char *
+php_phttpd_sapi_read_cookies(TSRMLS_D)
+{
+
+/*
+ int i;
+ char *http_cookie = NULL;
+
+ i = Ns_SetIFind(NSG(conn->headers), "cookie");
+ if(i != -1) {
+ http_cookie = Ns_SetValue(NSG(conn->headers), i);
+ }
+
+ return http_cookie;
+*/
+ fprintf(stderr,"***php_phttpd_sapi_read_cookies\n");
+
+ return 0;
+}
+
+static int
+php_phttpd_sapi_read_post(char *buf, uint count_bytes TSRMLS_DC)
+{
+/*
+ uint max_read;
+ uint total_read = 0;
+
+ max_read = MIN(NSG(data_avail), count_bytes);
+
+ total_read = Ns_ConnRead(NSG(conn), buf, max_read);
+
+ if(total_read == NS_ERROR) {
+ total_read = -1;
+ } else {
+ NSG(data_avail) -= total_read;
+ }
+
+ return total_read;
+*/
+ fprintf(stderr,"***php_phttpd_sapi_read_post\n");
+ return 0;
+}
+
+static sapi_module_struct phttpd_sapi_module = {
+ "phttpd",
+ "PHTTPD",
+
+ php_phttpd_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ php_phttpd_sapi_ub_write, /* unbuffered write */
+ NULL, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ php_phttpd_sapi_header_handler, /* header handler */
+ php_phttpd_sapi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+
+ php_phttpd_sapi_read_post, /* read POST data */
+ php_phttpd_sapi_read_cookies, /* read Cookies */
+
+ NULL, /* register server variables */
+ NULL, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+static void
+php_phttpd_request_ctor(TSRMLS_D TSRMLS_DC)
+{
+ memset(&SG(request_info), 0, sizeof(sapi_globals_struct)); /* pfusch! */
+
+ SG(request_info).query_string = PHG(cip)->hip->request;
+ SG(request_info).request_method = PHG(cip)->hip->method;
+ SG(request_info).path_translated = malloc(MAXPATHLEN);
+ SG(sapi_headers).http_response_code = 200;
+ if (url_expand(PHG(cip)->hip->url, SG(request_info).path_translated, MAXPATHLEN, &PHG(sb), NULL, NULL) == NULL) {
+ /* handle error */
+ }
+
+#if 0
+ char *server;
+ Ns_DString ds;
+ char *root;
+ int index;
+ char *tmp;
+
+ server = Ns_ConnServer(NSG(conn));
+
+ Ns_DStringInit(&ds);
+ Ns_UrlToFile(&ds, server, NSG(conn->request->url));
+
+ /* path_translated is the absolute path to the file */
+ SG(request_info).path_translated = strdup(Ns_DStringValue(&ds));
+ Ns_DStringFree(&ds);
+ root = Ns_PageRoot(server);
+ SG(request_info).request_uri = SG(request_info).path_translated + strlen(root);
+ SG(request_info).content_length = Ns_ConnContentLength(NSG(conn));
+ index = Ns_SetIFind(NSG(conn)->headers, "content-type");
+ SG(request_info).content_type = index == -1 ? NULL :
+ Ns_SetValue(NSG(conn)->headers, index);
+
+ tmp = Ns_ConnAuthUser(NSG(conn));
+ if(tmp) {
+ tmp = estrdup(tmp);
+ }
+ SG(request_info).auth_user = tmp;
+
+ tmp = Ns_ConnAuthPasswd(NSG(conn));
+ if(tmp) {
+ tmp = estrdup(tmp);
+ }
+ SG(request_info).auth_password = tmp;
+
+ NSG(data_avail) = SG(request_info).content_length;
+#endif
+}
+
+static void
+php_phttpd_request_dtor(TSRMLS_D TSRMLS_DC)
+{
+ free(SG(request_info).path_translated);
+}
+
+
+int php_doit(TSRMLS_D)
+{
+ struct stat sb;
+ zend_file_handle file_handle;
+ struct httpinfo *hip = PHG(cip)->hip;
+
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ return -1;
+ }
+
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.free_filename = 0;
+
+/*
+ php_phttpd_hash_environment(TSRMLS_C);
+*/
+ php_execute_script(&file_handle TSRMLS_CC);
+ php_request_shutdown(NULL);
+
+ return SG(sapi_headers).http_response_code;
+}
+
+int pm_init(const char **argv)
+{
+ tsrm_startup(1, 1, 0, NULL);
+ sapi_startup(&phttpd_sapi_module);
+ phttpd_sapi_module.startup(&phttpd_sapi_module);
+
+ ts_allocate_id(&ph_globals_id, sizeof(phttpd_globals_struct), NULL, NULL);
+
+ return 0;
+}
+
+void pm_exit(void)
+{
+ fprintf(stderr,"***pm_exit\n");
+}
+
+int pm_request(struct connectioninfo *cip)
+{
+ struct httpinfo *hip = cip->hip;
+ int status;
+ TSRMLS_FETCH();
+
+ if (strcasecmp(hip->method, "GET") == 0 ||
+ strcasecmp(hip->method, "HEAD") == 0 ||
+ strcasecmp(hip->method, "POST") == 0) {
+ PHG(cip) = cip;
+
+ php_phttpd_request_ctor(TSRMLS_C);
+ status = php_doit(TSRMLS_C);
+ php_phttpd_request_dtor(TSRMLS_C);
+
+ return status;
+ } else {
+ return -2;
+ }
+}
+
+#endif
diff --git a/sapi/pi3web/CREDITS b/sapi/pi3web/CREDITS
new file mode 100644
index 0000000..c4541f8
--- /dev/null
+++ b/sapi/pi3web/CREDITS
@@ -0,0 +1,2 @@
+pi3web
+Holger Zimmermann
diff --git a/sapi/pi3web/README b/sapi/pi3web/README
new file mode 100644
index 0000000..e3e523e
--- /dev/null
+++ b/sapi/pi3web/README
@@ -0,0 +1,50 @@
+PHP5 Module
+==========
+This module requires PHP5 as thread safe shared library. Have a look
+into the INSTALL file which accompanies that distribution.
+
+If you distribute this software bundled with the PHP software in source
+or binary form, then you must adhere to the PHP copyright conditions -
+the terms are reasonable.
+
+You should have checked out and built the PHP5 source package from the
+PHP CVS tree into the Pi3Web source directory called 'PHP5' first. Then
+build PHP5 as Pi3Web module and after that build the Pi3Web PHP5 wrapper:
+
+1. Checkout PHP5
+================
+cvs -d :pserver:cvsread@cvs.php.net:/repository login
+The required password is phpfi
+
+cvs -z3 -d :pserver:cvsread@cvs.php.net:/repository co php5
+
+You must also checkout the TSRM and the ZEND module from the ZEND cvs tree
+into the PHP5 root directory
+
+cvs -d :pserver:cvsread@cvs.zend.com:/repository login
+The required password is zend
+
+cvs -z3 -d :pserver:cvsread@cvs.zend.com:/repository co Zend TSRM
+
+2. Build PHP5
+=============
+2.1 POSIX
+---------
+cd ./php5
+./buildconf
+./configure --with-pi3web
+make
+
+2.2 Win32
+---------
+other required downloads from the php website
+ - bison 1.25
+ - bindlib32
+ - number4.tar.gz
+nmake php5dllts.mak
+
+3. Build Pi3Web PHP5 wrapper
+============================
+Run make in the Pi3Web /Source/PHP5 directory.
+
+For further information refer to http://www.php.net/version4/
diff --git a/sapi/pi3web/config.m4 b/sapi/pi3web/config.m4
new file mode 100644
index 0000000..7859481
--- /dev/null
+++ b/sapi/pi3web/config.m4
@@ -0,0 +1,27 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(pi3web, for Pi3Web support,
+[ --with-pi3web[=DIR] Build PHP as Pi3Web module], no, no)
+
+if test "$PHP_PI3WEB" != "no"; then
+ if test "$PHP_PI3WEB" = "yes"; then
+ PI3PATH=../.. # the default
+ else
+ PI3PATH=$PHP_PI3WEB
+ fi
+ test -f "$PI3PATH/PiAPI/PiAPI.h" || AC_MSG_ERROR([Unable to find PiAPI.h in $PI3PATH/PiAPI])
+ PHP_BUILD_THREAD_SAFE
+ AC_DEFINE(WITH_PI3WEB, 1, [whether you want Pi3Web support])
+ PHP_ADD_INCLUDE($PI3PATH/PiAPI)
+ PHP_ADD_INCLUDE($PI3PATH/Pi2API)
+ PHP_ADD_INCLUDE($PI3PATH/Pi3API)
+ PHP_ADD_INCLUDE($PI3PATH/PHP5)
+ PHP_SELECT_SAPI(pi3web, shared, pi3web_sapi.c)
+ INSTALL_IT="\$(SHELL) \$(srcdir)/install-sh -m 0755 $SAPI_SHARED \$(INSTALL_ROOT)$PI3PATH/bin/"
+fi
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/pi3web/config.w32 b/sapi/pi3web/config.w32
new file mode 100644
index 0000000..a5393e6
--- /dev/null
+++ b/sapi/pi3web/config.w32
@@ -0,0 +1,16 @@
+// vim:ft=javascript
+// $Id$
+
+ARG_WITH('pi3web', 'Pi3Web', 'no');
+
+if (PHP_PI3WEB != "no") {
+ if (CHECK_HEADER_ADD_INCLUDE('PiAPI.h', 'CFLAGS_PI3WEB', PHP_PHP_BUILD + "\\Pi3Web\\include;" + PHP_PI3WEB) &&
+ CHECK_LIB('piapi.lib', 'pi3web', PHP_PHP_BUILD + "\\Pi3Web\\lib;" + PHP_PI3WEB) &&
+ CHECK_LIB('pi2api.lib', 'pi3web', PHP_PHP_BUILD + "\\Pi3Web\\lib;" + PHP_PI3WEB) &&
+ CHECK_LIB('pi3api.lib', 'pi3web', PHP_PHP_BUILD + "\\Pi3Web\\lib;" + PHP_PI3WEB)) {
+ SAPI('pi3web', 'pi3web_sapi.c', 'php' + PHP_VERSION + 'pi3web.dll', '/D PHP5PI3WEB_EXPORTS');
+ AC_DEFINE('WITH_PI3WEB', 1);
+ } else {
+ WARNING('Pi3Web not enabled; headers/libraries not found');
+ }
+}
diff --git a/sapi/pi3web/php.sym b/sapi/pi3web/php.sym
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sapi/pi3web/php.sym
diff --git a/sapi/pi3web/php5pi3web.dsp b/sapi/pi3web/php5pi3web.dsp
new file mode 100644
index 0000000..bb5a248
--- /dev/null
+++ b/sapi/pi3web/php5pi3web.dsp
@@ -0,0 +1,136 @@
+# Microsoft Developer Studio Project File - Name="php5pi3web" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=php5pi3web - Win32 Debug_TS
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "php5pi3web.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "php5pi3web.mak" CFG="php5pi3web - Win32 Debug_TS"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "php5pi3web - Win32 Debug_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5pi3web - Win32 Release_TS" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "php5pi3web - Win32 Release_TS_inline" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "php5pi3web - Win32 Debug_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_TS"
+# PROP BASE Intermediate_Dir "Debug_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "..\..\Debug_TS"
+# PROP Intermediate_Dir "Debug_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "php5pi3web_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /GX /ZI /Od /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /I "..\..\..\..\PIAPI" /I "..\..\..\..\PI2API" /I "..\..\..\..\PI3API" /D "_DEBUG" /D ZEND_DEBUG=1 /D "_WINDOWS" /D "_USRDLL" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /D "PHP5PI3WEB_EXPORTS" /FR /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "_DEBUG"
+# ADD RSC /l 0x40d /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 php5ts_debug.lib kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib PiAPI.lib Pi2API.lib Pi3API.lib /nologo /version:4.0 /dll /debug /machine:I386 /nodefaultlib:"libcmt" /nodefaultlib:"libc" /pdbtype:sept /libpath:"..\..\Debug_TS" /libpath:"..\..\..\..\PIAPI" /libpath:"..\..\..\..\PI2API" /libpath:"..\..\..\..\PI3API"
+
+!ELSEIF "$(CFG)" == "php5pi3web - Win32 Release_TS"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_TS"
+# PROP BASE Intermediate_Dir "Release_TS"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS"
+# PROP Intermediate_Dir "Release_TS"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "php5pi3web_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /I "..\..\..\..\PIAPI" /I "..\..\..\..\PI2API" /I "..\..\..\..\PI3API" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /D "PHP5PI3WEB_EXPORTS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x40d /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 php5ts.lib kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib PiAPI.lib Pi2API.lib Pi3API.lib /nologo /version:4.0 /dll /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcmt.lib" /libpath:"..\..\Release_TS" /libpath:"..\..\..\..\PIAPI" /libpath:"..\..\..\..\PI2API" /libpath:"..\..\..\..\PI3API"
+
+!ELSEIF "$(CFG)" == "php5pi3web - Win32 Release_TS_inline"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "php5pi3web___Win32_Release_TS_inline"
+# PROP BASE Intermediate_Dir "php5pi3web___Win32_Release_TS_inline"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "..\..\Release_TS_inline"
+# PROP Intermediate_Dir "Release_TS_inline"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /GX /O2 /I ".." /I "..\main" /I "..\regex" /I "..\..\bindlib_w32" /I "..\Zend" /I "..\TSRM" /I "..\ext\mysql\libmysql" /I "..\..\..\PiAPI" /I "..\..\..\Pi2API" /I "..\..\..\Pi3API" /D "NDEBUG" /D ZEND_DEBUG=0 /D "_WINDOWS" /D "_USRDLL" /D "PHP5DLLTS_EXPORTS" /D "PHP_EXPORTS" /D "LIBZEND_EXPORTS" /D "TSRM_EXPORTS" /D "SAPI_EXPORTS" /D "MSVC5" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /FR /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "...\..\include" /I "..\..\win32" /I "..\..\Zend" /I "..\.." /I "..\..\main" /I "..\..\TSRM" /I "..\..\..\..\PIAPI" /I "..\..\..\..\PI2API" /I "..\..\..\..\PI3API" /D "NDEBUG" /D ZEND_DEBUG=0 /D "ZEND_WIN32_FORCE_INLINE" /D "_WINDOWS" /D "_USRDLL" /D "ZTS" /D "ZEND_WIN32" /D "PHP_WIN32" /D "WIN32" /D "_MBCS" /D "PHP5PI3WEB_EXPORTS" /FR /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ZendTS.lib TSRM.lib resolv.lib libmysql.lib PiAPI.lib Pi2API.lib Pi3API.lib /nologo /version:4.0 /dll /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcmt.lib" /out:"..\Release_TS\php5ts.dll" /libpath:"..\TSRM\Release_TS" /libpath:"..\Zend\Release_TS" /libpath:"..\..\bindlib_w32\Release" /libpath:"..\ext\mysql\libmysql\Release_TS" /libpath:"Release_TS" /libpath:"..\..\..\PiAPI" /libpath:"..\..\..\Pi2API" /libpath:"..\..\..\Pi3API"
+# ADD LINK32 php5ts.lib kernel32.lib user32.lib gdi32.lib wsock32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib PiAPI.lib Pi2API.lib Pi3API.lib /nologo /version:4.0 /dll /machine:I386 /nodefaultlib:"libc.lib" /nodefaultlib:"libcmt.lib" /libpath:"..\..\Release_TS_inline" /libpath:"..\..\..\..\PIAPI" /libpath:"..\..\..\..\PI2API" /libpath:"..\..\..\..\PI3API"
+
+!ENDIF
+
+# Begin Target
+
+# Name "php5pi3web - Win32 Debug_TS"
+# Name "php5pi3web - Win32 Release_TS"
+# Name "php5pi3web - Win32 Release_TS_inline"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ".c"
+# Begin Source File
+
+SOURCE=.\pi3web_sapi.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter ".h"
+# Begin Source File
+
+SOURCE=.\pi3web_sapi.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/sapi/pi3web/pi3web_sapi.c b/sapi/pi3web/pi3web_sapi.c
new file mode 100644
index 0000000..64eb2a6
--- /dev/null
+++ b/sapi/pi3web/pi3web_sapi.c
@@ -0,0 +1,439 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Pi3Web version 2.0 |
+ +----------------------------------------------------------------------+
+ | This file is committed by the Pi3 development group. |
+ | (pi3web.sourceforge.net) |
+ | |
+ | Author: Holger Zimmermann (zimpel@users.sourceforge.net) |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#define ZEND_INCLUDE_FULL_WINDOWS_HEADERS
+
+#include "php.h"
+#include "php_main.h"
+#include "php_variables.h"
+#include "SAPI.h"
+#include "php_globals.h"
+#include "ext/standard/info.h"
+#include "zend_highlight.h"
+#include "zend_indent.h"
+#include "zend_alloc.h"
+#include "ext/standard/basic_functions.h"
+#include "TSRM/TSRM.h"
+#include "PiAPI.h"
+#include "Pi3API.h"
+
+#include "pi3web_sapi.h"
+
+#define PI3WEB_SERVER_VAR_BUF_SIZE 1024
+
+int IWasLoaded=0;
+
+
+static void php_info_pi3web(ZEND_MODULE_INFO_FUNC_ARGS)
+{
+ char variable_buf[PI3WEB_SERVER_VAR_BUF_SIZE];
+ DWORD variable_len;
+ LPCONTROL_BLOCK lpCB = (LPCONTROL_BLOCK) SG(server_context);
+ PIDB *pDB = (PIDB *)lpCB->GetVariableNames(lpCB->ConnID);
+ PIDBIterator *pIter = PIDB_getIterator( pDB, PIDBTYPE_STRING, 0, 0 );
+
+ PUTS("<table border=0 cellpadding=3 cellspacing=1 width=600 align=center>\n");
+ PUTS("<tr><th colspan=2 bgcolor=\"" PHP_HEADER_COLOR "\">Pi3Web Server Information</th></tr>\n");
+ php_info_print_table_header(2, "Information Field", "Value");
+ php_info_print_table_row(2, "Pi3Web SAPI module version", "$Id$");
+ php_info_print_table_row(2, "Server Name Stamp", HTTPCore_getServerStamp());
+ snprintf(variable_buf, 511, "%d", HTTPCore_debugEnabled());
+ php_info_print_table_row(2, "Debug Enabled", variable_buf);
+ PIPlatform_getCurrentDirectory( variable_buf, PI3WEB_SERVER_VAR_BUF_SIZE);
+ php_info_print_table_row(2, "Current Path", variable_buf);
+ if (lpCB->GetServerVariable(lpCB->ConnID, "SERVER_NAME", variable_buf, &variable_len)
+ && variable_buf[0]) {
+ php_info_print_table_row(2, "Main Virtual Hostname", variable_buf);
+ };
+ snprintf(variable_buf, 511, "%d", PIPlatform_getProcessId());
+ php_info_print_table_row(2, "Server PID", variable_buf);
+ php_info_print_table_row(2, "Server Platform", PIPlatform_getDescription());
+
+ PUTS("</table><br />");
+
+ PUTS("<table border=0 cellpadding=3 cellspacing=1 width=600 align=center>\n");
+ PUTS("<tr><th colspan=2 bgcolor=\"" PHP_HEADER_COLOR "\">HTTP Request Information</th></tr>\n");
+ php_info_print_table_row(2, "HTTP Request Line", lpCB->lpszReq);
+ PUTS("<tr><th colspan=2 bgcolor=\"" PHP_HEADER_COLOR "\">HTTP Headers</th></tr>\n");
+ php_info_print_table_header(2, "Server Variable", "Value");
+
+ /* --- loop over all registered server variables --- */
+ for(; pIter && PIDBIterator_atValidElement( pIter ); PIDBIterator_next( pIter ) )
+ {
+ PCHAR pKey;
+ PIDBIterator_current( pIter, &pKey );
+ if ( !pKey ) { /* sanity */ continue; };
+
+ variable_len = PI3WEB_SERVER_VAR_BUF_SIZE;
+ if (lpCB->GetServerVariable(lpCB->ConnID, pKey, variable_buf, &variable_len)
+ && variable_buf[0]) {
+ php_info_print_table_row(2, pKey, variable_buf);
+ } else if (PIPlatform_getLastError() == PIAPI_EINVAL) {
+ char *tmp_variable_buf;
+
+ tmp_variable_buf = (char *) emalloc(variable_len);
+ if (lpCB->GetServerVariable(lpCB->ConnID, pKey, tmp_variable_buf, &variable_len)
+ && variable_buf[0]) {
+ php_info_print_table_row(2, pKey, tmp_variable_buf);
+ }
+ efree(tmp_variable_buf);
+ }
+ }
+
+ PUTS("</table>");
+}
+
+
+static zend_module_entry php_pi3web_module = {
+ STANDARD_MODULE_HEADER,
+ "PI3WEB",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ php_info_pi3web,
+ NULL,
+ STANDARD_MODULE_PROPERTIES
+};
+
+
+static int zend_pi3web_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ DWORD num_bytes = str_length;
+ LPCONTROL_BLOCK cb;
+
+ cb = (LPCONTROL_BLOCK) SG(server_context);
+
+ if ( !IWasLoaded ) return 0;
+ cb->WriteClient(cb->ConnID, (char *) str, &num_bytes, 0 );
+
+ if (num_bytes != str_length)
+ php_handle_aborted_connection();
+ return num_bytes;
+}
+
+
+static int sapi_pi3web_header_handler(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ return SAPI_HEADER_ADD;
+}
+
+
+static void accumulate_header_length(sapi_header_struct *sapi_header, uint *total_length TSRMLS_DC)
+{
+ *total_length += sapi_header->header_len+2;
+}
+
+
+static void concat_header(sapi_header_struct *sapi_header, char **combined_headers_ptr TSRMLS_DC)
+{
+ memcpy(*combined_headers_ptr, sapi_header->header, sapi_header->header_len);
+ *combined_headers_ptr += sapi_header->header_len;
+ **combined_headers_ptr = '\r';
+ (*combined_headers_ptr)++;
+ **combined_headers_ptr = '\n';
+ (*combined_headers_ptr)++;
+}
+
+
+static int sapi_pi3web_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ uint total_length = 2; /* account for the trailing \r\n */
+ char *combined_headers, *combined_headers_ptr;
+ LPCONTROL_BLOCK lpCB = (LPCONTROL_BLOCK) SG(server_context);
+ sapi_header_struct default_content_type;
+
+ if ( !IWasLoaded ) return SAPI_HEADER_SENT_SUCCESSFULLY;
+
+
+ if (SG(sapi_headers).send_default_content_type) {
+ sapi_get_default_content_type_header(&default_content_type TSRMLS_CC);
+ accumulate_header_length(&default_content_type, (void *) &total_length TSRMLS_CC);
+ }
+ zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) accumulate_header_length, (void *) &total_length TSRMLS_CC);
+
+ /* Generate headers */
+ combined_headers = (char *) emalloc(total_length+1);
+ combined_headers_ptr = combined_headers;
+ if (SG(sapi_headers).send_default_content_type) {
+ concat_header(&default_content_type, (void *) &combined_headers_ptr TSRMLS_CC);
+ sapi_free_header(&default_content_type); /* we no longer need it */
+ }
+ zend_llist_apply_with_argument(&SG(sapi_headers).headers, (llist_apply_with_arg_func_t) concat_header, (void *) &combined_headers_ptr TSRMLS_CC);
+ *combined_headers_ptr++ = '\r';
+ *combined_headers_ptr++ = '\n';
+ *combined_headers_ptr = 0;
+
+ lpCB->dwHttpStatusCode = SG(sapi_headers).http_response_code;
+ lpCB->SendHeaderFunction(lpCB->ConnID, &total_length, (LPDWORD) combined_headers);
+
+ efree(combined_headers);
+ if (SG(sapi_headers).http_status_line) {
+ efree(SG(sapi_headers).http_status_line);
+ SG(sapi_headers).http_status_line = 0;
+ }
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+
+static int php_pi3web_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, &php_pi3web_module, 1)==FAILURE) {
+ return FAILURE;
+ } else {
+ return SUCCESS;
+ }
+}
+
+
+static int sapi_pi3web_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ LPCONTROL_BLOCK lpCB = (LPCONTROL_BLOCK) SG(server_context);
+ DWORD read_from_buf=0;
+ DWORD read_from_input=0;
+ DWORD total_read=0;
+
+ if ((DWORD)SG(read_post_bytes) < lpCB->cbAvailable) {
+ read_from_buf = MIN(lpCB->cbAvailable-SG(read_post_bytes), count_bytes);
+ memcpy(buffer, lpCB->lpbData+SG(read_post_bytes), read_from_buf);
+ total_read += read_from_buf;
+ }
+ if (read_from_buf<count_bytes
+ && (SG(read_post_bytes)+read_from_buf) < lpCB->cbTotalBytes) {
+ DWORD cbRead=0, cbSize;
+
+ read_from_input = MIN(count_bytes-read_from_buf, lpCB->cbTotalBytes-SG(read_post_bytes)-read_from_buf);
+ while (cbRead < read_from_input) {
+ cbSize = read_from_input - cbRead;
+ if (!lpCB->ReadClient(lpCB->ConnID, buffer+read_from_buf+cbRead, &cbSize) || cbSize==0) {
+ break;
+ }
+ cbRead += cbSize;
+ }
+ total_read += cbRead;
+ }
+
+ /* removed after re-testing POST with Pi3Web 2.0.2 */
+ /* SG(read_post_bytes) += total_read; */
+ return total_read;
+}
+
+
+static char *sapi_pi3web_read_cookies(TSRMLS_D)
+{
+ LPCONTROL_BLOCK lpCB = (LPCONTROL_BLOCK) SG(server_context);
+ char variable_buf[PI3WEB_SERVER_VAR_BUF_SIZE];
+ DWORD variable_len = PI3WEB_SERVER_VAR_BUF_SIZE;
+
+ if (lpCB->GetServerVariable(lpCB->ConnID, "HTTP_COOKIE", variable_buf, &variable_len)) {
+ return estrndup(variable_buf, variable_len);
+ } else if (PIPlatform_getLastError()==PIAPI_EINVAL) {
+ char *tmp_variable_buf = (char *) emalloc(variable_len+1);
+
+ if (lpCB->GetServerVariable(lpCB->ConnID, "HTTP_COOKIE", tmp_variable_buf, &variable_len)) {
+ tmp_variable_buf[variable_len] = 0;
+ return tmp_variable_buf;
+ } else {
+ efree(tmp_variable_buf);
+ }
+ }
+ return NULL;
+}
+
+static void init_request_info(LPCONTROL_BLOCK lpCB TSRMLS_DC)
+{
+ SG(server_context) = lpCB;
+ SG(request_info).request_method = lpCB->lpszMethod;
+ SG(request_info).query_string = lpCB->lpszQueryString;
+ SG(request_info).path_translated = lpCB->lpszPathTranslated;
+ SG(request_info).request_uri = lpCB->lpszUri;
+ SG(request_info).content_type = lpCB->lpszContentType;
+ SG(request_info).content_length = lpCB->cbTotalBytes;
+ SG(request_info).auth_user = (lpCB->lpszUser) ? (char *)estrdup((const char *)(lpCB->lpszUser)) : 0;
+ SG(request_info).auth_password = (lpCB->lpszPassword) ? (char *)estrdup((const char *)(lpCB->lpszPassword)) : 0;
+ SG(sapi_headers).http_response_code = 200;
+}
+
+static void sapi_pi3web_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ char static_variable_buf[PI3WEB_SERVER_VAR_BUF_SIZE];
+ char *variable_buf;
+ DWORD variable_len = PI3WEB_SERVER_VAR_BUF_SIZE;
+ LPCONTROL_BLOCK lpCB = (LPCONTROL_BLOCK) SG(server_context);
+ PIDB *pDB = (PIDB *)lpCB->GetVariableNames(lpCB->ConnID);
+ PIDBIterator *pIter = PIDB_getIterator( pDB, PIDBTYPE_STRING, 0, 0 );
+
+ /* --- loop over all registered server variables --- */
+ for(; pIter && PIDBIterator_atValidElement( pIter ); PIDBIterator_next( pIter ) )
+ {
+ PCHAR pKey;
+ PIDBIterator_current( pIter, &pKey );
+ if ( !pKey ) { /* sanity */ continue; };
+
+ variable_len = PI3WEB_SERVER_VAR_BUF_SIZE;
+ if (lpCB->GetServerVariable(lpCB->ConnID, pKey, static_variable_buf, &variable_len)
+ && (variable_len > 1)) {
+ php_register_variable(pKey, static_variable_buf, track_vars_array TSRMLS_CC);
+ } else if (PIPlatform_getLastError()==PIAPI_EINVAL) {
+ variable_buf = (char *) emalloc(variable_len);
+ if (lpCB->GetServerVariable(lpCB->ConnID, pKey, variable_buf, &variable_len)) {
+ php_register_variable(pKey, variable_buf, track_vars_array TSRMLS_CC);
+ }
+ efree(variable_buf);
+ }
+
+ }
+
+
+ /* PHP_SELF support */
+ variable_len = PI3WEB_SERVER_VAR_BUF_SIZE;
+ if (lpCB->GetServerVariable(lpCB->ConnID, "SCRIPT_NAME", static_variable_buf, &variable_len)
+ && (variable_len > 1)) {
+ php_register_variable("PHP_SELF", static_variable_buf, track_vars_array TSRMLS_CC);
+ }
+}
+
+static sapi_module_struct pi3web_sapi_module = {
+ "pi3web", /* name */
+ "PI3WEB", /* pretty name */
+
+ php_pi3web_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+ NULL, /* activate */
+ NULL, /* deactivate */
+ zend_pi3web_ub_write, /* unbuffered write */
+ NULL, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+ php_error, /* error handler */
+ sapi_pi3web_header_handler, /* header handler */
+ sapi_pi3web_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+ sapi_pi3web_read_post, /* read POST data */
+ sapi_pi3web_read_cookies, /* read Cookies */
+ sapi_pi3web_register_variables, /* register server variables */
+ NULL, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+MODULE_API DWORD PHP5_wrapper(LPCONTROL_BLOCK lpCB)
+{
+ zend_file_handle file_handle = {0};
+ int iRet = PIAPI_COMPLETED;
+ TSRMLS_FETCH();
+
+ zend_first_try {
+ file_handle.filename = lpCB->lpszFileName;
+ file_handle.free_filename = 0;
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.opened_path = NULL;
+
+ init_request_info(lpCB TSRMLS_CC);
+ php_request_startup(TSRMLS_C);
+
+ switch ( lpCB->dwBehavior ) {
+ case PHP_MODE_STANDARD:
+ iRet = ( php_execute_script( &file_handle TSRMLS_CC ) ) ?
+ PIAPI_COMPLETED : PIAPI_ERROR;
+ break;
+ case PHP_MODE_HIGHLIGHT: {
+ zend_syntax_highlighter_ini syntax_highlighter_ini;
+ if ( open_file_for_scanning( &file_handle TSRMLS_CC ) == SUCCESS )
+ {
+ php_get_highlight_struct( &syntax_highlighter_ini );
+ zend_highlight( &syntax_highlighter_ini TSRMLS_CC );
+ }
+ else
+ {
+ iRet = PIAPI_ERROR;
+ };
+ };
+ break;
+ case PHP_MODE_INDENT: {
+ sapi_header_line ctr = {0};
+
+ ctr.line = "Content-Type: text/plain";
+ ctr.line_len = strlen(ctr.line);
+
+ sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC);
+ }
+ if ( open_file_for_scanning( &file_handle TSRMLS_CC ) == SUCCESS )
+ {
+ zend_indent();
+ }
+ else
+ {
+ iRet = PIAPI_ERROR;
+ };
+ break;
+ case PHP_MODE_LINT:
+ iRet = (php_lint_script(&file_handle TSRMLS_CC) == SUCCESS) ?
+ PIAPI_COMPLETED : PIAPI_ERROR;
+ break;
+ default:
+ iRet = PIAPI_ERROR;;
+ }
+
+ if (SG(request_info).cookie_data) {
+ efree(SG(request_info).cookie_data);
+ };
+
+ php_request_shutdown(NULL);
+ } zend_catch {
+ iRet = PIAPI_ERROR;
+ } zend_end_try();
+ return iRet;
+}
+
+MODULE_API BOOL PHP5_startup() {
+ tsrm_startup(1, 1, 0, NULL);
+ sapi_startup(&pi3web_sapi_module);
+ if (pi3web_sapi_module.startup) {
+ pi3web_sapi_module.startup(&pi3web_sapi_module);
+ };
+ IWasLoaded = 1;
+ return IWasLoaded;
+};
+
+MODULE_API BOOL PHP5_shutdown() {
+ if (pi3web_sapi_module.shutdown) {
+ pi3web_sapi_module.shutdown(&pi3web_sapi_module);
+ };
+ sapi_shutdown();
+ tsrm_shutdown();
+ IWasLoaded = 0;
+ return !IWasLoaded;
+};
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/sapi/pi3web/pi3web_sapi.h b/sapi/pi3web/pi3web_sapi.h
new file mode 100644
index 0000000..d229fec
--- /dev/null
+++ b/sapi/pi3web/pi3web_sapi.h
@@ -0,0 +1,102 @@
+#ifndef _PI3WEB_SAPI_H_
+#define _PI3WEB_SAPI_H_
+
+#ifdef PHP_WIN32
+# include <windows.h>
+# ifdef PHP5PI3WEB_EXPORTS
+# define MODULE_API __declspec(dllexport)
+# else
+# define MODULE_API __declspec(dllimport)
+# endif
+#else
+# if defined(__GNUC__) && __GNUC__ >= 4
+# define MODULE_API __attribute__ ((visibility("default")))
+# else
+# define MODULE_API
+# endif
+# define far
+
+ typedef int BOOL;
+ typedef void far *LPVOID;
+ typedef unsigned long DWORD;
+ typedef DWORD far *LPDWORD;
+ typedef char CHAR;
+ typedef CHAR *LPSTR;
+ typedef unsigned char BYTE;
+ typedef BYTE far *LPBYTE;
+#endif
+
+ typedef LPVOID HCONN;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PHP_MODE_STANDARD 1
+#define PHP_MODE_HIGHLIGHT 2
+#define PHP_MODE_INDENT 3
+#define PHP_MODE_LINT 4
+
+//
+// passed to the procedure on a new request
+//
+typedef struct _CONTROL_BLOCK {
+ DWORD cbSize; // size of this struct.
+ HCONN ConnID; // Context number not to be modified!
+ DWORD dwHttpStatusCode; // HTTP Status code
+ CHAR lpszLogData[80]; // null terminated log info
+
+ LPSTR lpszMethod; // REQUEST_METHOD
+ LPSTR lpszQueryString; // QUERY_STRING
+ LPSTR lpszPathInfo; // PATH_INFO
+ LPSTR lpszPathTranslated; // PATH_TRANSLATED
+ LPSTR lpszFileName; // FileName to PHP3 physical file
+ LPSTR lpszUri; // The request URI
+ LPSTR lpszReq; // The whole HTTP request line
+ LPSTR lpszUser; // The authenticated user
+ LPSTR lpszPassword; // The authenticated password
+
+ DWORD cbTotalBytes; // Total bytes indicated from client
+ DWORD cbAvailable; // Available number of bytes
+ LPBYTE lpbData; // pointer to cbAvailable bytes
+
+ LPSTR lpszContentType; // Content type of client data
+ DWORD dwBehavior; // PHP behavior (standard, highlight, intend
+
+
+ LPVOID (* GetVariableNames) (HCONN hConn);
+
+ BOOL (* GetServerVariable) ( HCONN hConn,
+ LPSTR lpszVariableName,
+ LPVOID lpvBuffer,
+ LPDWORD lpdwSize );
+
+ BOOL (* WriteClient) ( HCONN hConn,
+ LPVOID lpvBuffer,
+ LPDWORD lpdwBytes,
+ DWORD dwReserved );
+
+ BOOL (* ReadClient) ( HCONN hConn,
+ LPVOID lpvBuffer,
+ LPDWORD lpdwSize );
+
+ BOOL (* SendHeaderFunction)( HCONN hConn,
+ LPDWORD lpdwSize,
+ LPDWORD lpdwDataType );
+
+} CONTROL_BLOCK, *LPCONTROL_BLOCK;
+
+MODULE_API DWORD PHP5_wrapper(LPCONTROL_BLOCK lpCB);
+MODULE_API BOOL PHP5_startup();
+MODULE_API BOOL PHP5_shutdown();
+
+// the following type declaration is for the server side
+typedef DWORD ( * PFN_WRAPPERFUNC )( CONTROL_BLOCK *pCB );
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // end definition _PI3WEB_SAPI_H_
diff --git a/sapi/roxen/README b/sapi/roxen/README
new file mode 100644
index 0000000..d834a00
--- /dev/null
+++ b/sapi/roxen/README
@@ -0,0 +1,18 @@
+Roxen PHP support. Early version. Don't expect to be able to get it to
+work. Requires Pike 0.7.79 and Roxen 1.4. Anything less won't work.
+
+The module is now thread safe, in a couple of different modes. First
+mode, the default, uses a process global PHP lock in the Roxen
+module. This means that all PHP-requests are serialized (ie only one
+script is executed at any one time). The second option is using ZTS
+(Zend Thread Safe mode). Unless --enable-roxen-zts is specified, this
+won't be used.
+
+This solution now works fine and is recommended. Multiple PHP5
+requests will be run in parallell. The maximum number of parallell
+PHP5-execution is limited to the number of handle threads Roxen is
+started with.
+
+Support for this module is lacking. Please contact Roxen Internet
+Software for support and help. See http://www.roxen.com/company/contact/
+for contact information.
diff --git a/sapi/roxen/TODO b/sapi/roxen/TODO
new file mode 100644
index 0000000..248f36f
--- /dev/null
+++ b/sapi/roxen/TODO
@@ -0,0 +1,33 @@
+BUGS:
+
+- fix backtraces
+- exit in PHP exits Roxen
+- POST newline added?
+- Rewriting header handling so that more than one header with the same
+ name can be set (most importantly, cookies).
+- Recursive mutex lock problem:
+
+ And another error (when trying to include a class)
+
+ Recursive mutex locks!
+ /Usr/local/pike/7.0.54/lib/modules/PHP5.so.Interpreter:
+ run("/home/www/www.tx.pl/news/test.php",mapping[3],modules/scripting/php5.pike.PHPScript(),modules/scripting/php5.pike.PHPScript.done)
+ modules/scripting/php5.pike:169: run()
+ base_server/roxen.pike:569: handler_thread(3).
+
+ And after this every access to any php script (on other virtual sites
+ also) ends (of course there is no proper output) with this error:
+
+ Php4.Interpreter->run: Tried to run a PHP-script from a PHP
+ callback!/usr/local/pike/7.0.54/lib/modules/PHP5.so.Interpreter:
+ run("/home/www/biall.com.pl/index.php3",mapping[2],modules/scripting/php5.pike.PHPScript(),modules/scripting/php5.pike.PHPScript.done)
+ modules/scripting/php5.pike:169: run()
+ base_server/roxen.pike:569: handler_thread(3).
+
+
+ADDITIONS:
+
+- use th_farm
+- change cwd in single threaded mode
+- per-virtual-server configuration
+
diff --git a/sapi/roxen/config.m4 b/sapi/roxen/config.m4
new file mode 100644
index 0000000..9b0bb90
--- /dev/null
+++ b/sapi/roxen/config.m4
@@ -0,0 +1,55 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(roxen,,
+[ --with-roxen=DIR Build PHP as a Pike module. DIR is the base Roxen
+ directory, normally /usr/local/roxen/server], no, no)
+
+PHP_ARG_ENABLE(roxen-zts, whether Roxen module is build using ZTS,
+[ --enable-roxen-zts ROXEN: Build the Roxen module using Zend Thread Safety], no, no)
+
+RESULT=
+AC_MSG_CHECKING([for Roxen/Pike support])
+if test "$PHP_ROXEN" != "no"; then
+ if test ! -d $PHP_ROXEN ; then
+ AC_MSG_ERROR([You did not specify a directory])
+ fi
+ if test -f $PHP_ROXEN/bin/roxen; then
+ PIKE=$PHP_ROXEN/bin/roxen
+ elif test -f $PHP_ROXEN/bin/pike; then
+ PIKE=$PHP_ROXEN/bin/pike
+ else
+ AC_MSG_ERROR([Could not find a pike in $PHP_ROXEN/bin/])
+ fi
+
+ if $PIKE -e 'float v; catch(v = __VERSION__ + (__BUILD__/10000.0)); if(v < 0.7079) exit(1); exit(0);'; then
+ PIKE_MODULE_DIR=`$PIKE --show-paths 2>&1| grep '^Module' | sed -e 's/.*: //'`
+ PIKE_INCLUDE_DIR=`echo $PIKE_MODULE_DIR | sed -e 's,lib/pike/modules,include/pike,' -e 's,lib/modules,include/pike,'`
+ if test -z "$PIKE_INCLUDE_DIR" || test -z "$PIKE_MODULE_DIR"; then
+ AC_MSG_ERROR([Failed to figure out Pike module and include directories])
+ fi
+ else
+ AC_MSG_ERROR([Roxen/PHP requires Pike 0.7.79 or newer])
+ fi
+
+ PHP_ADD_INCLUDE($PIKE_INCLUDE_DIR)
+ AC_DEFINE(HAVE_ROXEN, 1, [Whether you use Roxen])
+ PHP_SELECT_SAPI(roxen, shared, roxen.c)
+ INSTALL_IT="\$(INSTALL) -m 0755 $SAPI_SHARED $PIKE_MODULE_DIR/PHP5.so"
+ RESULT="yes
+ Pike binary used: $PIKE
+ Pike include dir: $PIKE_INCLUDE_DIR
+ Pike module directory: $PIKE_MODULE_DIR"
+ PIKE_INCLUDE_DIR=" -I$PIKE_INCLUDE_DIR "
+
+ if test "$PHP_ROXEN_ZTS" != "no"; then
+ PHP_BUILD_THREAD_SAFE
+ AC_DEFINE(ROXEN_USE_ZTS, 1, [Whether to use Roxen in ZTS mode])
+ fi
+fi
+AC_MSG_RESULT([$RESULT])
+
+dnl ## Local Variables:
+dnl ## tab-width: 4
+dnl ## End:
diff --git a/sapi/roxen/roxen.c b/sapi/roxen/roxen.c
new file mode 100644
index 0000000..c897ef6
--- /dev/null
+++ b/sapi/roxen/roxen.c
@@ -0,0 +1,727 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: David Hedbor <neotron@php.net> |
+ | Based on aolserver SAPI by Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include "php.h"
+#ifdef HAVE_ROXEN
+
+#include "php_ini.h"
+#include "php_globals.h"
+#include "SAPI.h"
+#include "php_main.h"
+#include "ext/standard/info.h"
+
+#include "php_version.h"
+
+#ifndef ZTS
+/* Only valid if thread safety is enabled. */
+#undef ROXEN_USE_ZTS
+#endif
+
+
+/* Pike Include Files
+ *
+ * conflicts with pike avoided by only using long names. Requires a new
+ * Pike 0.7 since it was implemented for this interface only.
+ *
+ */
+#define NO_PIKE_SHORTHAND
+
+#include <fdlib.h>
+#include <program.h>
+#include <pike_types.h>
+#include <interpret.h>
+#include <module_support.h>
+#include <error.h>
+#include <array.h>
+#include <backend.h>
+#include <stralloc.h>
+#include <mapping.h>
+#include <object.h>
+#include <threads.h>
+#include <builtin_functions.h>
+#include <operators.h>
+
+#undef HIDE_GLOBAL_VARIABLES
+#undef REVEAL_GLOBAL_VARIABLES
+#define HIDE_GLOBAL_VARIABLES()
+#define REVEAL_GLOBAL_VARIABLES()
+
+/* php_roxen_request is per-request object storage */
+
+typedef struct
+{
+ struct mapping *request_data;
+ struct object *my_fd_obj;
+ int my_fd;
+ char *filename;
+} php_roxen_request;
+
+
+/* Defines to get to the data supplied when the script is started. */
+
+#ifdef ROXEN_USE_ZTS
+
+/* ZTS does work now, but it seems like it's faster using the "serialization"
+ * method I previously used. Thus it's not used unless ROXEN_USE_ZTS is defined.
+ */
+
+/* Per thread storage area id... */
+static int roxen_globals_id;
+
+# define GET_THIS() php_roxen_request *_request = ts_resource(roxen_globals_id)
+# define THIS _request
+#else
+static php_roxen_request *current_request = NULL;
+
+# define GET_THIS() current_request = ((php_roxen_request *)Pike_fp->current_storage)
+# define THIS current_request
+#endif
+
+/* File descriptor integer. Used to write directly to the FD without
+ * passing Pike
+ */
+#define MY_FD (THIS->my_fd)
+
+/* FD object. Really a PHPScript object from Pike which implements a couple
+ * of functions to handle headers, writing and buffering.
+ */
+#define MY_FD_OBJ ((struct object *)(THIS->my_fd_obj))
+
+/* Mapping with data supplied from the calling Roxen module. Contains
+ * a mapping with headers, an FD object etc.
+ */
+#define REQUEST_DATA ((struct mapping *)(THIS->request_data))
+
+
+#if defined(_REENTRANT) && !defined(ROXEN_USE_ZTS)
+/* Lock used to serialize the PHP execution. If ROXEN_USE_ZTS is defined, we
+ * are using the PHP thread safe mechanism instead.
+ */
+static PIKE_MUTEX_T roxen_php_execution_lock;
+# define PHP_INIT_LOCK() mt_init(&roxen_php_execution_lock)
+# define PHP_LOCK(X) THREADS_ALLOW();mt_lock(&roxen_php_execution_lock);THREADS_DISALLOW()
+# define PHP_UNLOCK(X) mt_unlock(&roxen_php_execution_lock);
+# define PHP_DESTROY() mt_destroy(&roxen_php_execution_lock)
+#else /* !_REENTRANT */
+# define PHP_INIT_LOCK()
+# define PHP_LOCK(X)
+# define PHP_UNLOCK(X)
+# define PHP_DESTROY()
+#endif /* _REENTRANT */
+
+extern int fd_from_object(struct object *o);
+static unsigned char roxen_php_initialized;
+
+/* This allows calling of pike functions from the PHP callbacks,
+ * which requires the Pike interpreter to be locked.
+ */
+#define THREAD_SAFE_RUN(COMMAND, what) do {\
+ struct thread_state *state;\
+ if((state = thread_state_for_id(th_self()))!=NULL) {\
+ if(!state->swapped) {\
+ COMMAND;\
+ } else {\
+ mt_lock(&interpreter_lock);\
+ SWAP_IN_THREAD(state);\
+ COMMAND;\
+ SWAP_OUT_THREAD(state);\
+ mt_unlock(&interpreter_lock);\
+ }\
+ }\
+} while(0)
+
+struct program *php_program;
+
+
+/* To avoid executing a PHP script from a PHP callback, which would
+ * create a deadlock, a global thread id is used. If the thread calling the
+ * php-script is the same as the current thread, it fails.
+ */
+static int current_thread = -1;
+
+
+/* Low level header lookup. Basically looks for the named header in the mapping
+ * headers in the supplied options mapping.
+ */
+
+static INLINE struct svalue *lookup_header(char *headername)
+{
+ struct svalue *headers, *value;
+ struct pike_string *sind;
+#ifdef ROXEN_USE_ZTS
+ GET_THIS();
+#endif
+ sind = make_shared_string("env");
+ headers = low_mapping_string_lookup(REQUEST_DATA, sind);
+ free_string(sind);
+ if(!headers || headers->type != PIKE_T_MAPPING) return NULL;
+ sind = make_shared_string(headername);
+ value = low_mapping_string_lookup(headers->u.mapping, sind);
+ free_string(sind);
+ if(!value) return NULL;
+ return value;
+}
+
+/* Lookup a header in the mapping and return the value as a string, or
+ * return the default if it's missing
+ */
+INLINE static char *lookup_string_header(char *headername, char *default_value)
+{
+ struct svalue *head = NULL;
+ THREAD_SAFE_RUN(head = lookup_header(headername), "header lookup");
+ if(!head || head->type != PIKE_T_STRING)
+ return default_value;
+ return head->u.string->str;
+}
+
+/* Lookup a header in the mapping and return the value as if it's an integer
+ * and otherwise return the default.
+ */
+INLINE static int lookup_integer_header(char *headername, int default_value)
+{
+ struct svalue *head = NULL;
+ THREAD_SAFE_RUN(head = lookup_header(headername), "header lookup");
+ if(!head || head->type != PIKE_T_INT)
+ return default_value;
+ return head->u.integer;
+}
+
+/*
+ * php_roxen_low_ub_write() writes data to the client connection. Might be
+ * rewritten to do more direct IO to save CPU and the need to lock the *
+ * interpreter for better threading.
+ */
+
+static int
+php_roxen_low_ub_write(const char *str, uint str_length TSRMLS_DC) {
+ int sent_bytes = 0;
+ struct pike_string *to_write = NULL;
+#ifdef ROXEN_USE_ZTS
+ GET_THIS();
+#endif
+
+ if(!MY_FD_OBJ->prog) {
+ PG(connection_status) = PHP_CONNECTION_ABORTED;
+ zend_bailout();
+ return -1;
+ }
+ to_write = make_shared_binary_string(str, str_length);
+ push_string(to_write);
+ safe_apply(MY_FD_OBJ, "write", 1);
+ if(Pike_sp[-1].type == PIKE_T_INT)
+ sent_bytes = Pike_sp[-1].u.integer;
+ pop_stack();
+ if(sent_bytes != str_length) {
+ /* This means the connection is closed. Dead. Gone. *sniff* */
+ php_handle_aborted_connection();
+ }
+ return sent_bytes;
+}
+
+/*
+ * php_roxen_sapi_ub_write() calls php_roxen_low_ub_write in a Pike thread
+ * safe manner.
+ */
+
+static int
+php_roxen_sapi_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+#ifdef ROXEN_USE_ZTS
+ GET_THIS();
+#endif
+
+ int sent_bytes = 0, fd = MY_FD;
+ if(fd)
+ {
+ for(sent_bytes=0;sent_bytes < str_length;)
+ {
+ int written;
+ written = fd_write(fd, str + sent_bytes, str_length - sent_bytes);
+ if(written < 0)
+ {
+ switch(errno)
+ {
+ default:
+ /* This means the connection is closed. Dead. Gone. *sniff* */
+ PG(connection_status) = PHP_CONNECTION_ABORTED;
+ zend_bailout();
+ return sent_bytes;
+ case EINTR:
+ case EWOULDBLOCK:
+ continue;
+ }
+
+ } else {
+ sent_bytes += written;
+ }
+ }
+ } else {
+ THREAD_SAFE_RUN(sent_bytes = php_roxen_low_ub_write(str, str_length TSRMLS_CC),
+ "write");
+ }
+ return sent_bytes;
+}
+
+/* php_roxen_set_header() sets a header in the header mapping. Called in a
+ * thread safe manner from php_roxen_sapi_header_handler.
+ */
+static void php_roxen_set_header(char *header_name, char *value, char *p)
+{
+ struct svalue hsval;
+ struct pike_string *hval, *ind, *hind;
+ struct mapping *headermap;
+ struct svalue *s_headermap;
+#ifdef ROXEN_USE_ZTS
+ GET_THIS();
+#endif
+ hval = make_shared_string(value);
+ ind = make_shared_string(" _headers");
+ hind = make_shared_binary_string(header_name,
+ (int)(p - header_name));
+
+ s_headermap = low_mapping_string_lookup(REQUEST_DATA, ind);
+ if(!s_headermap)
+ {
+ struct svalue mappie;
+ mappie.type = PIKE_T_MAPPING;
+ headermap = allocate_mapping(1);
+ mappie.u.mapping = headermap;
+ mapping_string_insert(REQUEST_DATA, ind, &mappie);
+ free_mapping(headermap);
+ } else
+ headermap = s_headermap->u.mapping;
+
+ hsval.type = PIKE_T_STRING;
+ hsval.u.string = hval;
+ mapping_string_insert(headermap, hind, &hsval);
+
+ free_string(hval);
+ free_string(ind);
+ free_string(hind);
+}
+
+/*
+ * php_roxen_sapi_header_handler() sets a HTTP reply header to be
+ * sent to the client.
+ */
+static int
+php_roxen_sapi_header_handler(sapi_header_struct *sapi_header,
+ sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ char *header_name, *header_content, *p;
+ header_name = sapi_header->header;
+ header_content = p = strchr(header_name, ':');
+
+ if(p) {
+ do {
+ header_content++;
+ } while(*header_content == ' ');
+ THREAD_SAFE_RUN(php_roxen_set_header(header_name, header_content, p), "header handler");
+ }
+ sapi_free_header(sapi_header);
+ return 0;
+}
+
+/*
+ * php_roxen_sapi_send_headers() flushes the headers to the client.
+ * Called before real content is sent by PHP.
+ */
+
+static int
+php_roxen_low_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ struct pike_string *ind;
+ struct svalue *s_headermap;
+#ifdef ROXEN_USE_ZTS
+ GET_THIS();
+#endif
+
+ if(!MY_FD_OBJ->prog) {
+ PG(connection_status) = PHP_CONNECTION_ABORTED;
+ zend_bailout();
+ return SAPI_HEADER_SEND_FAILED;
+ }
+ ind = make_shared_string(" _headers");
+ s_headermap = low_mapping_string_lookup(REQUEST_DATA, ind);
+ free_string(ind);
+
+ push_int(SG(sapi_headers).http_response_code);
+ if(s_headermap && s_headermap->type == PIKE_T_MAPPING)
+ ref_push_mapping(s_headermap->u.mapping);
+ else
+ push_int(0);
+ safe_apply(MY_FD_OBJ, "send_headers", 2);
+ pop_stack();
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+static int
+php_roxen_sapi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ int res = 0;
+ THREAD_SAFE_RUN(res = php_roxen_low_send_headers(sapi_headers TSRMLS_CC), "send headers");
+ return res;
+}
+
+/*
+ * php_roxen_sapi_read_post() reads a specified number of bytes from
+ * the client. Used for POST/PUT requests.
+ */
+
+INLINE static int php_roxen_low_read_post(char *buf, uint count_bytes)
+{
+ uint total_read = 0;
+#ifdef ROXEN_USE_ZTS
+ GET_THIS();
+#endif
+ TSRMLS_FETCH();
+
+ if(!MY_FD_OBJ->prog)
+ {
+ PG(connection_status) = PHP_CONNECTION_ABORTED;
+ zend_bailout();
+ return -1;
+ }
+ push_int(count_bytes);
+ safe_apply(MY_FD_OBJ, "read_post", 1);
+ if(Pike_sp[-1].type == PIKE_T_STRING) {
+ MEMCPY(buf, Pike_sp[-1].u.string->str,
+ (total_read = Pike_sp[-1].u.string->len));
+ buf[total_read] = '\0';
+ } else
+ total_read = 0;
+ pop_stack();
+ return total_read;
+}
+
+static int
+php_roxen_sapi_read_post(char *buf, uint count_bytes TSRMLS_DC)
+{
+ uint total_read = 0;
+ THREAD_SAFE_RUN(total_read = php_roxen_low_read_post(buf, count_bytes), "read post");
+ return total_read;
+}
+
+/*
+ * php_roxen_sapi_read_cookies() returns the Cookie header from
+ * the HTTP request header
+ */
+
+static char *
+php_roxen_sapi_read_cookies(TSRMLS_D)
+{
+ char *cookies;
+ cookies = lookup_string_header("HTTP_COOKIE", NULL);
+ return cookies;
+}
+
+static void php_info_roxen(ZEND_MODULE_INFO_FUNC_ARGS)
+{
+ /* char buf[512]; */
+ php_info_print_table_start();
+ php_info_print_table_row(2, "SAPI module version", "$Id$");
+ /* php_info_print_table_row(2, "Build date", Ns_InfoBuildDate());
+ php_info_print_table_row(2, "Config file path", Ns_InfoConfigFile());
+ php_info_print_table_row(2, "Error Log path", Ns_InfoErrorLog());
+ php_info_print_table_row(2, "Installation path", Ns_InfoHomePath());
+ php_info_print_table_row(2, "Hostname of server", Ns_InfoHostname());
+ php_info_print_table_row(2, "Source code label", Ns_InfoLabel());
+ php_info_print_table_row(2, "Server platform", Ns_InfoPlatform());
+ snprintf(buf, 511, "%s/%s", Ns_InfoServerName(), Ns_InfoServerVersion());
+ php_info_print_table_row(2, "Server version", buf);
+ snprintf(buf, 511, "%d day(s), %02d:%02d:%02d",
+ uptime / 86400,
+ (uptime / 3600) % 24,
+ (uptime / 60) % 60,
+ uptime % 60);
+ php_info_print_table_row(2, "Server uptime", buf);
+ */
+ php_info_print_table_end();
+}
+
+static zend_module_entry php_roxen_module = {
+ STANDARD_MODULE_HEADER,
+ "Roxen",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ php_info_roxen,
+ NULL,
+ STANDARD_MODULE_PROPERTIES
+};
+
+static int php_roxen_startup(sapi_module_struct *sapi_module)
+{
+ if(php_module_startup(sapi_module, &php_roxen_module, 1) == FAILURE) {
+ return FAILURE;
+ } else {
+ return SUCCESS;
+ }
+}
+
+/* this structure is static (as in "it does not change") */
+
+static sapi_module_struct roxen_sapi_module = {
+ "roxen",
+ "Roxen",
+ php_roxen_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+ NULL, /* activate */
+ NULL, /* deactivate */
+ php_roxen_sapi_ub_write, /* unbuffered write */
+ NULL, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+ php_error, /* error handler */
+ php_roxen_sapi_header_handler, /* header handler */
+ php_roxen_sapi_send_headers, /* send headers handler */
+ NULL, /* send header handler */
+ php_roxen_sapi_read_post, /* read POST data */
+ php_roxen_sapi_read_cookies, /* read Cookies */
+ NULL, /* register server variables */
+ NULL, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+/*
+ * php_roxen_hash_environment() populates the php script environment
+ * with a number of variables. HTTP_* variables are created for
+ * the HTTP header data, so that a script can access these.
+ */
+#define ADD_STRING(name) \
+ MAKE_STD_ZVAL(zvalue); \
+ zvalue->type = IS_STRING; \
+ zvalue->value.str.len = strlen(buf); \
+ zvalue->value.str.val = estrndup(buf, zvalue->value.str.len); \
+ zend_hash_update(&EG(symbol_table), name, sizeof(name), \
+ &zvalue, sizeof(zval *), NULL)
+
+static void
+php_roxen_hash_environment(TSRMLS_D)
+{
+ int i;
+ char buf[512];
+ zval *zvalue;
+ struct svalue *headers;
+ struct pike_string *sind;
+ struct array *indices;
+ struct svalue *ind, *val;
+#ifdef ROXEN_USE_ZTS
+ GET_THIS();
+#endif
+ sind = make_shared_string("env");
+ headers = low_mapping_string_lookup(REQUEST_DATA, sind);
+ free_string(sind);
+ if(headers && headers->type == PIKE_T_MAPPING) {
+ indices = mapping_indices(headers->u.mapping);
+ for(i = 0; i < indices->size; i++) {
+ ind = &indices->item[i];
+ val = low_mapping_lookup(headers->u.mapping, ind);
+ if(ind && ind->type == PIKE_T_STRING &&
+ val && val->type == PIKE_T_STRING) {
+ int buf_len;
+ buf_len = MIN(511, ind->u.string->len);
+ strncpy(buf, ind->u.string->str, buf_len);
+ buf[buf_len] = '\0'; /* Terminate correctly */
+ MAKE_STD_ZVAL(zvalue);
+ zvalue->type = IS_STRING;
+ zvalue->value.str.len = val->u.string->len;
+ zvalue->value.str.val = estrndup(val->u.string->str, zvalue->value.str.len);
+
+ zend_hash_update(&EG(symbol_table), buf, buf_len + 1, &zvalue, sizeof(zval *), NULL);
+ }
+ }
+ free_array(indices);
+ }
+
+ /*
+ MAKE_STD_ZVAL(zvalue);
+ zvalue->type = IS_LONG;
+ zvalue->value.lval = Ns_InfoBootTime();
+ zend_hash_update(&EG(symbol_table), "SERVER_BOOTTIME", sizeof("SERVER_BOOTTIME"), &zvalue, sizeof(zval *), NULL);
+ */
+}
+
+/*
+ * php_roxen_module_main() is called by the per-request handler and
+ * "executes" the script
+ */
+
+static int php_roxen_module_main(TSRMLS_D)
+{
+ int res, len;
+ char *dir;
+ zend_file_handle file_handle;
+#ifdef ROXEN_USE_ZTS
+ GET_THIS();
+#endif
+
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = THIS->filename;
+ file_handle.free_filename = 0;
+ file_handle.opened_path = NULL;
+
+ THREADS_ALLOW();
+ res = php_request_startup(TSRMLS_C);
+ THREADS_DISALLOW();
+ if(res == FAILURE) {
+ return 0;
+ }
+ php_roxen_hash_environment(TSRMLS_C);
+ THREADS_ALLOW();
+ php_execute_script(&file_handle TSRMLS_CC);
+ php_request_shutdown(NULL);
+ THREADS_DISALLOW();
+ return 1;
+}
+
+/*
+ * The php_roxen_request_handler() is called per request and handles
+ * everything for one request.
+ */
+
+void f_php_roxen_request_handler(INT32 args)
+{
+ struct object *my_fd_obj;
+ struct mapping *request_data;
+ struct svalue *done_callback, *raw_fd;
+ struct pike_string *script, *ind;
+ int status = 1;
+#ifdef ROXEN_USE_ZTS
+ GET_THIS();
+#endif
+ TSRMLS_FETCH();
+
+ if(current_thread == th_self())
+ php_error(E_WARNING, "PHP5.Interpreter->run: Tried to run a PHP-script from a PHP "
+ "callback!");
+ get_all_args("PHP5.Interpreter->run", args, "%S%m%O%*", &script,
+ &request_data, &my_fd_obj, &done_callback);
+ if(done_callback->type != PIKE_T_FUNCTION)
+ php_error(E_WARNING, "PHP5.Interpreter->run: Bad argument 4, expected function.\n");
+ PHP_LOCK(THIS); /* Need to lock here or reusing the same object might cause
+ * problems in changing stuff in that object */
+#ifndef ROXEN_USE_ZTS
+ GET_THIS();
+#endif
+ THIS->request_data = request_data;
+ THIS->my_fd_obj = my_fd_obj;
+ THIS->filename = script->str;
+ current_thread = th_self();
+ SG(request_info).query_string = lookup_string_header("QUERY_STRING", 0);
+ SG(server_context) = (void *)1; /* avoid server_context == NULL */
+
+ /* path_translated is apparently the absolute path to the file, not
+ the translated PATH_INFO
+ */
+ SG(request_info).path_translated =
+ lookup_string_header("SCRIPT_FILENAME", NULL);
+ SG(request_info).request_uri = lookup_string_header("DOCUMENT_URI", NULL);
+ if(!SG(request_info).request_uri)
+ SG(request_info).request_uri = lookup_string_header("SCRIPT_NAME", NULL);
+ SG(request_info).request_method = lookup_string_header("REQUEST_METHOD", "GET");
+ SG(request_info).content_length = lookup_integer_header("HTTP_CONTENT_LENGTH", 0);
+ SG(request_info).content_type = lookup_string_header("HTTP_CONTENT_TYPE", NULL);
+ SG(sapi_headers).http_response_code = 200;
+
+ /* FIXME: Check for auth stuff needs to be fixed... */
+ SG(request_info).auth_user = NULL;
+ SG(request_info).auth_password = NULL;
+
+ ind = make_shared_binary_string("my_fd", 5);
+ raw_fd = low_mapping_string_lookup(THIS->request_data, ind);
+ if(raw_fd && raw_fd->type == PIKE_T_OBJECT)
+ {
+ int fd = fd_from_object(raw_fd->u.object);
+ if(fd == -1)
+ php_error(E_WARNING, "PHP5.Interpreter->run: my_fd object not open or not an FD.\n");
+ THIS->my_fd = fd;
+ } else
+ THIS->my_fd = 0;
+
+ status = php_roxen_module_main(TSRMLS_C);
+ current_thread = -1;
+
+ apply_svalue(done_callback, 0);
+ pop_stack();
+ pop_n_elems(args);
+ push_int(status);
+ PHP_UNLOCK(THIS);
+}
+
+
+/* Clear the object global struct */
+static void clear_struct(struct object *o)
+{
+ MEMSET(Pike_fp->current_storage, 0, sizeof(php_roxen_request));
+}
+
+
+/*
+ * pike_module_init() is called by Pike once at startup
+ *
+ * This functions allocates basic structures
+ */
+
+void pike_module_init( void )
+{
+ if (!roxen_php_initialized) {
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+#ifdef ROXEN_USE_ZTS
+ ts_allocate_id(&roxen_globals_id, sizeof(php_roxen_request), NULL, NULL);
+#endif
+#endif
+ sapi_startup(&roxen_sapi_module);
+ /*php_roxen_startup(&roxen_sapi_module); removed - should be called from SAPI activation*/
+ roxen_php_initialized = 1;
+ PHP_INIT_LOCK();
+ }
+ start_new_program(); /* Text */
+ ADD_STORAGE(php_roxen_request);
+ set_init_callback(clear_struct);
+ pike_add_function("run", f_php_roxen_request_handler,
+ "function(string, mapping, object, function:int)", 0);
+ add_program_constant("Interpreter", (php_program = end_program()), 0);
+}
+
+/*
+ * pike_module_exit() performs the last steps before the
+ * server exists. Shutdowns basic services and frees memory
+ */
+
+void pike_module_exit(void)
+{
+ roxen_php_initialized = 0;
+ roxen_sapi_module.shutdown(&roxen_sapi_module);
+ if(php_program) free_program(php_program);
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ PHP_DESTROY();
+}
+#endif
diff --git a/sapi/tests/test001.phpt b/sapi/tests/test001.phpt
new file mode 100644
index 0000000..a964393
--- /dev/null
+++ b/sapi/tests/test001.phpt
@@ -0,0 +1,16 @@
+--TEST--
+IIS style CGI missing SCRIPT_FILENAME
+--DESCRIPTION--
+This would be similar to what IIS produces for a simple query.
+--ENV--
+return <<<END
+PATH_TRANSLATED=$filename
+PATH_INFO=$scriptname
+SCRIPT_NAME=$scriptname
+END;
+--FILE--
+<?php
+ echo "HELLO";
+?>
+--EXPECT--
+HELLO \ No newline at end of file
diff --git a/sapi/tests/test002.phpt b/sapi/tests/test002.phpt
new file mode 100644
index 0000000..42ade3d
--- /dev/null
+++ b/sapi/tests/test002.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Apache style CGI
+--DESCRIPTION--
+Apache likes to set SCRIPT_FILENAME to the php executable
+if you use ScriptAlias configurations, and the proper
+path is in PATH_TRANSLATED. SCRIPT_NAME in this is faked,
+but that is ok, Apache sets SCRIPT_NAME to the ScriptAlias
+of the executable.
+--ENV--
+return <<<END
+REDIRECT_URL=$scriptname
+PATH_TRANSLATED=$filename
+PATH_INFO=$scriptname
+SCRIPT_NAME=/scriptalias/php
+SCRIPT_FILENAME=$this->conf['TEST_PHP_EXECUTABLE']
+END;
+--FILE--
+<?php
+ echo "HELLO";
+?>
+--EXPECT--
+HELLO \ No newline at end of file
diff --git a/sapi/tests/test003.phpt b/sapi/tests/test003.phpt
new file mode 100644
index 0000000..5cabe66
--- /dev/null
+++ b/sapi/tests/test003.phpt
@@ -0,0 +1,21 @@
+--TEST--
+IIS style CGI missing SCRIPT_FILENAME, has PATH_INFO
+--DESCRIPTION--
+This would be similar to what IIS produces for a simple query
+that also has PATH_INFO.
+--REQUEST--
+return <<<END
+PATH_INFO=/path/info
+END;
+--ENV--
+return <<<END
+PATH_TRANSLATED=/path/bla
+PATH_INFO=/path/info
+SCRIPT_NAME=path
+END;
+--FILE--
+<?php
+ echo $_SERVER['PATH_INFO'];
+?>
+--EXPECT--
+/path/info \ No newline at end of file
diff --git a/sapi/tests/test004.phpt b/sapi/tests/test004.phpt
new file mode 100644
index 0000000..ef43774
--- /dev/null
+++ b/sapi/tests/test004.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Apache style CGI with PATH_INFO
+--DESCRIPTION--
+Apache likes to set SCRIPT_FILENAME to the php executable
+if you use ScriptAlias configurations, and the proper
+path is in PATH_TRANSLATED. SCRIPT_NAME in this is faked,
+but that is ok, Apache sets SCRIPT_NAME to the ScriptAlias
+of the executable.
+--REQUEST--
+return <<<END
+PATH_INFO=/path/info
+END;
+--ENV--
+return <<<END
+REDIRECT_URL=/path
+PATH_TRANSLATED=/path/info/fpp
+PATH_INFO=/path/info
+SCRIPT_NAME=/scriptalias/php
+SCRIPT_FILENAME=$this->conf['TEST_PHP_EXECUTABLE']
+END;
+--FILE--
+<?php
+ echo $_SERVER['PATH_INFO'];
+?>
+--EXPECT--
+/path/info \ No newline at end of file
diff --git a/sapi/tests/test005.phpt b/sapi/tests/test005.phpt
new file mode 100644
index 0000000..7415b66
--- /dev/null
+++ b/sapi/tests/test005.phpt
@@ -0,0 +1,27 @@
+--TEST--
+QUERY_STRING Security Bug
+--DESCRIPTION--
+This bug was present in PHP 4.3.0 only.
+A failure should print HELLO.
+--REQUEST--
+return <<<END
+SCRIPT_NAME=/nothing.php
+QUERY_STRING=$filename
+END;
+--ENV--
+return <<<END
+REDIRECT_URL=$scriptname
+PATH_TRANSLATED=c:\apache\1.3.27\htdocs\nothing.php
+QUERY_STRING=$filename
+PATH_INFO=/nothing.php
+SCRIPT_NAME=/phpexe/php.exe/nothing.php
+SCRIPT_FILENAME=c:\apache\1.3.27\htdocs\nothing.php
+END;
+--FILE--
+<?php
+ echo "HELLO";
+?>
+--EXPECTHEADERS--
+Status: 404
+--EXPECT--
+No input file specified. \ No newline at end of file
diff --git a/sapi/tests/test006.phpt b/sapi/tests/test006.phpt
new file mode 100644
index 0000000..45e3781
--- /dev/null
+++ b/sapi/tests/test006.phpt
@@ -0,0 +1,73 @@
+--TEST--
+Multipart Form POST Data
+--HEADERS--
+return <<<END
+Content-Type=multipart/form-data; boundary=---------------------------240723202011929
+Content-Length=862
+END;
+--ENV--
+return <<<END
+CONTENT_TYPE=multipart/form-data; boundary=---------------------------240723202011929
+CONTENT_LENGTH=862
+END;
+--POST--
+-----------------------------240723202011929
+Content-Disposition: form-data; name="entry"
+
+entry box
+-----------------------------240723202011929
+Content-Disposition: form-data; name="password"
+
+password box
+-----------------------------240723202011929
+Content-Disposition: form-data; name="radio1"
+
+test 1
+-----------------------------240723202011929
+Content-Disposition: form-data; name="checkbox1"
+
+test 1
+-----------------------------240723202011929
+Content-Disposition: form-data; name="choices"
+
+Choice 1
+-----------------------------240723202011929
+Content-Disposition: form-data; name="choices"
+
+Choice 2
+-----------------------------240723202011929
+Content-Disposition: form-data; name="file"; filename="info.php"
+Content-Type: application/octet-stream
+
+<?php
+phpinfo();
+?>
+-----------------------------240723202011929--
+
+--FILE--
+<?php
+error_reporting(0);
+print_r($_POST);
+print_r($_FILES);
+?>
+--EXPECTF--
+Array
+(
+ [entry] => entry box
+ [password] => password box
+ [radio1] => test 1
+ [checkbox1] => test 1
+ [choices] => Choice 2
+)
+Array
+(
+ [file] => Array
+ (
+ [name] => info.php
+ [type] => application/octet-stream
+ [tmp_name] => %s
+ [error] => 0
+ [size] => 21
+ )
+
+)
diff --git a/sapi/tests/test007.phpt b/sapi/tests/test007.phpt
new file mode 100644
index 0000000..8c50e4b
--- /dev/null
+++ b/sapi/tests/test007.phpt
@@ -0,0 +1,46 @@
+--TEST--
+Multipart Form POST Data, incorrect content length
+--HEADERS--
+return <<<END
+Content-Type=multipart/form-data; boundary=---------------------------240723202011929
+Content-Length=100
+END;
+--POST--
+-----------------------------240723202011929
+Content-Disposition: form-data; name="entry"
+
+entry box
+-----------------------------240723202011929
+Content-Disposition: form-data; name="password"
+
+password box
+-----------------------------240723202011929
+Content-Disposition: form-data; name="radio1"
+
+test 1
+-----------------------------240723202011929
+Content-Disposition: form-data; name="checkbox1"
+
+test 1
+-----------------------------240723202011929
+Content-Disposition: form-data; name="choices"
+
+Choice 1
+-----------------------------240723202011929
+Content-Disposition: form-data; name="choices"
+
+Choice 2
+-----------------------------240723202011929
+Content-Disposition: form-data; name="file"; filename="info.php"
+Content-Type: application/octet-stream
+
+<?php
+phpinfo();
+?>
+-----------------------------240723202011929--
+
+--FILE--
+<?php
+print @$_POST['choices'];
+?>
+--EXPECT--
diff --git a/sapi/thttpd/CREDITS b/sapi/thttpd/CREDITS
new file mode 100644
index 0000000..8f02f36
--- /dev/null
+++ b/sapi/thttpd/CREDITS
@@ -0,0 +1,2 @@
+thttpd
+Sascha Schumann
diff --git a/sapi/thttpd/README b/sapi/thttpd/README
new file mode 100644
index 0000000..1e80a01
--- /dev/null
+++ b/sapi/thttpd/README
@@ -0,0 +1,85 @@
+README FOR THTTPD MODULE (by Sascha Schumann)
+($Date$)
+
+ This is a SAPI module for PHP 4.x supporting thttpd, the tiny,
+ turbo, throttling HTTP server by Jef Poskanzer.
+
+ NOTE: All HTTP requests will be serialized. That means, one long running
+ script will block all other requests. Choose another web server,
+ if you want to execute arbitrarily long running scripts.
+
+ The module contains a patch against version 2.21b of thttpd. The patch
+ fixes a number of bugs and adds some functionality:
+
+ - HTTP/1.1 Persistent Connection/Pipeline Support
+ - PHP Scripting (**.php by default)
+ - Highlighting PHP Scripts (**.phps by default)
+ - Fast Accept Loop (unique to PHP)
+ - Periodic Connection Expiring (unique to PHP)
+ - Log to stdout (logfile=-)
+ - Fixes the Host: header vulnerability (affects vhosts only)
+ - Asynchronous request body handling (e.g. for POSTs)
+ - Accept filter for Linux
+ - Fix for non-blocking sending of thttpd-generated responses
+
+ You can configure the filename extensions by creating a config file for
+ thttpd and setting these entries:
+
+ phppat=PATTERN
+ phpspat=PATTERN
+
+ The PATTERN has the same format as defined here:
+
+ http://acme.com/software/thttpd/options.html#CGI_PATTERN
+
+ "**.php" means: match any file ending in .php in any directory.
+ Setting the pattern from the command line is not supported.
+
+ NOTE: This version supports *only* thttpd 2.21b, no prior or later
+ version.
+
+ This is a functional and stable module (it runs a large application
+ like IMP 2.2.0 without any problems). Its original intention was to
+ demonstrate the ability of PHP to work in every web server environment.
+
+REQUIRED DOWNLOADS
+
+ 1. thttpd 2.21b (2.20 or +2.22beta will _not_ work)
+
+ Full Distribution:
+ http://www.acme.com/software/thttpd/thttpd-2.21b.tar.gz
+
+ 2. PHP 4.x
+
+ Download:
+ http://www.php.net/
+
+ Snapshots from CVS:
+ http://snaps.php.net/
+
+
+BUILD INSTRUCTIONS
+
+ 1. Extract software packages
+
+ $ gunzip -c thttpd-2.xx.tar.gz | tar xf -
+ $ gunzip -c php-*.tar.gz | tar xf -
+
+ 2. Prepare PHP
+
+ $ cd php-*
+ $ ./configure \
+ --with-thttpd=../thttpd-2.xx \
+ <further PHP options>
+ $ make install
+ $ cd ..
+
+ You can see the list of valid PHP options by executing
+
+ $ ./configure --help
+
+ 3. Configure, compile, install thttpd
+
+ Now follow the thttpd instructions. The Makefile template of
+ thttpd was changed to automatically use the components
+ required by PHP.
diff --git a/sapi/thttpd/config.m4 b/sapi/thttpd/config.m4
new file mode 100644
index 0000000..371edae
--- /dev/null
+++ b/sapi/thttpd/config.m4
@@ -0,0 +1,39 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(thttpd,,
+[ --with-thttpd=SRCDIR Build PHP as thttpd module], no, no)
+
+AC_MSG_CHECKING([for thttpd])
+
+if test "$PHP_THTTPD" != "no"; then
+ if test ! -d $PHP_THTTPD; then
+ AC_MSG_RESULT(thttpd directory does not exist ($PHP_THTTPD))
+ fi
+
+ PHP_EXPAND_PATH($PHP_THTTPD, THTTPD)
+
+ if grep thttpd.2.21b $PHP_THTTPD/version.h >/dev/null; then
+ patch="test -f $THTTPD/php_patched || \
+ (cd $THTTPD && patch -p1 < $abs_srcdir/sapi/thttpd/thttpd_patch && touch php_patched)"
+
+ elif grep Premium $PHP_THTTPD/version.h >/dev/null; then
+ patch=
+ else
+ AC_MSG_ERROR([This version only supports thttpd-2.21b and Premium thttpd])
+ fi
+ PHP_TARGET_RDYNAMIC
+ INSTALL_IT="\
+ echo 'PHP_LIBS = -L. -lphp5 \$(PHP_LIBS) \$(EXTRA_LIBS)' > $THTTPD/php_makefile; \
+ echo 'PHP_LDFLAGS = \$(NATIVE_RPATHS) \$(PHP_LDFLAGS)' >> $THTTPD/php_makefile; \
+ echo 'PHP_CFLAGS = \$(COMMON_FLAGS) \$(CFLAGS_CLEAN) \$(CPPFLAGS) \$(EXTRA_CFLAGS)' >> $THTTPD/php_makefile; \
+ rm -f $THTTPD/php_thttpd.c $THTTPD/php_thttpd.h $THTTPD/libphp5.a; \
+ \$(LN_S) $abs_srcdir/sapi/thttpd/thttpd.c $THTTPD/php_thttpd.c; \
+ \$(LN_S) $abs_srcdir/sapi/thttpd/php_thttpd.h $abs_builddir/$SAPI_STATIC $THTTPD/;\
+ $patch"
+ PHP_THTTPD="yes, using $THTTPD"
+ PHP_ADD_INCLUDE($THTTPD)
+ PHP_SELECT_SAPI(thttpd, static)
+fi
+AC_MSG_RESULT($PHP_THTTPD)
diff --git a/sapi/thttpd/php.sym b/sapi/thttpd/php.sym
new file mode 100644
index 0000000..2214d39
--- /dev/null
+++ b/sapi/thttpd/php.sym
@@ -0,0 +1,3 @@
+thttpd_php_request
+thttpd_php_init
+thttpd_php_shutdown
diff --git a/sapi/thttpd/php_thttpd.h b/sapi/thttpd/php_thttpd.h
new file mode 100644
index 0000000..40d63d2
--- /dev/null
+++ b/sapi/thttpd/php_thttpd.h
@@ -0,0 +1,35 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_THTTPD_H
+#define PHP_THTTPD_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <libhttpd.h>
+
+void thttpd_php_shutdown(void);
+void thttpd_php_init(void);
+off_t thttpd_php_request(httpd_conn *hc, int show_source);
+
+void thttpd_register_on_close(void (*)(int));
+void thttpd_closed_conn(int fd);
+int thttpd_get_fd(void);
+void thttpd_set_dont_close(void);
+
+#endif
diff --git a/sapi/thttpd/stub.c b/sapi/thttpd/stub.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sapi/thttpd/stub.c
diff --git a/sapi/thttpd/thttpd.c b/sapi/thttpd/thttpd.c
new file mode 100644
index 0000000..1a1baa7
--- /dev/null
+++ b/sapi/thttpd/thttpd.c
@@ -0,0 +1,772 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include "php.h"
+#include "SAPI.h"
+#include "php_main.h"
+#include "php_thttpd.h"
+#include "php_variables.h"
+#include "version.h"
+#include "php_ini.h"
+#include "zend_highlight.h"
+
+#include "ext/standard/php_smart_str.h"
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef HAVE_GETNAMEINFO
+#include <sys/socket.h>
+#include <netdb.h>
+#endif
+
+typedef struct {
+ httpd_conn *hc;
+ void (*on_close)(int);
+
+ size_t unconsumed_length;
+ smart_str sbuf;
+ int seen_cl;
+ int seen_cn;
+} php_thttpd_globals;
+
+#define PHP_SYS_CALL(x) do { x } while (n == -1 && errno == EINTR)
+
+#ifdef PREMIUM_THTTPD
+# define do_keep_alive persistent
+#endif
+
+#ifdef ZTS
+static int thttpd_globals_id;
+#define TG(v) TSRMG(thttpd_globals_id, php_thttpd_globals *, v)
+#else
+static php_thttpd_globals thttpd_globals;
+#define TG(v) (thttpd_globals.v)
+#endif
+
+static int sapi_thttpd_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ int n;
+ uint sent = 0;
+
+ if (TG(sbuf).c != 0) {
+ smart_str_appendl_ex(&TG(sbuf), str, str_length, 1);
+ return str_length;
+ }
+
+ while (str_length > 0) {
+ PHP_SYS_CALL(n = send(TG(hc)->conn_fd, str, str_length, 0););
+
+ if (n == -1) {
+ if (errno == EAGAIN) {
+ smart_str_appendl_ex(&TG(sbuf), str, str_length, 1);
+
+ return sent + str_length;
+ } else
+ php_handle_aborted_connection();
+ }
+
+ TG(hc)->bytes_sent += n;
+ str += n;
+ sent += n;
+ str_length -= n;
+ }
+
+ return sent;
+}
+
+#define COMBINE_HEADERS 64
+
+#if defined(IOV_MAX)
+# if IOV_MAX - 64 <= 0
+# define SERIALIZE_HEADERS
+# endif
+#endif
+
+static int do_writev(struct iovec *vec, int nvec, int len TSRMLS_DC)
+{
+ int n;
+
+ assert(nvec <= IOV_MAX);
+
+ if (TG(sbuf).c == 0) {
+ PHP_SYS_CALL(n = writev(TG(hc)->conn_fd, vec, nvec););
+
+ if (n == -1) {
+ if (errno == EAGAIN) {
+ n = 0;
+ } else {
+ php_handle_aborted_connection();
+ }
+ }
+
+
+ TG(hc)->bytes_sent += n;
+ } else {
+ n = 0;
+ }
+
+ if (n < len) {
+ int i;
+
+ /* merge all unwritten data into sbuf */
+ for (i = 0; i < nvec; vec++, i++) {
+ /* has this vector been written completely? */
+ if (n >= vec->iov_len) {
+ /* yes, proceed */
+ n -= vec->iov_len;
+ continue;
+ }
+
+ if (n > 0) {
+ /* adjust vector */
+ vec->iov_base = (char *) vec->iov_base + n;
+ vec->iov_len -= n;
+ n = 0;
+ }
+
+ smart_str_appendl_ex(&TG(sbuf), vec->iov_base, vec->iov_len, 1);
+ }
+ }
+
+ return 0;
+}
+
+#ifdef SERIALIZE_HEADERS
+# define ADD_VEC(str,l) smart_str_appendl(&vec_str, (str), (l))
+# define VEC_BASE() smart_str vec_str = {0}
+# define VEC_FREE() smart_str_free(&vec_str)
+#else
+# define ADD_VEC(str,l) vec[n].iov_base=str;len += (vec[n].iov_len=l); n++
+# define VEC_BASE() struct iovec vec[COMBINE_HEADERS]
+# define VEC_FREE() do {} while (0)
+#endif
+
+#define ADD_VEC_S(str) ADD_VEC((str), sizeof(str)-1)
+
+#define CL_TOKEN "Content-length: "
+#define CN_TOKEN "Connection: "
+#define KA_DO "Connection: keep-alive\r\n"
+#define KA_NO "Connection: close\r\n"
+#define DEF_CT "Content-Type: text/html\r\n"
+
+static int sapi_thttpd_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
+{
+ char buf[1024], *p;
+ VEC_BASE();
+ int n = 0;
+ zend_llist_position pos;
+ sapi_header_struct *h;
+ size_t len = 0;
+
+ if (!SG(sapi_headers).http_status_line) {
+ ADD_VEC_S("HTTP/1.1 ");
+ p = smart_str_print_long(buf+sizeof(buf)-1,
+ SG(sapi_headers).http_response_code);
+ ADD_VEC(p, strlen(p));
+ ADD_VEC_S(" HTTP\r\n");
+ } else {
+ ADD_VEC(SG(sapi_headers).http_status_line,
+ strlen(SG(sapi_headers).http_status_line));
+ ADD_VEC("\r\n", 2);
+ }
+ TG(hc)->status = SG(sapi_headers).http_response_code;
+
+ if (SG(sapi_headers).send_default_content_type) {
+ ADD_VEC(DEF_CT, strlen(DEF_CT));
+ }
+
+ h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+
+ switch (h->header[0]) {
+ case 'c': case 'C':
+ if (!TG(seen_cl) && strncasecmp(h->header, CL_TOKEN, sizeof(CL_TOKEN)-1) == 0) {
+ TG(seen_cl) = 1;
+ } else if (!TG(seen_cn) && strncasecmp(h->header, CN_TOKEN, sizeof(CN_TOKEN)-1) == 0) {
+ TG(seen_cn) = 1;
+ }
+ }
+
+ ADD_VEC(h->header, h->header_len);
+#ifndef SERIALIZE_HEADERS
+ if (n >= COMBINE_HEADERS - 1) {
+ len = do_writev(vec, n, len TSRMLS_CC);
+ n = 0;
+ }
+#endif
+ ADD_VEC("\r\n", 2);
+
+ h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ }
+
+ if (TG(seen_cl) && !TG(seen_cn) && TG(hc)->do_keep_alive) {
+ ADD_VEC(KA_DO, sizeof(KA_DO)-1);
+ } else {
+ TG(hc)->do_keep_alive = 0;
+ ADD_VEC(KA_NO, sizeof(KA_NO)-1);
+ }
+
+ ADD_VEC("\r\n", 2);
+
+#ifdef SERIALIZE_HEADERS
+ sapi_thttpd_ub_write(vec_str.c, vec_str.len TSRMLS_CC);
+#else
+ do_writev(vec, n, len TSRMLS_CC);
+#endif
+
+ VEC_FREE();
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+/* to understand this, read cgi_interpose_input() in libhttpd.c */
+#define SIZEOF_UNCONSUMED_BYTES() (TG(hc)->read_idx - TG(hc)->checked_idx)
+#define CONSUME_BYTES(n) do { TG(hc)->checked_idx += (n); } while (0)
+
+
+static int sapi_thttpd_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+{
+ size_t read_bytes = 0;
+
+ if (TG(unconsumed_length) > 0) {
+ read_bytes = MIN(TG(unconsumed_length), count_bytes);
+ memcpy(buffer, TG(hc)->read_buf + TG(hc)->checked_idx, read_bytes);
+ TG(unconsumed_length) -= read_bytes;
+ CONSUME_BYTES(read_bytes);
+ }
+
+ return read_bytes;
+}
+
+static char *sapi_thttpd_read_cookies(TSRMLS_D)
+{
+ return TG(hc)->cookie;
+}
+
+#define BUF_SIZE 512
+#define ADD_STRING_EX(name,buf) \
+ php_register_variable(name, buf, track_vars_array TSRMLS_CC)
+#define ADD_STRING(name) ADD_STRING_EX((name), buf)
+
+static void sapi_thttpd_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ char buf[BUF_SIZE + 1];
+ char *p;
+
+ php_register_variable("PHP_SELF", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
+ php_register_variable("SERVER_SOFTWARE", SERVER_SOFTWARE, track_vars_array TSRMLS_CC);
+ php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC);
+ php_register_variable("REQUEST_METHOD", (char *) SG(request_info).request_method, track_vars_array TSRMLS_CC);
+ php_register_variable("REQUEST_URI", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
+ php_register_variable("PATH_TRANSLATED", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
+
+ if (TG(hc)->one_one) {
+ php_register_variable("SERVER_PROTOCOL", "HTTP/1.1", track_vars_array TSRMLS_CC);
+ } else {
+ php_register_variable("SERVER_PROTOCOL", "HTTP/1.0", track_vars_array TSRMLS_CC);
+ }
+
+ p = httpd_ntoa(&TG(hc)->client_addr);
+
+ ADD_STRING_EX("REMOTE_ADDR", p);
+ ADD_STRING_EX("REMOTE_HOST", p);
+
+ ADD_STRING_EX("SERVER_PORT",
+ smart_str_print_long(buf + sizeof(buf) - 1,
+ TG(hc)->hs->port));
+
+ buf[0] = '/';
+ memcpy(buf + 1, TG(hc)->pathinfo, strlen(TG(hc)->pathinfo) + 1);
+ ADD_STRING("PATH_INFO");
+
+ buf[0] = '/';
+ memcpy(buf + 1, TG(hc)->origfilename, strlen(TG(hc)->origfilename) + 1);
+ ADD_STRING("SCRIPT_NAME");
+
+#define CONDADD(name, field) \
+ if (TG(hc)->field[0]) { \
+ php_register_variable(#name, TG(hc)->field, track_vars_array TSRMLS_CC); \
+ }
+
+ CONDADD(QUERY_STRING, query);
+ CONDADD(HTTP_HOST, hdrhost);
+ CONDADD(HTTP_REFERER, referer);
+ CONDADD(HTTP_USER_AGENT, useragent);
+ CONDADD(HTTP_ACCEPT, accept);
+ CONDADD(HTTP_ACCEPT_LANGUAGE, acceptl);
+ CONDADD(HTTP_ACCEPT_ENCODING, accepte);
+ CONDADD(HTTP_COOKIE, cookie);
+ CONDADD(CONTENT_TYPE, contenttype);
+ CONDADD(REMOTE_USER, remoteuser);
+ CONDADD(SERVER_PROTOCOL, protocol);
+
+ if (TG(hc)->contentlength != -1) {
+ ADD_STRING_EX("CONTENT_LENGTH",
+ smart_str_print_long(buf + sizeof(buf) - 1,
+ TG(hc)->contentlength));
+ }
+
+ if (TG(hc)->authorization[0])
+ php_register_variable("AUTH_TYPE", "Basic", track_vars_array TSRMLS_CC);
+}
+
+static PHP_MINIT_FUNCTION(thttpd)
+{
+ return SUCCESS;
+}
+
+static zend_module_entry php_thttpd_module = {
+ STANDARD_MODULE_HEADER,
+ "thttpd",
+ NULL,
+ PHP_MINIT(thttpd),
+ NULL,
+ NULL,
+ NULL,
+ NULL, /* info */
+ NULL,
+ STANDARD_MODULE_PROPERTIES
+};
+
+static int php_thttpd_startup(sapi_module_struct *sapi_module)
+{
+#if PHP_API_VERSION >= 20020918
+ if (php_module_startup(sapi_module, &php_thttpd_module, 1) == FAILURE) {
+#else
+ if (php_module_startup(sapi_module) == FAILURE
+ || zend_startup_module(&php_thttpd_module) == FAILURE) {
+#endif
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static int sapi_thttpd_get_fd(int *nfd TSRMLS_DC)
+{
+ if (nfd) *nfd = TG(hc)->conn_fd;
+ return SUCCESS;
+}
+
+static sapi_module_struct thttpd_sapi_module = {
+ "thttpd",
+ "thttpd",
+
+ php_thttpd_startup,
+ php_module_shutdown_wrapper,
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ sapi_thttpd_ub_write,
+ NULL,
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error,
+
+ NULL,
+ sapi_thttpd_send_headers,
+ NULL,
+ sapi_thttpd_read_post,
+ sapi_thttpd_read_cookies,
+
+ sapi_thttpd_register_variables,
+ NULL, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ NULL, /* php.ini path override */
+ NULL, /* Block interruptions */
+ NULL, /* Unblock interruptions */
+
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ sapi_thttpd_get_fd
+};
+
+static void thttpd_module_main(int show_source TSRMLS_DC)
+{
+ zend_file_handle file_handle;
+
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ return;
+ }
+
+ if (show_source) {
+ zend_syntax_highlighter_ini syntax_highlighter_ini;
+
+ php_get_highlight_struct(&syntax_highlighter_ini);
+ highlight_file(SG(request_info).path_translated, &syntax_highlighter_ini TSRMLS_CC);
+ } else {
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.free_filename = 0;
+ file_handle.opened_path = NULL;
+
+ php_execute_script(&file_handle TSRMLS_CC);
+ }
+
+ php_request_shutdown(NULL);
+}
+
+static void thttpd_request_ctor(TSRMLS_D)
+{
+ smart_str s = {0};
+
+ TG(seen_cl) = 0;
+ TG(seen_cn) = 0;
+ TG(sbuf).c = 0;
+ SG(request_info).query_string = TG(hc)->query?strdup(TG(hc)->query):NULL;
+
+ smart_str_appends_ex(&s, TG(hc)->hs->cwd, 1);
+ smart_str_appends_ex(&s, TG(hc)->expnfilename, 1);
+ smart_str_0(&s);
+ SG(request_info).path_translated = s.c;
+
+ s.c = NULL;
+ smart_str_appendc_ex(&s, '/', 1);
+ smart_str_appends_ex(&s, TG(hc)->origfilename, 1);
+ smart_str_0(&s);
+ SG(request_info).request_uri = s.c;
+ SG(request_info).request_method = httpd_method_str(TG(hc)->method);
+ if (TG(hc)->one_one) SG(request_info).proto_num = 1001;
+ else SG(request_info).proto_num = 1000;
+ SG(sapi_headers).http_response_code = 200;
+ if (TG(hc)->contenttype)
+ SG(request_info).content_type = strdup(TG(hc)->contenttype);
+ SG(request_info).content_length = TG(hc)->contentlength == -1 ? 0
+ : TG(hc)->contentlength;
+
+ TG(unconsumed_length) = SG(request_info).content_length;
+
+ php_handle_auth_data(TG(hc)->authorization TSRMLS_CC);
+}
+
+static void thttpd_request_dtor(TSRMLS_D)
+{
+ smart_str_free_ex(&TG(sbuf), 1);
+ if (SG(request_info).query_string)
+ free(SG(request_info).query_string);
+ free(SG(request_info).request_uri);
+ free(SG(request_info).path_translated);
+ if (SG(request_info).content_type)
+ free(SG(request_info).content_type);
+}
+
+#ifdef ZTS
+
+#ifdef TSRM_ST
+#define thread_create_simple_detached(n) st_thread_create(n, NULL, 0, 0)
+#define thread_usleep(n) st_usleep(n)
+#define thread_exit() st_thread_exit(NULL)
+/* No preemption, simple operations are safe */
+#define thread_atomic_inc(n) (++n)
+#define thread_atomic_dec(n) (--n)
+#else
+#error No thread primitives available
+#endif
+
+/* We might want to replace this with a STAILQ */
+typedef struct qreq {
+ httpd_conn *hc;
+ struct qreq *next;
+} qreq_t;
+
+static MUTEX_T qr_lock;
+static qreq_t *queued_requests;
+static qreq_t *last_qr;
+static int nr_free_threads;
+static int nr_threads;
+static int max_threads = 50;
+
+#define HANDLE_STRINGS() { \
+ HANDLE_STR(encodedurl); \
+ HANDLE_STR(decodedurl); \
+ HANDLE_STR(origfilename); \
+ HANDLE_STR(expnfilename); \
+ HANDLE_STR(pathinfo); \
+ HANDLE_STR(query); \
+ HANDLE_STR(referer); \
+ HANDLE_STR(useragent); \
+ HANDLE_STR(accept); \
+ HANDLE_STR(accepte); \
+ HANDLE_STR(acceptl); \
+ HANDLE_STR(cookie); \
+ HANDLE_STR(contenttype); \
+ HANDLE_STR(authorization); \
+ HANDLE_STR(remoteuser); \
+ }
+
+static httpd_conn *duplicate_conn(httpd_conn *hc, httpd_conn *nhc)
+{
+ memcpy(nhc, hc, sizeof(*nhc));
+
+#define HANDLE_STR(m) nhc->m = nhc->m ? strdup(nhc->m) : NULL
+ HANDLE_STRINGS();
+#undef HANDLE_STR
+
+ return nhc;
+}
+
+static void destroy_conn(httpd_conn *hc)
+{
+#define HANDLE_STR(m) if (hc->m) free(hc->m)
+ HANDLE_STRINGS();
+#undef HANDLE_STR
+}
+
+static httpd_conn *dequeue_request(void)
+{
+ httpd_conn *ret = NULL;
+ qreq_t *m;
+
+ tsrm_mutex_lock(qr_lock);
+ if (queued_requests) {
+ m = queued_requests;
+ ret = m->hc;
+ if (!(queued_requests = m->next))
+ last_qr = NULL;
+ free(m);
+ }
+ tsrm_mutex_unlock(qr_lock);
+
+ return ret;
+}
+
+static void *worker_thread(void *);
+
+static void queue_request(httpd_conn *hc)
+{
+ qreq_t *m;
+ httpd_conn *nhc;
+
+ /* Mark as long-running request */
+ hc->file_address = (char *) 1;
+
+ /*
+ * We cannot synchronously revoke accesses to hc in the worker
+ * thread, so we need to pass a copy of hc to the worker thread.
+ */
+ nhc = malloc(sizeof *nhc);
+ duplicate_conn(hc, nhc);
+
+ /* Allocate request queue container */
+ m = malloc(sizeof *m);
+ m->hc = nhc;
+ m->next = NULL;
+
+ tsrm_mutex_lock(qr_lock);
+ /* Create new threads when reaching a certain threshhold */
+ if (nr_threads < max_threads && nr_free_threads < 2) {
+ nr_threads++; /* protected by qr_lock */
+
+ thread_atomic_inc(nr_free_threads);
+ thread_create_simple_detached(worker_thread);
+ }
+ /* Insert container into request queue */
+ if (queued_requests)
+ last_qr->next = m;
+ else
+ queued_requests = m;
+ last_qr = m;
+ tsrm_mutex_unlock(qr_lock);
+}
+
+static off_t thttpd_real_php_request(httpd_conn *hc, int TSRMLS_DC);
+
+static void *worker_thread(void *dummy)
+{
+ int do_work = 50;
+ httpd_conn *hc;
+
+ while (do_work) {
+ hc = dequeue_request();
+
+ if (!hc) {
+/* do_work--; */
+ thread_usleep(500000);
+ continue;
+ }
+/* do_work = 50; */
+
+ thread_atomic_dec(nr_free_threads);
+
+ thttpd_real_php_request(hc, 0 TSRMLS_CC);
+ shutdown(hc->conn_fd, 0);
+ destroy_conn(hc);
+ free(hc);
+
+ thread_atomic_inc(nr_free_threads);
+ }
+ thread_atomic_dec(nr_free_threads);
+ thread_atomic_dec(nr_threads);
+ thread_exit();
+}
+
+static void remove_dead_conn(int fd)
+{
+ qreq_t *m, *prev = NULL;
+
+ tsrm_mutex_lock(qr_lock);
+ m = queued_requests;
+ while (m) {
+ if (m->hc->conn_fd == fd) {
+ if (prev)
+ if (!(prev->next = m->next))
+ last_qr = prev;
+ else
+ if (!(queued_requests = m->next))
+ last_qr = NULL;
+ destroy_conn(m->hc);
+ free(m->hc);
+ free(m);
+ break;
+ }
+ prev = m;
+ m = m->next;
+ }
+ tsrm_mutex_unlock(qr_lock);
+}
+
+#endif
+
+static off_t thttpd_real_php_request(httpd_conn *hc, int show_source TSRMLS_DC)
+{
+ TG(hc) = hc;
+ hc->bytes_sent = 0;
+
+ if (hc->contentlength != -1) {
+ hc->should_linger = 1;
+ hc->do_keep_alive = 0;
+ }
+
+ if (hc->contentlength != -1
+ && SIZEOF_UNCONSUMED_BYTES() < hc->contentlength) {
+ hc->read_body_into_mem = 1;
+ return 0;
+ }
+
+ thttpd_request_ctor(TSRMLS_C);
+
+ thttpd_module_main(show_source TSRMLS_CC);
+
+ /* disable kl, if no content-length was seen or Connection: was set */
+ if (TG(seen_cl) == 0 || TG(seen_cn) == 1) {
+ TG(hc)->do_keep_alive = 0;
+ }
+
+ if (TG(sbuf).c != 0) {
+ if (TG(hc)->response)
+ free(TG(hc)->response);
+
+ TG(hc)->response = TG(sbuf).c;
+ TG(hc)->responselen = TG(sbuf).len;
+ TG(hc)->maxresponse = TG(sbuf).a;
+
+ TG(sbuf).c = 0;
+ TG(sbuf).len = 0;
+ TG(sbuf).a = 0;
+ }
+
+ thttpd_request_dtor(TSRMLS_C);
+
+ return 0;
+}
+
+off_t thttpd_php_request(httpd_conn *hc, int show_source)
+{
+#ifdef ZTS
+ queue_request(hc);
+#else
+ TSRMLS_FETCH();
+ return thttpd_real_php_request(hc, show_source TSRMLS_CC);
+#endif
+}
+
+void thttpd_register_on_close(void (*arg)(int))
+{
+ TSRMLS_FETCH();
+ TG(on_close) = arg;
+}
+
+void thttpd_closed_conn(int fd)
+{
+ TSRMLS_FETCH();
+ if (TG(on_close)) TG(on_close)(fd);
+}
+
+int thttpd_get_fd(void)
+{
+ TSRMLS_FETCH();
+ return TG(hc)->conn_fd;
+}
+
+void thttpd_set_dont_close(void)
+{
+ TSRMLS_FETCH();
+#ifndef PREMIUM_THTTPD
+ TG(hc)->file_address = (char *) 1;
+#endif
+}
+
+
+void thttpd_php_init(void)
+{
+ char *ini;
+
+#ifdef ZTS
+ tsrm_startup(1, 1, 0, NULL);
+ ts_allocate_id(&thttpd_globals_id, sizeof(php_thttpd_globals), NULL, NULL);
+ qr_lock = tsrm_mutex_alloc();
+ thttpd_register_on_close(remove_dead_conn);
+#endif
+
+ if ((ini = getenv("PHP_INI_PATH"))) {
+ thttpd_sapi_module.php_ini_path_override = ini;
+ }
+
+ sapi_startup(&thttpd_sapi_module);
+ thttpd_sapi_module.startup(&thttpd_sapi_module);
+
+ {
+ TSRMLS_FETCH();
+
+ SG(server_context) = (void *) 1;
+ }
+}
+
+void thttpd_php_shutdown(void)
+{
+ TSRMLS_FETCH();
+
+ if (SG(server_context) != NULL) {
+ thttpd_sapi_module.shutdown(&thttpd_sapi_module);
+ sapi_shutdown();
+#ifdef ZTS
+ tsrm_shutdown();
+#endif
+ }
+}
diff --git a/sapi/thttpd/thttpd_patch b/sapi/thttpd/thttpd_patch
new file mode 100644
index 0000000..33de921
--- /dev/null
+++ b/sapi/thttpd/thttpd_patch
@@ -0,0 +1,2377 @@
+diff -ur thttpd-2.21b/Makefile.in thttpd-2.21b-cool/Makefile.in
+--- thttpd-2.21b/Makefile.in Thu Mar 29 20:36:21 2001
++++ thttpd-2.21b-cool/Makefile.in Sat Sep 20 14:43:20 2003
+@@ -46,13 +46,15 @@
+
+ # You shouldn't need to edit anything below here.
+
++include php_makefile
++
+ CC = @CC@
+ CCOPT = @V_CCOPT@
+ DEFS = @DEFS@
+-INCLS = -I.
++INCLS = -I. $(PHP_CFLAGS)
+ CFLAGS = $(CCOPT) $(DEFS) $(INCLS)
+-LDFLAGS = @LDFLAGS@
+-LIBS = @LIBS@
++LDFLAGS = @LDFLAGS@ $(PHP_LDFLAGS)
++LIBS = @LIBS@ $(PHP_LIBS)
+ NETLIBS = @V_NETLIBS@
+ INSTALL = @INSTALL@
+
+@@ -62,7 +64,7 @@
+ @rm -f $@
+ $(CC) $(CFLAGS) -c $*.c
+
+-SRC = thttpd.c libhttpd.c fdwatch.c mmc.c timers.c match.c tdate_parse.c syslog.c
++SRC = thttpd.c libhttpd.c fdwatch.c mmc.c timers.c match.c tdate_parse.c syslog.c php_thttpd.c
+
+ OBJ = $(SRC:.c=.o) @LIBOBJS@
+
+@@ -77,7 +79,7 @@
+ all: this subdirs
+ this: $(ALL)
+
+-thttpd: $(OBJ)
++thttpd: $(OBJ) libphp5.a
+ @rm -f $@
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) $(NETLIBS)
+
+diff -ur thttpd-2.21b/config.h thttpd-2.21b-cool/config.h
+--- thttpd-2.21b/config.h Mon Apr 9 23:57:36 2001
++++ thttpd-2.21b-cool/config.h Sat Sep 20 14:43:20 2003
+@@ -82,6 +82,11 @@
+ */
+ #define IDLE_READ_TIMELIMIT 60
+
++/* CONFIGURE: How many seconds to allow for reading the subsequent requests
++** on a keep-alive connection. Should be simiar to LINGER_TIME
++*/
++#define IDLE_KEEPALIVE_TIMELIMIT 2
++
+ /* CONFIGURE: How many seconds before an idle connection gets closed.
+ */
+ #define IDLE_SEND_TIMELIMIT 300
+@@ -316,7 +321,7 @@
+ /* CONFIGURE: A list of index filenames to check. The files are searched
+ ** for in this order.
+ */
+-#define INDEX_NAMES "index.html", "index.htm", "Default.htm", "index.cgi"
++#define INDEX_NAMES "index.php", "index.html", "index.htm", "Default.htm", "index.cgi"
+
+ /* CONFIGURE: If this is defined then thttpd will automatically generate
+ ** index pages for directories that don't have an explicit index file.
+diff -ur thttpd-2.21b/configure thttpd-2.21b-cool/configure
+--- thttpd-2.21b/configure Sat Apr 21 02:07:14 2001
++++ thttpd-2.21b-cool/configure Sat Sep 20 14:43:20 2003
+@@ -1021,7 +1021,7 @@
+ fi
+ echo "$ac_t""$CPP" 1>&6
+
+-for ac_hdr in fcntl.h grp.h memory.h paths.h poll.h sys/poll.h sys/event.h osreldate.h
++for ac_hdr in fcntl.h grp.h memory.h paths.h poll.h sys/poll.h sys/event.h osreldate.h netinet/tcp.h
+ do
+ ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+ echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+diff -ur thttpd-2.21b/configure.in thttpd-2.21b-cool/configure.in
+--- thttpd-2.21b/configure.in Sat Apr 21 02:06:23 2001
++++ thttpd-2.21b-cool/configure.in Sat Sep 20 14:43:20 2003
+@@ -64,7 +64,7 @@
+ AC_MSG_RESULT(no)
+ fi
+
+-AC_CHECK_HEADERS(fcntl.h grp.h memory.h paths.h poll.h sys/poll.h sys/event.h osreldate.h)
++AC_CHECK_HEADERS(fcntl.h grp.h memory.h paths.h poll.h sys/poll.h sys/event.h osreldate.h netinet/tcp.h)
+ AC_HEADER_TIME
+ AC_HEADER_DIRENT
+
+diff -ur thttpd-2.21b/fdwatch.c thttpd-2.21b-cool/fdwatch.c
+--- thttpd-2.21b/fdwatch.c Fri Apr 13 07:36:08 2001
++++ thttpd-2.21b-cool/fdwatch.c Sat Sep 20 14:43:20 2003
+@@ -419,6 +419,7 @@
+ if ( pollfds == (struct pollfd*) 0 || poll_fdidx == (int*) 0 ||
+ poll_rfdidx == (int*) 0 )
+ return -1;
++ memset(pollfds, 0, sizeof(struct pollfd) * nfiles);
+ return 0;
+ }
+
+@@ -460,7 +461,7 @@
+
+ ridx = 0;
+ for ( i = 0; i < npollfds; ++i )
+- if ( pollfds[i].revents & ( POLLIN | POLLOUT ) )
++ if ( pollfds[i].revents & ( POLLIN | POLLOUT | POLLERR | POLLHUP | POLLNVAL ) )
+ poll_rfdidx[ridx++] = pollfds[i].fd;
+
+ return r;
+@@ -472,8 +473,8 @@
+ {
+ switch ( fd_rw[fd] )
+ {
+- case FDW_READ: return pollfds[poll_fdidx[fd]].revents & POLLIN;
+- case FDW_WRITE: return pollfds[poll_fdidx[fd]].revents & POLLOUT;
++ case FDW_READ: return pollfds[poll_fdidx[fd]].revents & ( POLLIN | POLLERR | POLLHUP | POLLNVAL );
++ case FDW_WRITE: return pollfds[poll_fdidx[fd]].revents & ( POLLOUT | POLLERR | POLLHUP | POLLNVAL );
+ default: return 0;
+ }
+ }
+diff -ur thttpd-2.21b/libhttpd.c thttpd-2.21b-cool/libhttpd.c
+--- thttpd-2.21b/libhttpd.c Tue Apr 24 00:42:40 2001
++++ thttpd-2.21b-cool/libhttpd.c Sat Sep 20 14:43:29 2003
+@@ -56,6 +56,10 @@
+ #include <unistd.h>
+ #include <stdarg.h>
+
++#ifdef HAVE_NETINET_TCP_H
++#include <netinet/tcp.h>
++#endif
++
+ #ifdef HAVE_OSRELDATE_H
+ #include <osreldate.h>
+ #endif /* HAVE_OSRELDATE_H */
+@@ -85,6 +89,12 @@
+ #include "match.h"
+ #include "tdate_parse.h"
+
++#include "php_thttpd.h"
++
++#ifdef __CYGWIN__
++# define timezone _timezone
++#endif
++
+ #ifndef STDIN_FILENO
+ #define STDIN_FILENO 0
+ #endif
+@@ -111,7 +121,7 @@
+ static int initialize_listen_socket( httpd_sockaddr* saP );
+ static void unlisten( httpd_server* hs );
+ static void add_response( httpd_conn* hc, char* str );
+-static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod );
++static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod, const char *, size_t );
+ static void send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg );
+ static void send_response_tail( httpd_conn* hc );
+ static void defang( char* str, char* dfstr, int dfsize );
+@@ -242,6 +252,10 @@
+ free( (void*) hs->cwd );
+ if ( hs->cgi_pattern != (char*) 0 )
+ free( (void*) hs->cgi_pattern );
++ if ( hs->php_pattern != (char*) 0 )
++ free( (void*) hs->php_pattern );
++ if ( hs->phps_pattern != (char*) 0 )
++ free( (void*) hs->phps_pattern );
+ if ( hs->charset != (char*) 0 )
+ free( (void*) hs->charset );
+ if ( hs->url_pattern != (char*) 0 )
+@@ -249,6 +263,7 @@
+ if ( hs->local_pattern != (char*) 0 )
+ free( (void*) hs->local_pattern );
+ free( (void*) hs );
++ thttpd_php_shutdown();
+ }
+
+
+@@ -257,7 +272,8 @@
+ char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P, int port,
+ char* cgi_pattern, char* charset, char* cwd, int no_log, FILE* logfp,
+ int no_symlink, int vhost, int global_passwd, char* url_pattern,
+- char* local_pattern, int no_empty_referers )
++ char* local_pattern, int no_empty_referers, char* php_pattern,
++ char* phps_pattern )
+ {
+ httpd_server* hs;
+ static char ghnbuf[256];
+@@ -312,6 +328,8 @@
+ }
+
+ hs->port = port;
++ hs->php_pattern = strdup(php_pattern);
++ hs->phps_pattern = strdup(phps_pattern);
+ if ( cgi_pattern == (char*) 0 )
+ hs->cgi_pattern = (char*) 0;
+ else
+@@ -329,7 +347,7 @@
+ while ( ( cp = strstr( hs->cgi_pattern, "|/" ) ) != (char*) 0 )
+ (void) strcpy( cp + 1, cp + 2 );
+ }
+- hs->charset = strdup( charset );
++ hs->charset = strdup( charset );
+ hs->cwd = strdup( cwd );
+ if ( hs->cwd == (char*) 0 )
+ {
+@@ -385,6 +403,8 @@
+ return (httpd_server*) 0;
+ }
+
++ thttpd_php_init();
++
+ /* Done initializing. */
+ if ( hs->binding_hostname == (char*) 0 )
+ syslog( LOG_INFO, "%.80s starting on port %d", SERVER_SOFTWARE, hs->port );
+@@ -418,6 +438,11 @@
+ }
+ (void) fcntl( listen_fd, F_SETFD, 1 );
+
++#if defined(TCP_DEFER_ACCEPT) && defined(SOL_TCP)
++ on = 30; /* give clients 30s to send first data packet */
++ setsockopt(listen_fd, SOL_TCP, TCP_DEFER_ACCEPT, &on, sizeof(on));
++#endif
++
+ /* Allow reuse of local addresses. */
+ on = 1;
+ if ( setsockopt(
+@@ -582,6 +607,9 @@
+ /* And send it, if necessary. */
+ if ( hc->responselen > 0 )
+ {
++/*
++printf("**RESPONSE [%d]** len = %d\n%*.*s\n", hc->conn_fd, hc->responselen, hc->responselen, hc->responselen, hc->response);
++*/
+ (void) write( hc->conn_fd, hc->response, hc->responselen );
+ hc->responselen = 0;
+ }
+@@ -619,18 +647,22 @@
+ }
+ }
+
++extern time_t httpd_time_now;
++extern char httpd_now_buf[];
++
++#define SMART_STR_USE_REALLOC
++
++#include "ext/standard/php_smart_str.h"
+
+ static void
+-send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod )
++send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod, const char *last_modified, size_t last_modified_len)
+ {
+- time_t now;
+ const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";
+- char nowbuf[100];
+ char modbuf[100];
+- char fixed_type[500];
+- char buf[1000];
+ int partial_content;
+-
++ smart_str s = {0};
++ int type_len;
++
+ hc->status = status;
+ hc->bytes_to_send = length;
+ if ( hc->mime_flag )
+@@ -649,41 +681,89 @@
+ else
+ partial_content = 0;
+
+- now = time( (time_t*) 0 );
+ if ( mod == (time_t) 0 )
+- mod = now;
+- (void) strftime( nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime( &now ) );
+- (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) );
+- (void) my_snprintf(
+- fixed_type, sizeof(fixed_type), type, hc->hs->charset );
+- (void) my_snprintf( buf, sizeof(buf),
+- "%.20s %d %s\r\nServer: %s\r\nContent-Type: %s\r\nDate: %s\r\nLast-Modified: %s\r\nAccept-Ranges: bytes\r\nConnection: close\r\n",
+- hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type,
+- nowbuf, modbuf );
+- add_response( hc, buf );
++ mod = httpd_time_now;
++
++ if (last_modified == 0) {
++ (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) );
++ last_modified = modbuf;
++ last_modified_len = strlen(modbuf);
++ }
++
++ type_len = strlen(type);
++
++ if (hc->response) {
++ s.c = hc->response;
++ s.len = 0;
++ s.a = hc->maxresponse;
++ hc->response = 0;
++ hc->maxresponse = 0;
++ }
++
++ smart_str_appends(&s, "HTTP/1.1 ");
++ smart_str_append_long(&s, status);
++ smart_str_appends(&s, " HTTP\r\nServer: " EXPOSED_SERVER_SOFTWARE "\r\n"
++ "Content-Type: ");
++
++ if (type[type_len-2] == '%' && type[type_len-1] == 's') {
++ smart_str_appendl(&s, type, type_len - 2);
++ smart_str_appends(&s, hc->hs->charset);
++ } else {
++ smart_str_appendl(&s, type, type_len);
++ }
++
++
++ smart_str_appends(&s, "\r\nDate: ");
++ smart_str_appends(&s, httpd_now_buf);
++ smart_str_appends(&s, "\r\nLast-Modified: ");
++ smart_str_appendl(&s, last_modified, last_modified_len);
++ smart_str_appends(&s, "\r\nAccept-Ranges: bytes\r\n");
++
+ if ( encodings[0] != '\0' )
+ {
+- (void) my_snprintf( buf, sizeof(buf),
+- "Content-Encoding: %s\r\n", encodings );
+- add_response( hc, buf );
++ smart_str_appends(&s, "Content-Encoding: ");
++ smart_str_appends(&s, encodings);
++ smart_str_appends(&s, "\r\n");
+ }
+ if ( partial_content )
+ {
+- (void) my_snprintf( buf, sizeof(buf),
+- "Content-Range: bytes %ld-%ld/%d\r\nContent-Length: %ld\r\n",
+- (long) hc->init_byte_loc, (long) hc->end_byte_loc, length,
+- (long) ( hc->end_byte_loc - hc->init_byte_loc + 1 ) );
+- add_response( hc, buf );
++
++ smart_str_appends(&s, "Content-Range: bytes ");
++ smart_str_append_long(&s, hc->init_byte_loc);
++ smart_str_appendc(&s, '-');
++ smart_str_append_long(&s, hc->end_byte_loc);
++ smart_str_appendc(&s, '/');
++ smart_str_append_long(&s, length);
++ smart_str_appends(&s, "\r\nContent-Length: ");
++ smart_str_append_long(&s, hc->end_byte_loc - hc->init_byte_loc + 1);
++ smart_str_appends(&s, "\r\n");
++
+ }
+ else if ( length >= 0 )
+ {
+- (void) my_snprintf( buf, sizeof(buf),
+- "Content-Length: %d\r\n", length );
+- add_response( hc, buf );
++ smart_str_appends(&s, "Content-Length: ");
++ smart_str_append_long(&s, length);
++ smart_str_appends(&s, "\r\n");
+ }
++ else {
++ hc->do_keep_alive = 0;
++ }
+ if ( extraheads[0] != '\0' )
+- add_response( hc, extraheads );
+- add_response( hc, "\r\n" );
++ smart_str_appends(&s, extraheads);
++ if (hc->do_keep_alive) {
++ smart_str_appends(&s, "Connection: keep-alive\r\n\r\n" );
++ } else {
++ smart_str_appends(&s, "Connection: close\r\n\r\n" );
++ }
++ smart_str_0(&s);
++
++ if (hc->response) {
++ free(hc->response);
++ }
++ hc->response = s.c;
++ hc->maxresponse = s.a;
++ hc->responselen = s.len;
++
+ }
+ }
+
+@@ -725,7 +805,7 @@
+ {
+ char defanged_arg[1000], buf[2000];
+
+- send_mime( hc, status, title, "", extraheads, "text/html", -1, 0 );
++ send_mime( hc, status, title, "", extraheads, "text/html", -1, 0, 0, 0 );
+ (void) my_snprintf( buf, sizeof(buf),
+ "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n<BODY BGCOLOR=\"#cc9999\"><H2>%d %s</H2>\n",
+ status, title, status, title );
+@@ -764,7 +844,7 @@
+ char* cp2;
+
+ for ( cp1 = str, cp2 = dfstr;
+- *cp1 != '\0' && cp2 - dfstr < dfsize - 1;
++ *cp1 != '\0' && cp2 - dfstr < dfsize - 5;
+ ++cp1, ++cp2 )
+ {
+ switch ( *cp1 )
+@@ -834,7 +914,7 @@
+ fp = fopen( filename, "r" );
+ if ( fp == (FILE*) 0 )
+ return 0;
+- send_mime( hc, status, title, "", extraheads, "text/html", -1, 0 );
++ send_mime( hc, status, title, "", extraheads, "text/html", -1, 0, 0, 0 );
+ for (;;)
+ {
+ r = fread( buf, 1, sizeof(buf) - 1, fp );
+@@ -1336,6 +1416,9 @@
+ if ( hc->tildemapped )
+ return 1;
+
++ if ( hc->hostname[0] == '.' || strchr( hc->hostname, '/' ) != (char*) 0 )
++ return 0;
++
+ /* Figure out the host directory. */
+ #ifdef VHOST_DIRLEVELS
+ httpd_realloc_str(
+@@ -1436,7 +1519,7 @@
+ restlen = strlen( path );
+ httpd_realloc_str( &rest, &maxrest, restlen );
+ (void) strcpy( rest, path );
+- if ( rest[restlen - 1] == '/' )
++ if ( restlen > 0 && rest[restlen - 1] == '/' )
+ rest[--restlen] = '\0'; /* trim trailing slash */
+ if ( ! tildemapped )
+ /* Remove any leading slashes. */
+@@ -1603,6 +1686,70 @@
+
+
+ int
++httpd_request_reset(httpd_conn* hc, int preserve_read_buf )
++{
++ if (!preserve_read_buf) {
++ hc->read_idx = 0;
++ hc->checked_idx = 0;
++ }
++
++ if (hc->read_buf_is_mmap) {
++ hc->read_buf_is_mmap = 0;
++ munmap(hc->read_buf, hc->read_size);
++ hc->read_buf = NULL;
++ hc->read_size = 0;
++ httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 );
++ }
++ hc->checked_state = CHST_FIRSTWORD;
++ hc->method = METHOD_UNKNOWN;
++ hc->status = 0;
++ hc->bytes_to_send = 0;
++ hc->bytes_sent = 0;
++ hc->encodedurl = "";
++ hc->decodedurl[0] = '\0';
++ hc->protocol = "UNKNOWN";
++ hc->origfilename[0] = '\0';
++ hc->expnfilename[0] = '\0';
++ hc->encodings[0] = '\0';
++ hc->pathinfo[0] = '\0';
++ hc->query[0] = '\0';
++ hc->referer = "";
++ hc->useragent = "";
++ hc->accept[0] = '\0';
++ hc->accepte[0] = '\0';
++ hc->acceptl = "";
++ hc->cookie = "";
++ hc->contenttype = "";
++ hc->reqhost[0] = '\0';
++ hc->hdrhost = "";
++ hc->hostdir[0] = '\0';
++ hc->authorization = "";
++ hc->remoteuser[0] = '\0';
++ hc->response[0] = '\0';
++#ifdef TILDE_MAP_2
++ hc->altdir[0] = '\0';
++#endif /* TILDE_MAP_2 */
++ hc->responselen = 0;
++ hc->if_modified_since = (time_t) -1;
++ hc->range_if = (time_t) -1;
++ hc->contentlength = -1;
++ hc->type = "";
++ hc->hostname = (char*) 0;
++ hc->mime_flag = 1;
++ hc->one_one = 0;
++ hc->got_range = 0;
++ hc->tildemapped = 0;
++ hc->init_byte_loc = 0;
++ hc->end_byte_loc = -1;
++ hc->keep_alive = 0;
++ hc->do_keep_alive = 0;
++ hc->should_linger = 0;
++ hc->file_address = (char*) 0;
++ hc->read_body_into_mem = 0;
++ return GC_OK;
++}
++
++int
+ httpd_get_conn( httpd_server* hs, int listen_fd, httpd_conn* hc )
+ {
+ httpd_sockaddr sa;
+@@ -1612,6 +1759,7 @@
+ {
+ hc->read_size = 0;
+ httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 );
++ hc->read_buf_is_mmap = 0;
+ hc->maxdecodedurl =
+ hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings =
+ hc->maxpathinfo = hc->maxquery = hc->maxaccept =
+@@ -1631,12 +1779,19 @@
+ httpd_realloc_str( &hc->reqhost, &hc->maxreqhost, 0 );
+ httpd_realloc_str( &hc->hostdir, &hc->maxhostdir, 0 );
+ httpd_realloc_str( &hc->remoteuser, &hc->maxremoteuser, 0 );
+- httpd_realloc_str( &hc->response, &hc->maxresponse, 0 );
++ httpd_realloc_str( &hc->response, &hc->maxresponse, 350 );
+ #ifdef TILDE_MAP_2
+ httpd_realloc_str( &hc->altdir, &hc->maxaltdir, 0 );
+ #endif /* TILDE_MAP_2 */
+ hc->initialized = 1;
+ }
++ if (hc->read_buf_is_mmap) {
++ hc->read_buf_is_mmap = 0;
++ munmap(hc->read_buf, hc->read_size);
++ hc->read_buf = NULL;
++ hc->read_size = 0;
++ httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 );
++ }
+
+ /* Accept the new connection. */
+ sz = sizeof(sa);
+@@ -1657,53 +1812,12 @@
+ hc->hs = hs;
+ memset( &hc->client_addr, 0, sizeof(hc->client_addr) );
+ memcpy( &hc->client_addr, &sa, sockaddr_len( &sa ) );
+- hc->read_idx = 0;
+- hc->checked_idx = 0;
+- hc->checked_state = CHST_FIRSTWORD;
+- hc->method = METHOD_UNKNOWN;
+- hc->status = 0;
+- hc->bytes_to_send = 0;
+- hc->bytes_sent = 0;
+- hc->encodedurl = "";
+- hc->decodedurl[0] = '\0';
+- hc->protocol = "UNKNOWN";
+- hc->origfilename[0] = '\0';
+- hc->expnfilename[0] = '\0';
+- hc->encodings[0] = '\0';
+- hc->pathinfo[0] = '\0';
+- hc->query[0] = '\0';
+- hc->referer = "";
+- hc->useragent = "";
+- hc->accept[0] = '\0';
+- hc->accepte[0] = '\0';
+- hc->acceptl = "";
+- hc->cookie = "";
+- hc->contenttype = "";
+- hc->reqhost[0] = '\0';
+- hc->hdrhost = "";
+- hc->hostdir[0] = '\0';
+- hc->authorization = "";
+- hc->remoteuser[0] = '\0';
+- hc->response[0] = '\0';
+-#ifdef TILDE_MAP_2
+- hc->altdir[0] = '\0';
+-#endif /* TILDE_MAP_2 */
+- hc->responselen = 0;
+- hc->if_modified_since = (time_t) -1;
+- hc->range_if = (time_t) -1;
+- hc->contentlength = -1;
+- hc->type = "";
+- hc->hostname = (char*) 0;
+- hc->mime_flag = 1;
+- hc->one_one = 0;
+- hc->got_range = 0;
+- hc->tildemapped = 0;
+- hc->init_byte_loc = 0;
+- hc->end_byte_loc = -1;
+- hc->keep_alive = 0;
+- hc->should_linger = 0;
+- hc->file_address = (char*) 0;
+- return GC_OK;
++
++/*
++printf("doing httpd_get_con(%d)\n", hc->conn_fd);
++*/
++
++ return httpd_request_reset(hc, 0);
+ }
+
+
+@@ -1720,6 +1834,9 @@
+ {
+ char c;
+
++/*
++printf("**REQUEST [%d]**\n%*.*s\n", hc->conn_fd, hc->read_idx, hc->read_idx, hc->read_buf);
++*/
+ for ( ; hc->checked_idx < hc->read_idx; ++hc->checked_idx )
+ {
+ c = hc->read_buf[hc->checked_idx];
+@@ -1912,8 +2029,11 @@
+ eol = strpbrk( protocol, " \t\n\r" );
+ if ( eol != (char*) 0 )
+ *eol = '\0';
+- if ( strcasecmp( protocol, "HTTP/1.0" ) != 0 )
++ if ( strcasecmp( protocol, "HTTP/1.0" ) != 0 ) {
+ hc->one_one = 1;
++ hc->keep_alive = 1;
++ hc->do_keep_alive = 1;
++ }
+ }
+ }
+ /* Check for HTTP/1.1 absolute URL. */
+@@ -2129,6 +2249,7 @@
+ cp = &buf[11];
+ cp += strspn( cp, " \t" );
+ if ( strcasecmp( cp, "keep-alive" ) == 0 )
++ hc->do_keep_alive = 1;
+ hc->keep_alive = 1;
+ }
+ #ifdef LOG_UNKNOWN_HEADERS
+@@ -2168,6 +2289,9 @@
+ }
+ }
+
++/*
++printf("one_one = %d keep_alive = %d\n", hc->one_one, hc->keep_alive);
++*/
+ if ( hc->one_one )
+ {
+ /* Check that HTTP/1.1 requests specify a host, as required. */
+@@ -2177,14 +2301,14 @@
+ return -1;
+ }
+
+- /* If the client wants to do keep-alives, it might also be doing
+- ** pipelining. There's no way for us to tell. Since we don't
+- ** implement keep-alives yet, if we close such a connection there
+- ** might be unread pipelined requests waiting. So, we have to
+- ** do a lingering close.
++ /*
++ ** Disable keep alive support for bad browsers,
++ ** list taken from Apache 1.3.19
+ */
+- if ( hc->keep_alive )
+- hc->should_linger = 1;
++ if ( hc->do_keep_alive &&
++ ( strstr(hc->useragent, "Mozilla/2") != NULL ||
++ strstr(hc->useragent, "MSIE 4.0b2;") != NULL))
++ hc->do_keep_alive = 0;
+ }
+
+ /* Ok, the request has been parsed. Now we resolve stuff that
+@@ -2349,15 +2473,24 @@
+
+
+ void
+-httpd_close_conn( httpd_conn* hc, struct timeval* nowP )
+- {
+- make_log_entry( hc, nowP );
++httpd_complete_request( httpd_conn* hc, struct timeval* nowP)
++{
++ if (hc->method != METHOD_UNKNOWN)
++ make_log_entry( hc, nowP );
+
+- if ( hc->file_address != (char*) 0 )
++ if ( hc->file_address == (char*) 1 )
++ {
++ thttpd_closed_conn(hc->conn_fd);
++ } else if ( hc->file_address != (char*) 0 )
+ {
+ mmc_unmap( hc->file_address, &(hc->sb), nowP );
+ hc->file_address = (char*) 0;
+ }
++ }
++
++void
++httpd_close_conn( httpd_conn* hc, struct timeval* nowP )
++{
+ if ( hc->conn_fd >= 0 )
+ {
+ (void) close( hc->conn_fd );
+@@ -2370,7 +2503,12 @@
+ {
+ if ( hc->initialized )
+ {
+- free( (void*) hc->read_buf );
++
++ if ( hc->read_buf_is_mmap ) {
++ munmap( hc->read_buf, hc->read_size );
++ } else {
++ free( (void*) hc->read_buf );
++ }
+ free( (void*) hc->decodedurl );
+ free( (void*) hc->origfilename );
+ free( (void*) hc->expnfilename );
+@@ -2556,7 +2694,7 @@
+ return -1;
+ }
+
+- send_mime( hc, 200, ok200title, "", "", "text/html", -1, hc->sb.st_mtime );
++ send_mime( hc, 200, ok200title, "", "", "text/html", -1, hc->sb.st_mtime, 0, 0 );
+ if ( hc->method == METHOD_HEAD )
+ closedir( dirp );
+ else if ( hc->method == METHOD_GET )
+@@ -3026,11 +3164,9 @@
+ post_post_garbage_hack( httpd_conn* hc )
+ {
+ char buf[2];
+- int r;
+
+- r = recv( hc->conn_fd, buf, sizeof(buf), MSG_PEEK );
+- if ( r > 0 )
+- (void) read( hc->conn_fd, buf, r );
++ fcntl(hc->conn_fd, F_SETFL, O_NONBLOCK);
++ (void) read( hc->conn_fd, buf, 2 );
+ }
+
+
+@@ -3313,6 +3449,11 @@
+ int r;
+ ClientData client_data;
+
++ /*
++ ** We are not going to leave the socket open after a CGI... too hard
++ */
++ hc->do_keep_alive = 0;
++
+ if ( hc->method == METHOD_GET || hc->method == METHOD_POST )
+ {
+ httpd_clear_ndelay( hc->conn_fd );
+@@ -3369,6 +3510,7 @@
+ int expnlen, indxlen;
+ char* cp;
+ char* pi;
++ int nocache = 0;
+
+ expnlen = strlen( hc->expnfilename );
+
+@@ -3561,6 +3703,16 @@
+ match( hc->hs->cgi_pattern, hc->expnfilename ) )
+ return cgi( hc );
+
++ if ( hc->hs->php_pattern != (char*) 0 &&
++ match( hc->hs->php_pattern, hc->expnfilename)) {
++ return thttpd_php_request( hc, 0 );
++ }
++
++ if ( hc->hs->phps_pattern != (char*) 0 &&
++ match( hc->hs->phps_pattern, hc->expnfilename)) {
++ return thttpd_php_request( hc, 1 );
++ }
++
+ /* It's not CGI. If it's executable or there's pathinfo, someone's
+ ** trying to either serve or run a non-CGI file as CGI. Either case
+ ** is prohibited.
+@@ -3594,32 +3746,46 @@
+ hc->end_byte_loc = hc->sb.st_size - 1;
+
+ figure_mime( hc );
++ if ( strncmp(hc->decodedurl, "/nocache/", sizeof("/nocache/") - 1 ) == 0 )
++ nocache = 1;
+
+ if ( hc->method == METHOD_HEAD )
+ {
+ send_mime(
+ hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,
+- hc->sb.st_mtime );
++ hc->sb.st_mtime, 0, 0 );
+ }
+- else if ( hc->if_modified_since != (time_t) -1 &&
++ else if ( !nocache && hc->if_modified_since != (time_t) -1 &&
+ hc->if_modified_since >= hc->sb.st_mtime )
+ {
+- hc->method = METHOD_HEAD;
+ send_mime(
+- hc, 304, err304title, hc->encodings, "", hc->type, hc->sb.st_size,
+- hc->sb.st_mtime );
++ hc, 304, err304title, hc->encodings, "", hc->type, -1,
++ hc->sb.st_mtime, 0, 0 );
+ }
+ else
+ {
+- hc->file_address = mmc_map( hc->expnfilename, &(hc->sb), nowP );
++ char *extraheads = "";
++ char *lm;
++ size_t lml;
++
++ if ( nocache )
++ {
++ extraheads = "Expires: Thu, 19 Nov 1981 08:52:00 GMT\r\n"
++ "Cache-Control: no-store, no-cache, must-revalidate, "
++ "post-check=0, pre-check=0\r\n"
++ "Pragma: no-cache\r\n";
++ }
++
++ hc->file_address = mmc_map( hc->expnfilename, &(hc->sb), nowP, nocache, &lm, &lml );
+ if ( hc->file_address == (char*) 0 )
+ {
+ httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
+ return -1;
+ }
++
+ send_mime(
+- hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,
+- hc->sb.st_mtime );
++ hc, 200, ok200title, hc->encodings, extraheads, hc->type, hc->sb.st_size,
++ hc->sb.st_mtime, lm, lml );
+ }
+
+ return 0;
+@@ -3638,6 +3804,9 @@
+ return r;
+ }
+
++#define smart_str_append_const(a,b) smart_str_appendl(a,b,sizeof(b)-1)
++
++static smart_str bentries;
+
+ static void
+ make_log_entry( httpd_conn* hc, struct timeval* nowP )
+@@ -3648,88 +3817,62 @@
+
+ if ( hc->hs->no_log )
+ return;
+-
+- /* This is straight CERN Combined Log Format - the only tweak
+- ** being that if we're using syslog() we leave out the date, because
+- ** syslogd puts it in. The included syslogtocern script turns the
+- ** results into true CERN format.
+- */
+-
+ /* Format remote user. */
+ if ( hc->remoteuser[0] != '\0' )
+- ru = hc->remoteuser;
++ ru = hc->remoteuser;
+ else
+- ru = "-";
++ ru = "-";
+ /* If we're vhosting, prepend the hostname to the url. This is
+ ** a little weird, perhaps writing separate log files for
+ ** each vhost would make more sense.
+ */
+- if ( hc->hs->vhost && ! hc->tildemapped )
+- (void) my_snprintf( url, sizeof(url),
+- "/%.100s%.200s",
+- hc->hostname == (char*) 0 ? hc->hs->server_hostname : hc->hostname,
+- hc->encodedurl );
+- else
+- (void) my_snprintf( url, sizeof(url),
+- "%.200s", hc->encodedurl );
+- /* Format the bytes. */
+- if ( (long) hc->bytes_sent >= 0 )
+- (void) my_snprintf( bytes, sizeof(bytes),
+- "%ld", (long) hc->bytes_sent );
+- else
+- (void) strcpy( bytes, "-" );
+
+ /* Logfile or syslog? */
+ if ( hc->hs->logfp != (FILE*) 0 )
+- {
+- time_t now;
+- struct tm* t;
+- const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S";
+- char date_nozone[100];
+- int zone;
+- char sign;
+- char date[100];
+-
+- /* Get the current time, if necessary. */
+- if ( nowP != (struct timeval*) 0 )
+- now = nowP->tv_sec;
+- else
+- now = time( (time_t*) 0 );
+- /* Format the time, forcing a numeric timezone (some log analyzers
+- ** are stoooopid about this).
+- */
+- t = localtime( &now );
+- (void) strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t );
+-#ifdef HAVE_TM_GMTOFF
+- zone = t->tm_gmtoff / 60L;
+-#else
+- zone = -timezone / 60L;
+- /* Probably have to add something about daylight time here. */
+-#endif
+- if ( zone >= 0 )
+- sign = '+';
+- else
+- {
+- sign = '-';
+- zone = -zone;
+- }
+- zone = ( zone / 60 ) * 100 + zone % 60;
+- (void) my_snprintf( date, sizeof(date),
+- "%s %c%04d", date_nozone, sign, zone );
+- /* And write the log entry. */
+- (void) fprintf( hc->hs->logfp,
+- "%.80s - %.80s [%s] \"%.80s %.300s %.80s\" %d %s \"%.200s\" \"%.80s\"\n",
+- httpd_ntoa( &hc->client_addr ), ru, date,
+- httpd_method_str( hc->method ), url, hc->protocol,
+- hc->status, bytes, hc->referer, hc->useragent );
+- (void) fflush( hc->hs->logfp ); /* don't need to flush every time */
+- }
+- else
+- syslog( LOG_INFO,
+- "%.80s - %.80s \"%.80s %.200s %.80s\" %d %s \"%.200s\" \"%.80s\"",
+- httpd_ntoa( &hc->client_addr ), ru,
+- httpd_method_str( hc->method ), url, hc->protocol,
+- hc->status, bytes, hc->referer, hc->useragent );
++ {
++ /* XXXXXXX */
++
++ smart_str_appends(&bentries, httpd_ntoa(&hc->client_addr));
++ smart_str_append_const(&bentries, " - ");
++ smart_str_appends(&bentries, ru);
++ smart_str_append_const(&bentries, " [");
++ smart_str_appendl(&bentries, hc->hs->log_date, hc->hs->log_date_len);
++ smart_str_append_const(&bentries, "] \"");
++ smart_str_appends(&bentries, httpd_method_str(hc->method));
++ smart_str_appendc(&bentries, ' ');
++
++ if (hc->hs->vhost && ! hc->tildemapped) {
++ smart_str_appendc(&bentries, '/');
++ if (hc->hostname)
++ smart_str_appends(&bentries, hc->hostname);
++ else
++ smart_str_appends(&bentries, hc->hs->server_hostname);
++ }
++ smart_str_appends(&bentries, hc->encodedurl);
++
++ smart_str_appendc(&bentries, ' ');
++ smart_str_appends(&bentries, hc->protocol);
++ smart_str_append_const(&bentries, "\" ");
++ smart_str_append_long(&bentries, hc->status);
++ if (hc->bytes_sent >= 0) {
++ smart_str_appendc(&bentries, ' ');
++ smart_str_append_long(&bentries, hc->bytes_sent);
++ smart_str_append_const(&bentries, " \"");
++ } else {
++ smart_str_append_const(&bentries, " - \"");
++ }
++ smart_str_appends(&bentries, hc->referer);
++ smart_str_append_const(&bentries, "\" \"");
++ smart_str_appends(&bentries, hc->useragent);
++ smart_str_append_const(&bentries, "\"\n");
++
++ if (bentries.len > 16384) {
++ int fd = fileno(hc->hs->logfp);
++ write(fd, bentries.c, bentries.len);
++ bentries.len = 0;
++ }
++ }
++
+ }
+
+
+@@ -3840,7 +3983,24 @@
+ {
+ #ifdef HAVE_GETNAMEINFO
+ static char str[200];
++ static smart_str httpd_ntoa_buf;
++
++ if (saP->sa_in.sin_family == AF_INET) {
++ unsigned long n = ntohl(saP->sa_in.sin_addr.s_addr);
+
++ httpd_ntoa_buf.len = 0;
++ smart_str_append_long(&httpd_ntoa_buf, (n >> 24));
++ smart_str_appendc(&httpd_ntoa_buf, '.');
++ smart_str_append_long(&httpd_ntoa_buf, (n >> 16) & 255);
++ smart_str_appendc(&httpd_ntoa_buf, '.');
++ smart_str_append_long(&httpd_ntoa_buf, (n >> 8) & 255);
++ smart_str_appendc(&httpd_ntoa_buf, '.');
++ smart_str_append_long(&httpd_ntoa_buf, (n >> 0) & 255);
++ smart_str_0(&httpd_ntoa_buf);
++
++ return httpd_ntoa_buf.c;
++ }
++
+ if ( getnameinfo( &saP->sa, sockaddr_len( saP ), str, sizeof(str), 0, 0, NI_NUMERICHOST ) != 0 )
+ {
+ str[0] = '?';
+diff -ur thttpd-2.21b/libhttpd.h thttpd-2.21b-cool/libhttpd.h
+--- thttpd-2.21b/libhttpd.h Tue Apr 24 00:36:50 2001
++++ thttpd-2.21b-cool/libhttpd.h Sat Sep 20 14:43:20 2003
+@@ -69,6 +69,8 @@
+ char* server_hostname;
+ int port;
+ char* cgi_pattern;
++ char* php_pattern;
++ char* phps_pattern;
+ char* charset;
+ char* cwd;
+ int listen4_fd, listen6_fd;
+@@ -80,6 +82,8 @@
+ char* url_pattern;
+ char* local_pattern;
+ int no_empty_referers;
++ size_t log_date_len;
++ char log_date[100];
+ } httpd_server;
+
+ /* A connection. */
+@@ -88,6 +92,7 @@
+ httpd_server* hs;
+ httpd_sockaddr client_addr;
+ char* read_buf;
++ char read_buf_is_mmap;
+ int read_size, read_idx, checked_idx;
+ int checked_state;
+ int method;
+@@ -132,11 +137,12 @@
+ int got_range;
+ int tildemapped; /* this connection got tilde-mapped */
+ off_t init_byte_loc, end_byte_loc;
+- int keep_alive;
++ int keep_alive, do_keep_alive;
+ int should_linger;
+ struct stat sb;
+ int conn_fd;
+ char* file_address;
++ char read_body_into_mem;
+ } httpd_conn;
+
+ /* Methods. */
+@@ -168,7 +174,8 @@
+ char* hostname, httpd_sockaddr* sa4P, httpd_sockaddr* sa6P, int port,
+ char* cgi_pattern, char* charset, char* cwd, int no_log, FILE* logfp,
+ int no_symlink, int vhost, int global_passwd, char* url_pattern,
+- char* local_pattern, int no_empty_referers );
++ char* local_pattern, int no_empty_referers, char* php_pattern,
++ char* phps_pattern );
+
+ /* Change the log file. */
+ extern void httpd_set_logfp( httpd_server* hs, FILE* logfp );
+@@ -229,6 +236,8 @@
+ ** If you don't have a current timeval handy just pass in 0.
+ */
+ extern void httpd_close_conn( httpd_conn* hc, struct timeval* nowP );
++void httpd_complete_request( httpd_conn* hc, struct timeval* nowP);
++int httpd_request_reset(httpd_conn* hc,int );
+
+ /* Call this to de-initialize a connection struct and *really* free the
+ ** mallocced strings.
+diff -ur thttpd-2.21b/mime_encodings.txt thttpd-2.21b-cool/mime_encodings.txt
+--- thttpd-2.21b/mime_encodings.txt Wed May 10 03:22:28 2000
++++ thttpd-2.21b-cool/mime_encodings.txt Sat Sep 20 14:43:20 2003
+@@ -3,6 +3,6 @@
+ # A list of file extensions followed by the corresponding MIME encoding.
+ # Extensions not found in the table proceed to the mime_types table.
+
+-Z x-compress
+-gz x-gzip
++Z compress
++gz gzip
+ uu x-uuencode
+diff -ur thttpd-2.21b/mime_types.txt thttpd-2.21b-cool/mime_types.txt
+--- thttpd-2.21b/mime_types.txt Sat Apr 14 04:53:30 2001
++++ thttpd-2.21b-cool/mime_types.txt Sat Sep 20 14:43:20 2003
+@@ -1,135 +1,138 @@
+-# mime_types.txt
+-#
+-# A list of file extensions followed by the corresponding MIME type.
+-# Extensions not found in the table are returned as text/plain.
+-
+-html text/html; charset=%s
+-htm text/html; charset=%s
+-txt text/plain; charset=%s
+-rtx text/richtext
+-etx text/x-setext
+-tsv text/tab-separated-values
+-css text/css
+-xml text/xml
+-dtd text/xml
+-
+-gif image/gif
+-jpg image/jpeg
+-jpeg image/jpeg
+-jpe image/jpeg
+-jfif image/jpeg
+-tif image/tiff
+-tiff image/tiff
+-pbm image/x-portable-bitmap
+-pgm image/x-portable-graymap
+-ppm image/x-portable-pixmap
+-pnm image/x-portable-anymap
+-xbm image/x-xbitmap
+-xpm image/x-xpixmap
+-xwd image/x-xwindowdump
+-ief image/ief
+-png image/png
+-
+-au audio/basic
+-snd audio/basic
+-aif audio/x-aiff
+-aiff audio/x-aiff
+-aifc audio/x-aiff
+-ra audio/x-pn-realaudio
+-ram audio/x-pn-realaudio
+-rm audio/x-pn-realaudio
+-rpm audio/x-pn-realaudio-plugin
+-wav audio/wav
+-mid audio/midi
+-midi audio/midi
+-kar audio/midi
+-mpga audio/mpeg
+-mp2 audio/mpeg
+-mp3 audio/mpeg
+-
+-mpeg video/mpeg
+-mpg video/mpeg
+-mpe video/mpeg
+-qt video/quicktime
+-mov video/quicktime
+-avi video/x-msvideo
+-movie video/x-sgi-movie
+-mv video/x-sgi-movie
+-vx video/x-rad-screenplay
+-
+-a application/octet-stream
++ez application/andrew-inset
++hqx application/mac-binhex40
++cpt application/mac-compactpro
++doc application/msword
+ bin application/octet-stream
++dms application/octet-stream
++lha application/octet-stream
++lzh application/octet-stream
+ exe application/octet-stream
+-dump application/octet-stream
+-o application/octet-stream
+-class application/java
+-js application/x-javascript
++class application/octet-stream
++so application/octet-stream
++dll application/octet-stream
++oda application/oda
++pdf application/pdf
+ ai application/postscript
+ eps application/postscript
+ ps application/postscript
+-dir application/x-director
++smi application/smil
++smil application/smil
++mif application/vnd.mif
++xls application/vnd.ms-excel
++ppt application/vnd.ms-powerpoint
++wbxml application/vnd.wap.wbxml
++wmlc application/vnd.wap.wmlc
++wmlsc application/vnd.wap.wmlscriptc
++bcpio application/x-bcpio
++vcd application/x-cdlink
++pgn application/x-chess-pgn
++cpio application/x-cpio
++csh application/x-csh
+ dcr application/x-director
++dir application/x-director
+ dxr application/x-director
+-fgd application/x-director
+-aam application/x-authorware-map
+-aas application/x-authorware-seg
+-aab application/x-authorware-bin
+-fh4 image/x-freehand
+-fh7 image/x-freehand
+-fh5 image/x-freehand
+-fhc image/x-freehand
+-fh image/x-freehand
+-spl application/futuresplash
+-swf application/x-shockwave-flash
+ dvi application/x-dvi
++spl application/x-futuresplash
+ gtar application/x-gtar
+ hdf application/x-hdf
+-hqx application/mac-binhex40
+-iv application/x-inventor
++js application/x-javascript
++skp application/x-koan
++skd application/x-koan
++skt application/x-koan
++skm application/x-koan
+ latex application/x-latex
+-man application/x-troff-man
+-me application/x-troff-me
+-mif application/x-mif
+-ms application/x-troff-ms
+-oda application/oda
+-pdf application/pdf
+-rtf application/rtf
+-bcpio application/x-bcpio
+-cpio application/x-cpio
+-sv4cpio application/x-sv4cpio
+-sv4crc application/x-sv4crc
+-sh application/x-shar
++nc application/x-netcdf
++cdf application/x-netcdf
++sh application/x-sh
+ shar application/x-shar
++swf application/x-shockwave-flash
+ sit application/x-stuffit
++sv4cpio application/x-sv4cpio
++sv4crc application/x-sv4crc
+ tar application/x-tar
++tcl application/x-tcl
+ tex application/x-tex
+-texi application/x-texinfo
+ texinfo application/x-texinfo
++texi application/x-texinfo
++t application/x-troff
+ tr application/x-troff
+ roff application/x-troff
+ man application/x-troff-man
+ me application/x-troff-me
+ ms application/x-troff-ms
+-zip application/x-zip-compressed
+-tsp application/dsptype
+-wsrc application/x-wais-source
+ ustar application/x-ustar
+-cdf application/x-netcdf
+-nc application/x-netcdf
+-doc application/msword
+-ppt application/powerpoint
+-
+-crt application/x-x509-ca-cert
+-crl application/x-pkcs7-crl
+-
++src application/x-wais-source
++xhtml application/xhtml+xml
++xht application/xhtml+xml
++zip application/zip
++au audio/basic
++snd audio/basic
++mid audio/midi
++midi audio/midi
++kar audio/midi
++mpga audio/mpeg
++mp2 audio/mpeg
++mp3 audio/mpeg
++aif audio/x-aiff
++aiff audio/x-aiff
++aifc audio/x-aiff
++m3u audio/x-mpegurl
++ram audio/x-pn-realaudio
++rm audio/x-pn-realaudio
++rpm audio/x-pn-realaudio-plugin
++ra audio/x-realaudio
++wav audio/x-wav
++pdb chemical/x-pdb
++xyz chemical/x-xyz
++bmp image/bmp
++gif image/gif
++ief image/ief
++jpeg image/jpeg
++jpg image/jpeg
++jpe image/jpeg
++png image/png
++tiff image/tiff
++tif image/tiff
++djvu image/vnd.djvu
++djv image/vnd.djvu
++wbmp image/vnd.wap.wbmp
++ras image/x-cmu-raster
++pnm image/x-portable-anymap
++pbm image/x-portable-bitmap
++pgm image/x-portable-graymap
++ppm image/x-portable-pixmap
++rgb image/x-rgb
++xbm image/x-xbitmap
++xpm image/x-xpixmap
++xwd image/x-xwindowdump
++igs model/iges
++iges model/iges
++msh model/mesh
++mesh model/mesh
++silo model/mesh
+ wrl model/vrml
+ vrml model/vrml
+-mime message/rfc822
+-
+-pac application/x-ns-proxy-autoconfig
+-
++css text/css
++html text/html; charset=%s
++htm text/html; charset=%s
++asc text/plain; charset=%s
++txt text/plain; charset=%s
++rtx text/richtext
++rtf text/rtf
++sgml text/sgml
++sgm text/sgml
++tsv text/tab-separated-values
+ wml text/vnd.wap.wml
+-wmlc application/vnd.wap.wmlc
+ wmls text/vnd.wap.wmlscript
+-wmlsc application/vnd.wap.wmlscriptc
+-wbmp image/vnd.wap.wbmp
++etx text/x-setext
++xml text/xml
++xsl text/xml
++mpeg video/mpeg
++mpg video/mpeg
++mpe video/mpeg
++qt video/quicktime
++mov video/quicktime
++mxu video/vnd.mpegurl
++avi video/x-msvideo
++movie video/x-sgi-movie
++ice x-conference/x-cooltalk
+diff -ur thttpd-2.21b/mmc.c thttpd-2.21b-cool/mmc.c
+--- thttpd-2.21b/mmc.c Fri Apr 13 23:02:15 2001
++++ thttpd-2.21b-cool/mmc.c Sat Sep 20 14:43:20 2003
+@@ -70,6 +70,9 @@
+ unsigned int hash;
+ int hash_idx;
+ struct MapStruct* next;
++ char nocache;
++ size_t last_modified_len;
++ char last_modified[100];
+ } Map;
+
+
+@@ -93,12 +96,13 @@
+
+
+ void*
+-mmc_map( char* filename, struct stat* sbP, struct timeval* nowP )
++mmc_map( char* filename, struct stat* sbP, struct timeval* nowP, int nocache, char **last_modified, size_t *last_modified_len )
+ {
+ time_t now;
+ struct stat sb;
+ Map* m;
+ int fd;
++ const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";
+
+ /* Stat the file, if necessary. */
+ if ( sbP != (struct stat*) 0 )
+@@ -130,7 +134,7 @@
+ /* Yep. Just return the existing map */
+ ++m->refcount;
+ m->reftime = now;
+- return m->addr;
++ goto done;
+ }
+
+ /* Open the file. */
+@@ -167,12 +171,13 @@
+ m->ctime = sb.st_ctime;
+ m->refcount = 1;
+ m->reftime = now;
++ m->nocache = (char) nocache;
+
+ /* Avoid doing anything for zero-length files; some systems don't like
+ ** to mmap them, other systems dislike mallocing zero bytes.
+ */
+ if ( m->size == 0 )
+- m->addr = (void*) 1; /* arbitrary non-NULL address */
++ m->addr = (void*) 5; /* arbitrary non-NULL address */
+ else
+ {
+ #ifdef HAVE_MMAP
+@@ -223,6 +228,13 @@
+ maps = m;
+ ++map_count;
+
++ strftime( m->last_modified, sizeof(m->last_modified), rfc1123fmt, gmtime( &sb.st_mtime ) );
++ m->last_modified_len = strlen(m->last_modified);
++
++done:
++ *last_modified = m->last_modified;
++ *last_modified_len = m->last_modified_len;
++
+ /* And return the address. */
+ return m->addr;
+ }
+@@ -231,27 +243,32 @@
+ void
+ mmc_unmap( void* addr, struct stat* sbP, struct timeval* nowP )
+ {
+- Map* m = (Map*) 0;
++ Map* m = (Map*) 0, **mm = &maps;
+
+ /* Find the Map entry for this address. First try a hash. */
+ if ( sbP != (struct stat*) 0 )
+ {
+ m = find_hash( sbP->st_ino, sbP->st_dev, sbP->st_size, sbP->st_ctime );
+- if ( m != (Map*) 0 && m->addr != addr )
++ if ( m != (Map*) 0 && ( m->addr != addr || m->nocache == 1 ) )
+ m = (Map*) 0;
+ }
+ /* If that didn't work, try a full search. */
+ if ( m == (Map*) 0 )
+- for ( m = maps; m != (Map*) 0; m = m->next )
++ for ( m = maps; m != (Map*) 0; m = m->next ) {
+ if ( m->addr == addr )
+ break;
++ mm = &m->next;
++ }
+ if ( m == (Map*) 0 )
+ syslog( LOG_ERR, "mmc_unmap failed to find entry!" );
+ else if ( m->refcount <= 0 )
+ syslog( LOG_ERR, "mmc_unmap found zero or negative refcount!" );
+ else
+ {
+- --m->refcount;
++ if ( --m->refcount == 0 && m->nocache == 1 ) {
++ really_unmap( mm );
++ return;
++ }
+ if ( nowP != (struct timeval*) 0 )
+ m->reftime = nowP->tv_sec;
+ else
+diff -ur thttpd-2.21b/mmc.h thttpd-2.21b-cool/mmc.h
+--- thttpd-2.21b/mmc.h Fri Apr 13 07:36:54 2001
++++ thttpd-2.21b-cool/mmc.h Sat Sep 20 14:43:20 2003
+@@ -31,8 +31,9 @@
+ /* Returns an mmap()ed area for the given file, or (void*) 0 on errors.
+ ** If you have a stat buffer on the file, pass it in, otherwise pass 0.
+ ** Same for the current time.
++** Set nocache to 1, if this entry is supposed to be removed quickly.
+ */
+-extern void* mmc_map( char* filename, struct stat* sbP, struct timeval* nowP );
++extern void* mmc_map( char* filename, struct stat* sbP, struct timeval* nowP, int nocache, char **last_modified, size_t *last_modified_len );
+
+ /* Done with an mmap()ed area that was returned by mmc_map().
+ ** If you have a stat buffer on the file, pass it in, otherwise pass 0.
+diff -ur thttpd-2.21b/thttpd.c thttpd-2.21b-cool/thttpd.c
+--- thttpd-2.21b/thttpd.c Tue Apr 24 00:41:57 2001
++++ thttpd-2.21b-cool/thttpd.c Sat Sep 20 14:43:20 2003
+@@ -53,6 +53,10 @@
+ #endif
+ #include <unistd.h>
+
++#include <sys/mman.h>
++
++#include <limits.h>
++
+ #include "fdwatch.h"
+ #include "libhttpd.h"
+ #include "mmc.h"
+@@ -66,6 +70,8 @@
+ static char* dir;
+ static int do_chroot, no_log, no_symlink, do_vhost, do_global_passwd;
+ static char* cgi_pattern;
++static char* php_pattern;
++static char* phps_pattern;
+ static char* url_pattern;
+ static int no_empty_referers;
+ static char* local_pattern;
+@@ -95,10 +101,10 @@
+ httpd_conn* hc;
+ int tnums[MAXTHROTTLENUMS]; /* throttle indexes */
+ int numtnums;
++ int keep_alive;
+ long limit;
+ time_t started_at;
+- Timer* idle_read_timer;
+- Timer* idle_send_timer;
++ time_t last_io;
+ Timer* wakeup_timer;
+ Timer* linger_timer;
+ long wouldblock_delay;
+@@ -106,17 +112,22 @@
+ off_t bytes_sent;
+ off_t bytes_to_send;
+ } connecttab;
+-static connecttab* connects;
++static connecttab* connects, **free_connects;
++static int next_free_connect;
+ static int numconnects, maxconnects;
+ static int httpd_conn_count;
+
+ /* The connection states. */
+-#define CNST_FREE 0
+-#define CNST_READING 1
+-#define CNST_SENDING 2
+-#define CNST_PAUSING 3
+-#define CNST_LINGERING 4
+-
++enum {
++ CNST_FREE = 0,
++ CNST_READING,
++ CNST_SENDING,
++ CNST_PAUSING,
++ CNST_LINGERING,
++ CNST_SENDING_RESP,
++ CNST_READING_BODY,
++ CNST_TOTAL_NR
++};
+
+ static httpd_server* hs = (httpd_server*) 0;
+ int terminate = 0;
+@@ -140,23 +151,32 @@
+ static int handle_newconnect( struct timeval* tvP, int listen_fd );
+ static void handle_read( connecttab* c, struct timeval* tvP );
+ static void handle_send( connecttab* c, struct timeval* tvP );
++static void handle_send_resp( connecttab* c, struct timeval* tvP );
++static void handle_read_body( connecttab* c, struct timeval* tvP );
+ static void handle_linger( connecttab* c, struct timeval* tvP );
+ static int check_throttles( connecttab* c );
++static void timeout_conns( ClientData client_data, struct timeval* nowP );
+ static void clear_throttles( connecttab* c, struct timeval* tvP );
+ static void update_throttles( ClientData client_data, struct timeval* nowP );
+-static void clear_connection( connecttab* c, struct timeval* tvP );
++static void clear_connection( connecttab* c, struct timeval* tvP, int );
+ static void really_clear_connection( connecttab* c, struct timeval* tvP );
+-static void idle_read_connection( ClientData client_data, struct timeval* nowP );
+-static void idle_send_connection( ClientData client_data, struct timeval* nowP );
+ static void wakeup_connection( ClientData client_data, struct timeval* nowP );
+ static void linger_clear_connection( ClientData client_data, struct timeval* nowP );
+ static void occasional( ClientData client_data, struct timeval* nowP );
++static void periodic_jobs( ClientData client_data, struct timeval* nowP );
+ #ifdef STATS_TIME
+ static void show_stats( ClientData client_data, struct timeval* nowP );
+ #endif /* STATS_TIME */
+ static void logstats( struct timeval* nowP );
+ static void thttpd_logstats( long secs );
++static void boot_request(connecttab *c, struct timeval *tvP);
++
++typedef void (*handler_func)(connecttab*, struct timeval *);
++
++handler_func handler_array[CNST_TOTAL_NR] =
++{NULL, handle_read, handle_send, NULL, handle_linger, handle_send_resp, handle_read_body};
+
++#define RUN_HANDLER(type, c) if (handler_array[type]) handler_array[type](c, &tv)
+
+ static void
+ handle_term( int sig )
+@@ -177,7 +197,7 @@
+ return;
+
+ /* Re-open the log file. */
+- if ( logfile != (char*) 0 )
++ if ( logfile != (char*) 0 && strcmp(logfile, "-") != 0)
+ {
+ logfp = fopen( logfile, "a" );
+ if ( logfp == (FILE*) 0 )
+@@ -198,6 +218,8 @@
+ }
+
+
++time_t httpd_time_now;
++
+ static void
+ handle_usr2( int sig )
+ {
+@@ -217,7 +239,6 @@
+ int num_ready;
+ int cnum, ridx;
+ connecttab* c;
+- httpd_conn* hc;
+ httpd_sockaddr sa4;
+ httpd_sockaddr sa6;
+ int gotv4, gotv6;
+@@ -270,7 +291,9 @@
+ no_log = 1;
+ logfp = (FILE*) 0;
+ }
+- else
++ else if (strcmp(logfile, "-") == 0) {
++ logfp = stdout;
++ } else
+ {
+ logfp = fopen( logfile, "a" );
+ if ( logfp == (FILE*) 0 )
+@@ -420,7 +443,8 @@
+ hostname,
+ gotv4 ? &sa4 : (httpd_sockaddr*) 0, gotv6 ? &sa6 : (httpd_sockaddr*) 0,
+ port, cgi_pattern, charset, cwd, no_log, logfp, no_symlink, do_vhost,
+- do_global_passwd, url_pattern, local_pattern, no_empty_referers );
++ do_global_passwd, url_pattern, local_pattern, no_empty_referers,
++ php_pattern, phps_pattern);
+ if ( hs == (httpd_server*) 0 )
+ exit( 1 );
+
+@@ -430,6 +454,12 @@
+ syslog( LOG_CRIT, "tmr_create(occasional) failed" );
+ exit( 1 );
+ }
++
++ if (tmr_create(0, timeout_conns, JunkClientData, 30 * 1000, 1) == 0) {
++ syslog(LOG_CRIT, "tmr_create(timeout_conns) failed");
++ exit(1);
++ }
++
+ if ( numthrottles > 0 )
+ {
+ /* Set up the throttles timer. */
+@@ -439,6 +469,12 @@
+ exit( 1 );
+ }
+ }
++
++ if (tmr_create(0, periodic_jobs, JunkClientData, 2000, 1) == 0) {
++ syslog(LOG_CRIT, "tmr_create failed");
++ exit(1);
++ }
++
+ #ifdef STATS_TIME
+ /* Set up the stats timer. */
+ if ( tmr_create( (struct timeval*) 0, show_stats, JunkClientData, STATS_TIME * 1000L, 1 ) == (Timer*) 0 )
+@@ -454,12 +490,14 @@
+ /* If we're root, try to become someone else. */
+ if ( getuid() == 0 )
+ {
++#ifndef __CYGWIN__
+ /* Set aux groups to null. */
+ if ( setgroups( 0, (const gid_t*) 0 ) < 0 )
+ {
+ syslog( LOG_CRIT, "setgroups - %m" );
+ exit( 1 );
+ }
++#endif
+ /* Set primary group. */
+ if ( setgid( gid ) < 0 )
+ {
+@@ -495,13 +533,17 @@
+ }
+ maxconnects -= SPARE_FDS;
+ connects = NEW( connecttab, maxconnects );
++ free_connects = malloc(sizeof(connecttab *) * maxconnects);
++ next_free_connect = maxconnects;
+ if ( connects == (connecttab*) 0 )
+ {
+ syslog( LOG_CRIT, "out of memory allocating a connecttab" );
+ exit( 1 );
+ }
++
+ for ( cnum = 0; cnum < maxconnects; ++cnum )
+ {
++ free_connects[cnum] = &connects[maxconnects - cnum - 1];
+ connects[cnum].conn_state = CNST_FREE;
+ connects[cnum].hc = (httpd_conn*) 0;
+ }
+@@ -518,6 +560,9 @@
+
+ /* Main loop. */
+ (void) gettimeofday( &tv, (struct timezone*) 0 );
++ httpd_time_now = tv.tv_sec;
++ periodic_jobs(JunkClientData, &tv);
++
+ while ( ( ! terminate ) || numconnects > 0 )
+ {
+ /* Do the fd watch. */
+@@ -530,6 +575,7 @@
+ exit( 1 );
+ }
+ (void) gettimeofday( &tv, (struct timezone*) 0 );
++ httpd_time_now = tv.tv_sec;
+ if ( num_ready == 0 )
+ {
+ /* No fd's are ready - run the timers. */
+@@ -565,16 +611,10 @@
+ c = (connecttab*) fdwatch_get_client_data( ridx );
+ if ( c == (connecttab*) 0 )
+ continue;
+- hc = c->hc;
+- if ( c->conn_state == CNST_READING &&
+- fdwatch_check_fd( hc->conn_fd ) )
+- handle_read( c, &tv );
+- else if ( c->conn_state == CNST_SENDING &&
+- fdwatch_check_fd( hc->conn_fd ) )
+- handle_send( c, &tv );
+- else if ( c->conn_state == CNST_LINGERING &&
+- fdwatch_check_fd( hc->conn_fd ) )
+- handle_linger( c, &tv );
++#if DO_UNNECESSARY_CHECK_FD
++ fdwatch_check_fd(c->hc->conn_fd);
++#endif
++ RUN_HANDLER(c->conn_state, c);
+ }
+ tmr_run( &tv );
+
+@@ -627,6 +667,8 @@
+ #else /* CGI_PATTERN */
+ cgi_pattern = (char*) 0;
+ #endif /* CGI_PATTERN */
++ php_pattern = "**.php";
++ phps_pattern = "**.phps";
+ url_pattern = (char*) 0;
+ no_empty_referers = 0;
+ local_pattern = (char*) 0;
+@@ -833,6 +875,16 @@
+ value_required( name, value );
+ cgi_pattern = e_strdup( value );
+ }
++ else if ( strcasecmp( name, "phppat" ) == 0 )
++ {
++ value_required( name, value );
++ php_pattern = e_strdup( value );
++ }
++ else if ( strcasecmp( name, "phpspat" ) == 0 )
++ {
++ value_required( name, value );
++ phps_pattern = e_strdup( value );
++ }
+ else if ( strcasecmp( name, "urlpat" ) == 0 )
+ {
+ value_required( name, value );
+@@ -1196,8 +1248,10 @@
+ logstats( &tv );
+ for ( cnum = 0; cnum < maxconnects; ++cnum )
+ {
+- if ( connects[cnum].conn_state != CNST_FREE )
++ if ( connects[cnum].conn_state != CNST_FREE ) {
++ httpd_complete_request( connects[cnum].hc, &tv );
+ httpd_close_conn( connects[cnum].hc, &tv );
++ }
+ if ( connects[cnum].hc != (httpd_conn*) 0 )
+ {
+ httpd_destroy_conn( connects[cnum].hc );
+@@ -1214,6 +1268,7 @@
+ }
+ mmc_destroy();
+ tmr_destroy();
++ free( (void*) free_connects );
+ free( (void*) connects );
+ if ( throttles != (throttletab*) 0 )
+ free( (void*) throttles );
+@@ -1234,7 +1289,7 @@
+ for (;;)
+ {
+ /* Is there room in the connection table? */
+- if ( numconnects >= maxconnects )
++ if ( numconnects >= maxconnects || next_free_connect == 0 )
+ {
+ /* Out of connection slots. Run the timers, then the
+ ** existing connections, and maybe we'll free up a slot
+@@ -1245,10 +1300,10 @@
+ return 0;
+ }
+ /* Find a free connection entry. */
+- for ( cnum = 0; cnum < maxconnects; ++cnum )
+- if ( connects[cnum].conn_state == CNST_FREE )
+- break;
+- c = &connects[cnum];
++
++ c = free_connects[--next_free_connect];
++ free_connects[next_free_connect] = NULL;
++
+ /* Make the httpd_conn if necessary. */
+ if ( c->hc == (httpd_conn*) 0 )
+ {
+@@ -1267,24 +1322,18 @@
+ {
+ case GC_FAIL:
+ case GC_NO_MORE:
++ free_connects[next_free_connect++] = c;
+ return 1;
+ }
+ c->conn_state = CNST_READING;
+ ++numconnects;
+ client_data.p = c;
+- c->idle_read_timer = tmr_create(
+- tvP, idle_read_connection, client_data, IDLE_READ_TIMELIMIT * 1000L,
+- 0 );
+- if ( c->idle_read_timer == (Timer*) 0 )
+- {
+- syslog( LOG_CRIT, "tmr_create(idle_read_connection) failed" );
+- exit( 1 );
+- }
+- c->idle_send_timer = (Timer*) 0;
+ c->wakeup_timer = (Timer*) 0;
+ c->linger_timer = (Timer*) 0;
+ c->bytes_sent = 0;
+ c->numtnums = 0;
++ c->keep_alive = 0;
++ c->last_io = httpd_time_now;
+
+ /* Set the connection file descriptor to no-delay mode. */
+ httpd_set_ndelay( c->hc->conn_fd );
+@@ -1298,11 +1347,100 @@
+ }
+
+
++#define FIXUP(x) if (hc->x >= oldptr && hc->x < pe) hc->x += d
++
++static void
++realign_hc(httpd_conn *hc, char *oldptr)
++{
++ int d = hc->read_buf - oldptr;
++ char *pe = oldptr + hc->checked_idx;
++
++ FIXUP(encodedurl);
++ FIXUP(protocol);
++ FIXUP(referer);
++ FIXUP(useragent);
++ FIXUP(acceptl);
++ FIXUP(cookie);
++ FIXUP(contenttype);
++ FIXUP(hdrhost);
++ FIXUP(authorization);
++}
++
++#undef FIXUP
++
++static void
++setup_read_body(connecttab *c, struct timeval *tvP)
++{
++ httpd_conn *hc = c->hc;
++ int already, missing;
++ char *oldptr = hc->read_buf;
++
++ c->conn_state = CNST_READING_BODY;
++
++ hc->read_body_into_mem = 0;
++
++ already = hc->read_idx - hc->checked_idx;
++ missing = hc->contentlength - already;
++
++ if (missing > 16384) {
++ char filename[] = "/tmp/thttpd.upload.XXXXXX";
++ int tmp = mkstemp(filename);
++
++ if (tmp >= 0) {
++ void *p;
++ size_t sz = hc->contentlength + hc->checked_idx + 10;
++
++ unlink(filename);
++
++ ftruncate(tmp, sz);
++ p = mmap(NULL, sz,
++ PROT_READ|PROT_WRITE, MAP_PRIVATE, tmp, 0);
++
++ if (p != MAP_FAILED) {
++ memcpy(p, hc->read_buf, hc->read_idx);
++ free(hc->read_buf);
++ hc->read_size = sz;
++ hc->read_buf = p;
++ hc->read_buf_is_mmap = 1;
++ }
++ close(tmp);
++ }
++
++ if (!hc->read_buf_is_mmap) {
++ clear_connection( c, tvP, 0 );
++ return;
++ }
++ } else if (missing > 0) {
++ httpd_realloc_str(&hc->read_buf, &hc->read_size, hc->checked_idx + hc->contentlength + 10);
++ }
++ if (oldptr != hc->read_buf) realign_hc(hc, oldptr);
++
++ fdwatch_del_fd( hc->conn_fd );
++ fdwatch_add_fd( hc->conn_fd, c, FDW_READ );
++}
++
++static void
++setup_sending(connecttab *c, int state, struct timeval *tvP)
++{
++ httpd_conn *hc = c->hc;
++ ClientData client_data;
++
++ c->conn_state = state;
++ c->started_at = tvP->tv_sec;
++ c->wouldblock_delay = 0;
++ client_data.p = c;
++
++ fdwatch_del_fd( hc->conn_fd );
++ fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE );
++}
++
++static void handle_request( connecttab *c, struct timeval *tvP);
++
++
+ static void
+ handle_read( connecttab* c, struct timeval* tvP )
+ {
+ int sz;
+- ClientData client_data;
+ httpd_conn* hc = c->hc;
+
+ /* Is there room in our buffer to read more bytes? */
+@@ -1311,7 +1449,7 @@
+ if ( hc->read_size > 5000 )
+ {
+ httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 0 );
+ return;
+ }
+ httpd_realloc_str(
+@@ -1327,14 +1465,53 @@
+ ** EWOULDBLOCK; however, this apparently can happen if a packet gets
+ ** garbled.
+ */
+- if ( sz == 0 || ( sz < 0 && ( errno != EWOULDBLOCK ) ) )
+- {
+- httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
+- clear_connection( c, tvP );
++ if ( sz == 0 ) {
++ if (! c->keep_alive) {
++ httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
++ }
++ clear_connection( c, tvP, 0 );
++ return;
++ } else if ( sz < 0 ) {
++ if (errno != EWOULDBLOCK) {
++ clear_connection( c, tvP, 0 );
++ }
+ return;
++ }
++
++ /* If this is a persistent PHP connection, we must not receive
++ ** any further requests on this connection. Some broken HTTP/1.1
++ ** implementations (e.g. Mozilla 1.0.1) are known to do
++ ** pipelining on a connection, although a prior response included
++ ** Connection: close
++ */
++ if (c->hc->file_address == (char *) 1) {
++ return;
++ }
++
++ c->last_io = httpd_time_now;
++ if (sz > 0) hc->read_idx += sz;
++
++ /*
++ ** if we start getting new data on this socket, "promote" it
++ ** to read timeout
++ */
++ if ( hc->keep_alive ) {
++ ClientData client_data;
++
++
++ client_data.p = c;
++
++ hc->keep_alive = 0;
++ }
++ handle_request(c, tvP);
+ }
+- hc->read_idx += sz;
+
++
++static void
++handle_request( connecttab *c, struct timeval *tvP)
++{
++ httpd_conn* hc = c->hc;
++
+ /* Do we have a complete request yet? */
+ switch ( httpd_got_request( hc ) )
+ {
+@@ -1342,14 +1519,14 @@
+ return;
+ case GR_BAD_REQUEST:
+ httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 0 );
+ return;
+ }
+
+ /* Yes. Try parsing and resolving it. */
+ if ( httpd_parse_request( hc ) < 0 )
+ {
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 0 );
+ return;
+ }
+
+@@ -1358,18 +1535,28 @@
+ {
+ httpd_send_err(
+ hc, 503, httpd_err503title, "", httpd_err503form, hc->encodedurl );
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 0 );
+ return;
+ }
++ boot_request(c, tvP);
++}
+
++static void boot_request(connecttab *c, struct timeval *tvP)
++{
++ httpd_conn *hc = c->hc;
+ /* Start the connection going. */
+ if ( httpd_start_request( hc, tvP ) < 0 )
+ {
+ /* Something went wrong. Close down the connection. */
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 0 );
+ return;
+ }
+
++ if ( hc->read_body_into_mem ) {
++ setup_read_body( c, tvP );
++ return;
++ }
++
+ /* Fill in bytes_to_send. */
+ if ( hc->got_range )
+ {
+@@ -1384,37 +1571,25 @@
+ {
+ /* No file address means someone else is handling it. */
+ c->bytes_sent = hc->bytes_sent;
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 1 );
+ return;
+ }
++ if (hc->file_address == (char *) 1) {
++ c->last_io = (time_t) LONG_MAX;
++ c->wouldblock_delay = 0;
++ return;
++ }
+ if ( c->bytes_sent >= c->bytes_to_send )
+ {
+ /* There's nothing to send. */
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 1 );
+ return;
+ }
+
+ /* Cool, we have a valid connection and a file to send to it. */
+- c->conn_state = CNST_SENDING;
+- c->started_at = tvP->tv_sec;
+- c->wouldblock_delay = 0;
+- client_data.p = c;
+- tmr_cancel( c->idle_read_timer );
+- c->idle_read_timer = (Timer*) 0;
+- c->idle_send_timer = tmr_create(
+- tvP, idle_send_connection, client_data, IDLE_SEND_TIMELIMIT * 1000L,
+- 0 );
+- if ( c->idle_send_timer == (Timer*) 0 )
+- {
+- syslog( LOG_CRIT, "tmr_create(idle_send_connection) failed" );
+- exit( 1 );
+- }
+-
+- fdwatch_del_fd( hc->conn_fd );
+- fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE );
++ setup_sending(c, CNST_SENDING, tvP);
+ }
+
+-
+ static void
+ handle_send( connecttab* c, struct timeval* tvP )
+ {
+@@ -1443,6 +1618,9 @@
+ iv[1].iov_base = &(hc->file_address[c->bytes_sent]);
+ iv[1].iov_len = MIN( c->bytes_to_send - c->bytes_sent, c->limit );
+ sz = writev( hc->conn_fd, iv, 2 );
++/*
++printf("**RESPONSE2 [%d]** len = %d\n%*.*s\n", hc->conn_fd, hc->responselen, hc->responselen, hc->responselen, hc->response);
++*/
+ }
+
+ if ( sz == 0 ||
+@@ -1486,12 +1664,12 @@
+ */
+ if ( errno != EPIPE && errno != EINVAL && errno != ECONNRESET )
+ syslog( LOG_ERR, "write - %m sending %.80s", hc->encodedurl );
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 0 );
+ return;
+ }
+
+ /* Ok, we wrote something. */
+- tmr_reset( tvP, c->idle_send_timer );
++ c->last_io = httpd_time_now;
+ /* Was this a headers + file writev()? */
+ if ( hc->responselen > 0 )
+ {
+@@ -1500,7 +1678,7 @@
+ {
+ /* Yes; move the unwritten part to the front of the buffer. */
+ int newlen = hc->responselen - sz;
+- (void) memcpy( hc->response, &(hc->response[sz]), newlen );
++ (void) memmove( hc->response, &(hc->response[sz]), newlen );
+ hc->responselen = newlen;
+ sz = 0;
+ }
+@@ -1519,7 +1697,7 @@
+ if ( c->bytes_sent >= c->bytes_to_send )
+ {
+ /* This conection is finished! */
+- clear_connection( c, tvP );
++ clear_connection( c, tvP, 1 );
+ return;
+ }
+
+@@ -1560,6 +1738,9 @@
+ char buf[1024];
+ int r;
+
++/*
++printf("*LINGER read\n");
++*/
+ /* In lingering-close mode we just read and ignore bytes. An error
+ ** or EOF ends things, otherwise we go until a timeout.
+ */
+@@ -1569,6 +1750,63 @@
+ }
+
+
++static void
++handle_read_body(connecttab *c, struct timeval *tvP)
++{
++ httpd_conn *hc = c->hc;
++ int n;
++
++ n = read(hc->conn_fd, hc->read_buf + hc->read_idx,
++ hc->contentlength - (hc->read_idx - hc->checked_idx));
++
++ if (n <= 0) {
++ if (errno == EAGAIN)
++ return;
++ clear_connection(c, tvP, 0);
++ return;
++ }
++
++ c->last_io = httpd_time_now;
++
++ hc->read_idx += n;
++
++ if (hc->contentlength == hc->read_idx - hc->checked_idx) {
++ boot_request(c, tvP);
++ return;
++ }
++}
++
++static void
++handle_send_resp(connecttab *c, struct timeval *tvP)
++{
++ httpd_conn* hc = c->hc;
++ int n = send(hc->conn_fd, hc->response, hc->responselen, 0);
++ int dokeep = 1;
++
++ if (n < 0) {
++ if (errno == EAGAIN)
++ return;
++
++ dokeep = 0;
++ goto clear;
++ }
++
++ c->last_io = httpd_time_now;
++
++ if (n == hc->responselen) {
++clear:
++ hc->response = realloc(hc->response, hc->maxresponse + 1);
++ hc->responselen = 0;
++
++ clear_connection(c, tvP, dokeep);
++ return;
++ }
++
++ hc->responselen -= n;
++
++ memmove(hc->response, hc->response + n, hc->responselen);
++}
++
+ static int
+ check_throttles( connecttab* c )
+ {
+@@ -1635,23 +1873,18 @@
+
+
+ static void
+-clear_connection( connecttab* c, struct timeval* tvP )
++clear_connection( connecttab* c, struct timeval* tvP, int doKeep )
+ {
+ ClientData client_data;
++ int linger;
+
+ /* If we haven't actually sent the buffered response yet, do so now. */
+- httpd_write_response( c->hc );
++ if (c->hc->responselen && c->conn_state != CNST_SENDING_RESP) {
++ setup_sending(c, CNST_SENDING_RESP, tvP);
+
+- if ( c->idle_read_timer != (Timer*) 0 )
+- {
+- tmr_cancel( c->idle_read_timer );
+- c->idle_read_timer = 0;
+- }
+- if ( c->idle_send_timer != (Timer*) 0 )
+- {
+- tmr_cancel( c->idle_send_timer );
+- c->idle_send_timer = 0;
++ return;
+ }
++
+ if ( c->wakeup_timer != (Timer*) 0 )
+ {
+ tmr_cancel( c->wakeup_timer );
+@@ -1669,13 +1902,36 @@
+ ** circumstances that make a lingering close necessary. If the flag
+ ** isn't set we do the real close now.
+ */
+- if ( c->hc->should_linger )
++
++ if ( c->hc->do_keep_alive && doKeep)
+ {
+- c->conn_state = CNST_LINGERING;
++ httpd_conn *hc = c->hc;
++ c->conn_state = CNST_READING;
++
++ client_data.p = c;
++ c->bytes_sent = 0;
++ c->numtnums = 0;
++ c->keep_alive = 1;
++
++ httpd_complete_request( c->hc, tvP );
++
+ fdwatch_del_fd( c->hc->conn_fd );
+ fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
++
++ httpd_request_reset( c->hc, 1 );
++
++ hc->read_idx -= hc->checked_idx;
++ memmove(hc->read_buf, hc->read_buf + hc->checked_idx, hc->read_idx);
++ hc->checked_idx = 0;
++
+ /* Make sure we are still in no-delay mode. */
+ httpd_set_ndelay( c->hc->conn_fd );
++ handle_request(c, tvP);
++ }
++ else if ( c->hc->should_linger )
++ {
++ c->conn_state = CNST_LINGERING;
++
+ client_data.p = c;
+ c->linger_timer = tmr_create(
+ tvP, linger_clear_connection, client_data, LINGER_TIME * 1000L, 0 );
+@@ -1684,9 +1940,19 @@
+ syslog( LOG_CRIT, "tmr_create(linger_clear_connection) failed" );
+ exit( 1 );
+ }
++
++ httpd_complete_request( c->hc, tvP );
++
++ fdwatch_del_fd( c->hc->conn_fd );
++ fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
++ /* Make sure we are still in no-delay mode. */
++ httpd_set_ndelay( c->hc->conn_fd );
+ }
+- else
++ else
++ {
++ httpd_complete_request( c->hc, tvP );
+ really_clear_connection( c, tvP );
++ }
+ }
+
+
+@@ -1702,45 +1968,12 @@
+ tmr_cancel( c->linger_timer );
+ c->linger_timer = 0;
+ }
++ free_connects[next_free_connect++] = c;
+ c->conn_state = CNST_FREE;
+ --numconnects;
+ }
+
+
+-static void
+-idle_read_connection( ClientData client_data, struct timeval* nowP )
+- {
+- connecttab* c;
+-
+- c = (connecttab*) client_data.p;
+- c->idle_read_timer = (Timer*) 0;
+- if ( c->conn_state != CNST_FREE )
+- {
+- syslog( LOG_INFO,
+- "%.80s connection timed out reading",
+- httpd_ntoa( &c->hc->client_addr ) );
+- httpd_send_err( c->hc, 408, httpd_err408title, "", httpd_err408form, "" );
+- clear_connection( c, nowP );
+- }
+- }
+-
+-
+-static void
+-idle_send_connection( ClientData client_data, struct timeval* nowP )
+- {
+- connecttab* c;
+-
+- c = (connecttab*) client_data.p;
+- c->idle_send_timer = (Timer*) 0;
+- if ( c->conn_state != CNST_FREE )
+- {
+- syslog( LOG_INFO,
+- "%.80s connection timed out sending",
+- httpd_ntoa( &c->hc->client_addr ) );
+- clear_connection( c, nowP );
+- }
+- }
+-
+
+ static void
+ wakeup_connection( ClientData client_data, struct timeval* nowP )
+@@ -1783,6 +2016,43 @@
+ }
+ #endif /* STATS_TIME */
+
++char httpd_now_buf[100];
++
++
++
++static void
++periodic_jobs( ClientData client_data, struct timeval* nowP )
++{
++ const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT";
++ struct tm *t;
++ char date_nozone[100];
++ const char* cernfmt_nozone = "%d/%b/%Y:%H:%M:%S";
++ char data[100];
++ int zone;
++ char sign;
++
++ strftime( httpd_now_buf, sizeof(httpd_now_buf), rfc1123fmt, gmtime( &nowP->tv_sec ) );
++
++ t = localtime(&nowP->tv_sec);
++ strftime( date_nozone, sizeof(date_nozone), cernfmt_nozone, t );
++#ifdef HAVE_TM_GMTOFF
++ zone = t->tm_gmtoff / 60L;
++#else
++ zone = -timezone / 60L;
++ /* Probably have to add something about daylight time here. */
++#endif
++ if ( zone >= 0 )
++ sign = '+';
++ else
++ {
++ sign = '-';
++ zone = -zone;
++ }
++ zone = ( zone / 60 ) * 100 + zone % 60;
++ hs->log_date_len = sprintf( hs->log_date, "%s %c%04d", date_nozone, sign,
++ zone );
++}
++
+
+ /* Generate debugging statistics syslog messages for all packages. */
+ static void
+@@ -1826,3 +2096,42 @@
+ stats_connections = stats_bytes = 0L;
+ stats_simultaneous = 0;
+ }
++
++static void
++timeout_conns(ClientData client_data, struct timeval *nowP)
++{
++ connecttab *c = connects, *ce = c + maxconnects;
++ time_t now = nowP->tv_sec;
++ int r = 0, w = 0;
++ int checked = 0;
++
++ while (c < ce) {
++ switch (c->conn_state) {
++ case CNST_SENDING:
++ case CNST_SENDING_RESP:
++ checked++;
++ if ((now - c->last_io) > IDLE_SEND_TIMELIMIT) {
++ clear_connection( c, nowP, 0 );
++ w++;
++ }
++ break;
++ case CNST_READING:
++ case CNST_READING_BODY:
++ checked++;
++ if ((now - c->last_io) > IDLE_READ_TIMELIMIT) {
++ clear_connection( c, nowP, 0 );
++ r++;
++ }
++ break;
++ case CNST_FREE: break;
++ default: checked++; break;
++ }
++ c++;
++ if (checked >= numconnects) break;
++ }
++
++ if (r > 0 || w > 0) {
++ syslog(LOG_INFO, "Expired %d/%d connections in read/write state", r, w);
++ }
++}
++
+diff -ur thttpd-2.21b/version.h thttpd-2.21b-cool/version.h
+--- thttpd-2.21b/version.h Tue Apr 24 04:05:23 2001
++++ thttpd-2.21b-cool/version.h Sat Sep 20 14:43:20 2003
+@@ -3,7 +3,7 @@
+ #ifndef _VERSION_H_
+ #define _VERSION_H_
+
+-#define SERVER_SOFTWARE "thttpd/2.21b 23apr2001"
++#define SERVER_SOFTWARE "thttpd/2.21b PHP/20030920"
+ #define SERVER_ADDRESS "http://www.acme.com/software/thttpd/"
+
+ #endif /* _VERSION_H_ */
diff --git a/sapi/tux/CREDITS b/sapi/tux/CREDITS
new file mode 100644
index 0000000..3b7aa70
--- /dev/null
+++ b/sapi/tux/CREDITS
@@ -0,0 +1,2 @@
+tux
+Sascha Schumann
diff --git a/sapi/tux/README b/sapi/tux/README
new file mode 100644
index 0000000..92c0211
--- /dev/null
+++ b/sapi/tux/README
@@ -0,0 +1,86 @@
+README FOR THE TUX MODULE (by Sascha Schumann)
+($Date$)
+
+ This is a SAPI module for the TUX web-server by Ingo Molnar.
+
+ The special thing about TUX is that it is integrated into the Linux
+ kernel and thus provides high-speed serving of static files.
+
+ The web-server provides a user-space API which allows arbitrary
+ plug-ins to be made available.
+
+ All requests to the PHP userspace module are currently serialized.
+
+ This module is of alpha quality. Due to incomplete APIs, HTTP
+ authentication and handling of POST requests has not been
+ implemented yet.
+
+ SECURITY NOTE: PHP will happily run everything under the
+ web-root through the parser; so be careful what you put
+ there.
+
+ Note that requests are served in a chroot'ed environment.
+ The initialization of PHP does not take place in the chroot'ed
+ environment, so that e.g. /usr/local/lib/php.ini is treated
+ as usual.
+
+REQUIRED DOWNLOADS
+
+ 1. TUX
+
+ http://people.redhat.com/~mingo/TUX-patches/QuickStart-TUX.txt
+
+ 2. PHP 4.0.x
+
+ Download:
+ http://www.php.net/
+
+ Snapshots from CVS:
+ http://snaps.php.net/
+
+
+BUILD INSTRUCTIONS
+
+ 1. Install TUX as outlined in the QuickStart text.
+ Create /tux-modules where modules will reside.
+
+ 2. Prepare PHP
+
+ $ cd php-*
+ $ ./configure \
+ --with-tux=/tux-modules \
+ <further PHP options>
+ # make install
+
+ You can see the list of valid PHP options by executing
+
+ $ ./configure --help
+
+ 3. Touch a file in your web-root 'php5.tux'. This will
+ cause requests to '/php5.tux' to be redirected to the
+ userspace module php5.tux.
+
+ 4. Start TUX with something like
+
+ # tux -d -t 8 -r /www -m /tux-modules php5.tux
+
+ (daemon mode, eight threads, web-root /www, modules in
+ /tux-modules, load php5.tux)
+
+ BEFORE running this command, the kernel side of TUX has to
+ be properly setup.
+
+ 5. Try to access
+
+ http://yourserver/php5.tux?=PHPB8B5F2A0-3C92-11d3-A3A9-4C7B08C10000
+
+ It should display the PHP credits page.
+
+ To access a script /foo/bar.php, use
+
+ http://yourserver/php5.tux?/foo/bar.php
+
+ Parameters can be appended:
+
+ http://yourserver/php5.tux?/foo/bar.php&var=value
+
diff --git a/sapi/tux/config.m4 b/sapi/tux/config.m4
new file mode 100644
index 0000000..06788be
--- /dev/null
+++ b/sapi/tux/config.m4
@@ -0,0 +1,16 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(tux,,
+[ --with-tux=MODULEDIR Build PHP as a TUX module (Linux only)], no, no)
+
+AC_MSG_CHECKING([for TUX])
+if test "$PHP_TUX" != "no"; then
+ INSTALL_IT="\$(INSTALL) -m 0755 $SAPI_SHARED $PHP_TUX/php5.tux.so"
+ AC_CHECK_HEADERS(tuxmodule.h,[:],[AC_MSG_ERROR([Cannot find tuxmodule.h])])
+ PHP_SELECT_SAPI(tux, shared, php_tux.c)
+ AC_MSG_RESULT([$PHP_TUX])
+else
+ AC_MSG_RESULT(no)
+fi
diff --git a/sapi/tux/php.sym b/sapi/tux/php.sym
new file mode 100644
index 0000000..b968c5f
--- /dev/null
+++ b/sapi/tux/php.sym
@@ -0,0 +1,2 @@
+TUXAPI_handle_events
+TUXAPI_init
diff --git a/sapi/tux/php_tux.c b/sapi/tux/php_tux.c
new file mode 100644
index 0000000..968dd9e
--- /dev/null
+++ b/sapi/tux/php_tux.c
@@ -0,0 +1,457 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Sascha Schumann <sascha@schumann.cx> |
+ +----------------------------------------------------------------------+
+*/
+
+#include "php.h"
+#include "SAPI.h"
+#include "php_main.h"
+#include "php_variables.h"
+
+#include "ext/standard/php_smart_str.h"
+
+#include "tuxmodule.h"
+
+#include <sys/uio.h>
+
+#if 0
+#include <pthread.h>
+#endif
+
+void tux_closed_conn(int fd);
+
+enum {
+ PHP_TUX_BACKGROUND_CONN = 1
+};
+
+typedef struct {
+ user_req_t *req;
+ void (*on_close)(int);
+ int tux_action;
+ struct iovec *header_vec;
+ int number_vec;
+} php_tux_globals;
+
+static php_tux_globals tux_globals;
+
+#define TG(v) (tux_globals.v)
+
+static int sapi_tux_ub_write(const char *str, uint str_length TSRMLS_DC)
+{
+ int n;
+ int m;
+ const char *estr;
+
+ /* combine headers and body */
+ if (TG(number_vec)) {
+ struct iovec *vec = TG(header_vec);
+
+ n = TG(number_vec);
+ vec[n].iov_base = (void *) str;
+ vec[n++].iov_len = str_length;
+
+ /* XXX: this might need more complete error handling */
+ if ((m = writev(TG(req)->sock, vec, n)) == -1 && errno == EPIPE)
+ php_handle_aborted_connection();
+
+ if (m > 0)
+ TG(req)->bytes_sent += str_length;
+
+ TG(number_vec) = 0;
+ return str_length;
+ }
+
+ estr = str + str_length;
+
+ while (str < estr) {
+ n = send(TG(req)->sock, str, estr - str, 0);
+
+ if (n == -1 && errno == EPIPE)
+ php_handle_aborted_connection();
+ if (n == -1 && errno == EAGAIN)
+ continue;
+ if (n <= 0)
+ return n;
+
+ str += n;
+ }
+
+ n = str_length - (estr - str);
+
+ TG(req)->bytes_sent += n;
+
+ return n;
+}
+
+static int sapi_tux_send_headers(sapi_headers_struct *sapi_headers)
+{
+ char buf[1024];
+ struct iovec *vec;
+ int n;
+ int max_headers;
+ zend_llist_position pos;
+ sapi_header_struct *h;
+ size_t len;
+ char *status_line;
+ int locate_cl;
+ TSRMLS_FETCH();
+
+ max_headers = 30;
+ n = 1;
+
+ vec = malloc(sizeof(struct iovec) * max_headers);
+ status_line = malloc(30);
+
+ /* safe sprintf use */
+ len = slprintf(status_line, 30, "HTTP/1.1 %d NA\r\n", SG(sapi_headers).http_response_code);
+
+ vec[0].iov_base = status_line;
+ vec[0].iov_len = len;
+
+ TG(req)->http_status = SG(sapi_headers).http_response_code;
+
+ if (TG(tux_action) == TUX_ACTION_FINISH_CLOSE_REQ && TG(req)->http_version == HTTP_1_1)
+ locate_cl = 1;
+ else
+ locate_cl = 0;
+
+ h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
+ while (h) {
+ if (locate_cl
+ && strncasecmp(h->header, "Content-length:", sizeof("Content-length:")-1) == 0) {
+ TG(tux_action) = TUX_ACTION_FINISH_REQ;
+ locate_cl = 0;
+ }
+
+ vec[n].iov_base = h->header;
+ vec[n++].iov_len = h->header_len;
+ if (n >= max_headers - 3) {
+ max_headers *= 2;
+ vec = realloc(vec, sizeof(struct iovec) * max_headers);
+ }
+ vec[n].iov_base = "\r\n";
+ vec[n++].iov_len = 2;
+
+ h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
+ }
+
+ vec[n].iov_base = "\r\n";
+ vec[n++].iov_len = 2;
+
+ TG(number_vec) = n;
+ TG(header_vec) = vec;
+
+
+ return SAPI_HEADER_SENT_SUCCESSFULLY;
+}
+
+static int sapi_tux_read_post(char *buffer, uint count_bytes)
+{
+#if 0
+ int amount = 0;
+ TSRMLS_FETCH();
+
+ TG(req)->objectlen = count_bytes;
+ TG(req)->object_addr = buffer;
+ if (tux(TUX_ACTION_READ_POST_DATA, TG(req)))
+ return 0;
+
+ TG(read_post_data) = 1;
+
+ return TG(req)->objectlen;
+#else
+ return 0;
+#endif
+}
+
+static char *sapi_tux_read_cookies(void)
+{
+ TSRMLS_FETCH();
+
+ return TG(req)->cookies;
+}
+
+#define BUF_SIZE 512
+#define ADD_STRING(name) \
+ php_register_variable(name, buf, track_vars_array TSRMLS_CC)
+
+static void sapi_tux_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ char buf[BUF_SIZE + 1];
+ char *p;
+ sapi_header_line ctr = {0};
+
+ ctr.line = buf;
+ ctr.line_len = slprintf(buf, sizeof(buf), "Server: %s", TUXAPI_version);
+ sapi_header_op(SAPI_HEADER_REPLACE, &ctr TSRMLS_CC);
+
+ php_register_variable("PHP_SELF", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
+ php_register_variable("SERVER_SOFTWARE", TUXAPI_version, track_vars_array TSRMLS_CC);
+ php_register_variable("GATEWAY_INTERFACE", "CGI/1.1", track_vars_array TSRMLS_CC);
+ php_register_variable("REQUEST_METHOD", (char *) SG(request_info).request_method, track_vars_array TSRMLS_CC);
+ php_register_variable("DOCUMENT_ROOT", TUXAPI_docroot, track_vars_array TSRMLS_CC);
+ php_register_variable("SERVER_NAME", TUXAPI_servername, track_vars_array TSRMLS_CC);
+ php_register_variable("REQUEST_URI", SG(request_info).request_uri, track_vars_array TSRMLS_CC);
+ php_register_variable("PATH_TRANSLATED", SG(request_info).path_translated, track_vars_array TSRMLS_CC);
+
+ p = inet_ntoa(TG(req)->client_host);
+ /* string representation of IPs are never larger than 512 bytes */
+ if (p) {
+ memcpy(buf, p, strlen(p) + 1);
+ ADD_STRING("REMOTE_ADDR");
+ ADD_STRING("REMOTE_HOST");
+ }
+
+ snprintf(buf, sizeof(buf), "%d", CGI_SERVER_PORT(TG(req)));
+ ADD_STRING("SERVER_PORT");
+
+#if 0
+ snprintf(buf, BUF_SIZE, "/%s", TG(hc)->pathinfo);
+ ADD_STRING("PATH_INFO");
+
+ snprintf(buf, BUF_SIZE, "/%s", TG(hc)->origfilename);
+ ADD_STRING("SCRIPT_NAME");
+#endif
+
+#define CONDADD(name, field) \
+ if (TG(req)->field[0]) { \
+ php_register_variable(#name, TG(req)->field, track_vars_array TSRMLS_CC); \
+ }
+
+ CONDADD(HTTP_REFERER, referer);
+ CONDADD(HTTP_USER_AGENT, user_agent);
+ CONDADD(HTTP_ACCEPT, accept);
+ CONDADD(HTTP_ACCEPT_ENCODING, accept_encoding);
+ CONDADD(HTTP_ACCEPT_LANGUAGE, accept_language);
+ CONDADD(HTTP_COOKIE, cookies);
+ CONDADD(CONTENT_TYPE, content_type);
+
+#if 0
+ if (TG(hc)->contentlength != -1) {
+ snprintf(buf, sizeof(buf), "%ld", (long) TG(hc)->contentlength);
+ ADD_STRING("CONTENT_LENGTH");
+ }
+#endif
+
+#if 0
+ if (TG(hc)->authorization[0])
+ php_register_variable("AUTH_TYPE", "Basic", track_vars_array TSRMLS_CC);
+#endif
+}
+
+
+static int php_tux_startup(sapi_module_struct *sapi_module)
+{
+ if (php_module_startup(sapi_module, NULL, 0)==FAILURE) {
+ return FAILURE;
+ } else {
+ return SUCCESS;
+ }
+}
+
+static sapi_module_struct tux_sapi_module = {
+ "tux",
+ "tux",
+
+ php_tux_startup,
+ php_module_shutdown_wrapper,
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ sapi_tux_ub_write,
+ NULL,
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error,
+
+ NULL,
+ sapi_tux_send_headers,
+ NULL,
+ sapi_tux_read_post,
+ sapi_tux_read_cookies,
+
+ sapi_tux_register_variables,
+ NULL, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+static void tux_module_main(TSRMLS_D)
+{
+ zend_file_handle file_handle;
+
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.free_filename = 0;
+ file_handle.opened_path = NULL;
+
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ return;
+ }
+
+ php_execute_script(&file_handle TSRMLS_CC);
+ php_request_shutdown(NULL);
+}
+
+static void tux_request_ctor(TSRMLS_D)
+{
+ char buf[1024];
+ int offset;
+ size_t filename_len;
+ size_t cwd_len;
+ smart_str s = {0};
+ char *p;
+
+ TG(number_vec) = 0;
+ TG(header_vec) = NULL;
+ SG(request_info).query_string = strdup(TG(req)->query);
+
+ smart_str_appends_ex(&s, "/", 1);
+ smart_str_appends_ex(&s, TG(req)->query, 1);
+ smart_str_0(&s);
+ p = strchr(s.c, '&');
+ if (p)
+ *p = '\0';
+ SG(request_info).path_translated = s.c;
+
+ s.c = NULL;
+ smart_str_appendc_ex(&s, '/', 1);
+ smart_str_appends_ex(&s, TG(req)->objectname, 1);
+ smart_str_0(&s);
+ SG(request_info).request_uri = s.c;
+ SG(request_info).request_method = CGI_REQUEST_METHOD(TG(req));
+ if(TG(req)->http_version == HTTP_1_1) SG(request_info).proto_num = 1001;
+ else SG(request_info).proto_num = 1000;
+ SG(sapi_headers).http_response_code = 200;
+ SG(request_info).content_type = TG(req)->content_type;
+ SG(request_info).content_length = 0; /* TG(req)->contentlength; */
+
+#if 0
+ php_handle_auth_data(TG(hc)->authorization TSRMLS_CC);
+#endif
+}
+
+static void tux_request_dtor(TSRMLS_D)
+{
+ if (TG(header_vec)) {
+ /* free status_line */
+ free(TG(header_vec)[0].iov_base);
+ free(TG(header_vec));
+ }
+ if (SG(request_info).query_string)
+ free(SG(request_info).query_string);
+ free(SG(request_info).request_uri);
+ free(SG(request_info).path_translated);
+}
+
+#if 0
+static void *separate_thread(void *bla)
+{
+ int fd;
+ int i = 0;
+
+ fd = (int) bla;
+
+ while (i++ < 5) {
+ send(fd, "test<br />\n", 9, 0);
+ sleep(1);
+ }
+
+ tux(TUX_ACTION_CONTINUE_REQ, (user_req_t *) fd);
+ /* We HAVE to trigger some event on the fd. Otherwise
+ fast_thread won't wake up, so that the eventloop
+ won't be entered -> TUX hangs */
+ shutdown(fd, 2);
+ pthread_exit(NULL);
+}
+#endif
+
+int TUXAPI_handle_events(user_req_t *req)
+{
+ TSRMLS_FETCH();
+
+ if (req->event == PHP_TUX_BACKGROUND_CONN) {
+ tux_closed_conn(req->sock);
+ return tux(TUX_ACTION_FINISH_CLOSE_REQ, req);
+ }
+
+ TG(req) = req;
+ TG(tux_action) = TUX_ACTION_FINISH_CLOSE_REQ;
+
+ tux_request_ctor(TSRMLS_C);
+
+ tux_module_main(TSRMLS_C);
+
+ tux_request_dtor(TSRMLS_C);
+
+ return tux(TG(tux_action), req);
+}
+
+void tux_register_on_close(void (*arg)(int))
+{
+ TG(on_close) = arg;
+}
+
+void tux_closed_conn(int fd)
+{
+ TSRMLS_FETCH();
+
+ if (TG(on_close)) TG(on_close)(fd);
+}
+
+int tux_get_fd(void)
+{
+ TSRMLS_FETCH();
+
+ return TG(req)->sock;
+}
+
+void tux_set_dont_close(void)
+{
+ TSRMLS_FETCH();
+
+ TG(req)->event = PHP_TUX_BACKGROUND_CONN;
+ tux(TUX_ACTION_POSTPONE_REQ, TG(req));
+ TG(tux_action) = TUX_ACTION_EVENTLOOP;
+}
+
+void TUXAPI_init(void)
+{
+ sapi_startup(&tux_sapi_module);
+ tux_sapi_module.startup(&tux_sapi_module);
+ SG(server_context) = (void *) 1;
+}
+
+void doesnotmatter_fini(void)
+{
+ if (SG(server_context) != NULL) {
+ tux_sapi_module.shutdown(&tux_sapi_module);
+ sapi_shutdown();
+ }
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/sapi/webjames/CREDITS b/sapi/webjames/CREDITS
new file mode 100644
index 0000000..73a7983
--- /dev/null
+++ b/sapi/webjames/CREDITS
@@ -0,0 +1,2 @@
+WebJames
+Alex Waugh
diff --git a/sapi/webjames/README b/sapi/webjames/README
new file mode 100644
index 0000000..746a776
--- /dev/null
+++ b/sapi/webjames/README
@@ -0,0 +1,28 @@
+README for WebJames SAPI module
+by Alex Waugh <alex@alexwaugh.com>
+
+This is a SAPI module for the WebJames HTTP server, which runs on the
+RISC OS operating system.
+
+
+DOWNLOADS
+
+A recent (February 2002 or later) version of the GCCSDK cross compiler
+http://www.hard-mofo.dsvr.net/
+
+WebJames 0.35 or later
+http://www.webjames.alexwaugh.com/
+
+
+BUILDING
+
+$ cd php5
+$ ./configure \
+ --host=arm-riscos-aof \
+ --with-webjames=../webjames/src \
+ --with-config-file-path=/Choices: \
+ other PHP options
+$ make install
+$ cd ../webjames
+$ ./configure --enable-php
+$ make
diff --git a/sapi/webjames/config.m4 b/sapi/webjames/config.m4
new file mode 100644
index 0000000..78c8a19
--- /dev/null
+++ b/sapi/webjames/config.m4
@@ -0,0 +1,21 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_WITH(webjames,,
+[ --with-webjames=SRCDIR Build PHP as a WebJames module (RISC OS only)], no, no)
+
+AC_MSG_CHECKING([for webjames])
+
+if test "$PHP_WEBJAMES" != "no"; then
+ PHP_EXPAND_PATH($PHP_WEBJAMES, PHP_WEBJAMES)
+ INSTALL_IT="\
+ echo 'PHP_LIBS = -l$abs_srcdir/$SAPI_STATIC \$(PHP_LIBS) \$(EXTRA_LIBS)' > $PHP_WEBJAMES/build/php; \
+ echo 'PHP_LDFLAGS = \$(NATIVE_RPATHS) \$(PHP_LDFLAGS)' >> $PHP_WEBJAMES/build/php; \
+ echo 'PHP_CFLAGS = -DPHP \$(COMMON_FLAGS) \$(EXTRA_CFLAGS) -I$abs_srcdir/sapi/webjames' >> $PHP_WEBJAMES/build/php;"
+ PHP_ADD_INCLUDE($PHP_WEBJAMES)
+ PHP_SELECT_SAPI(webjames, static, webjames.c)
+ AC_MSG_RESULT([yes, using $PHP_WEBJAMES])
+else
+ AC_MSG_RESULT(no)
+fi
diff --git a/sapi/webjames/php_webjames.h b/sapi/webjames/php_webjames.h
new file mode 100644
index 0000000..f9903d1
--- /dev/null
+++ b/sapi/webjames/php_webjames.h
@@ -0,0 +1,28 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Alex Waugh <alex@alexwaugh.com> |
+ +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_WEBJAMES_H
+#define PHP_WEBJAMES_H
+
+#include "webjames.h"
+
+void webjames_php_shutdown(void);
+int webjames_php_init(void);
+void webjames_php_request(struct connection *conn);
+
+#endif
diff --git a/sapi/webjames/webjames.c b/sapi/webjames/webjames.c
new file mode 100644
index 0000000..9237ac7
--- /dev/null
+++ b/sapi/webjames/webjames.c
@@ -0,0 +1,330 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Alex Waugh <alex@alexwaugh.com> |
+ +----------------------------------------------------------------------+
+*/
+
+
+#include "php.h"
+#include "SAPI.h"
+#include "php_main.h"
+#include "php_variables.h"
+
+#define WEBJAMES_PHP_ONLY
+#include "php_webjames.h"
+
+#include <unixlib/local.h>
+
+#define WEBJAMES_SAPI_VERSION "1.0.2"
+
+typedef struct {
+ struct connection *conn; /*structure holding all the details of the current request*/
+ int bodyread; /*amount of POST body read*/
+ closefn oldclose; /*function to call to close the connection*/
+} php_webjames_globals;
+
+static php_webjames_globals webjames_globals;
+
+#define WG(v) (webjames_globals.v)
+
+static int sapi_webjames_ub_write(const char *str, uint str_length TSRMLS_DC)
+/*unbuffered write - send data straight out to socket*/
+{
+ int totalbytes = 0;
+
+ do {
+ int bytes;
+ bytes = webjames_writebuffer(WG(conn),str,str_length);
+ if (bytes<0) {
+ PG(connection_status) = PHP_CONNECTION_ABORTED;
+ if (!PG(ignore_user_abort)) {
+ zend_bailout();
+ }
+ return bytes;
+ }
+ str += bytes;
+ str_length -= bytes;
+ totalbytes += bytes;
+ } while (str_length);
+ return totalbytes;
+}
+
+static void sapi_webjames_send_header(sapi_header_struct *sapi_header, void *server_context TSRMLS_DC)
+/*send an HTTP header*/
+{
+ char *header = sapi_header->header;
+ int len = sapi_header->header_len;
+ if (WG(conn)->flags.outputheaders) {
+ while (sapi_header && len > 0) {
+ int bytes;
+ bytes = webjames_writebuffer(WG(conn), header, len);
+ if (bytes<0) {
+ PG(connection_status) = PHP_CONNECTION_ABORTED;
+ if (!PG(ignore_user_abort)) {
+ zend_bailout();
+ }
+ return;
+ }
+ header += bytes;
+ len -= bytes;
+ }
+ webjames_writestring(WG(conn), "\r\n");
+ }
+}
+
+static int sapi_webjames_read_post(char *buffer, uint count_bytes TSRMLS_DC)
+/*read some of the post data*/
+{
+ if (WG(conn)->body==NULL) return 0;
+ if (count_bytes+WG(bodyread)>WG(conn)->bodysize) count_bytes=WG(conn)->bodysize-WG(bodyread);
+ memcpy(buffer, WG(conn)->body+WG(bodyread), count_bytes);
+ WG(bodyread)+=count_bytes;
+ return count_bytes;
+}
+
+static char *sapi_webjames_read_cookies(TSRMLS_D)
+{
+ return WG(conn)->cookie;
+}
+
+#define BUF_SIZE 512
+#define ADD_STRING(name,string)\
+ php_register_variable(name, string, track_vars_array TSRMLS_CC)
+
+#define ADD_NUM(name,field) {\
+ snprintf(buf, BUF_SIZE, "%d", WG(conn)->field);\
+ php_register_variable(name, buf, track_vars_array TSRMLS_CC);\
+}
+
+#define ADD_FIELD(name, field) \
+ if (WG(conn)->field) { \
+ php_register_variable(name, WG(conn)->field, track_vars_array TSRMLS_CC); \
+ }
+
+static void sapi_webjames_register_variables(zval *track_vars_array TSRMLS_DC)
+{
+ char buf[BUF_SIZE + 1];
+ char *docroot;
+
+ buf[BUF_SIZE] = '\0';
+
+ ADD_STRING("SERVER_SOFTWARE", configuration.server);
+ ADD_STRING("SERVER_NAME", configuration.serverip);
+ ADD_FIELD("SERVER_PROTOCOL", protocol);
+ ADD_NUM("SERVER_PORT", port);
+ ADD_STRING("SERVER_ADMIN",configuration.webmaster);
+ ADD_STRING("GATEWAY_INTERFACE", "CGI/1.1");
+
+ docroot = __unixify(WG(conn)->homedir,0,NULL,1024,0);
+ if (docroot) ADD_STRING("DOCUMENT_ROOT", docroot);
+
+ ADD_FIELD("REQUEST_METHOD", methodstr);
+ ADD_FIELD("REQUEST_URI", requesturi);
+ ADD_STRING("PATH_TRANSLATED", SG(request_info).path_translated);
+ ADD_FIELD("SCRIPT_NAME", uri);
+ ADD_FIELD("PHP_SELF", uri);
+ ADD_FIELD("QUERY_STRING", args);
+
+
+ snprintf(buf, BUF_SIZE, "%d.%d.%d.%d", WG(conn)->ipaddr[0], WG(conn)->ipaddr[1], WG(conn)->ipaddr[2], WG(conn)->ipaddr[3]);
+ ADD_STRING("REMOTE_ADDR", buf);
+ if (WG(conn)->dnsstatus == DNS_OK) ADD_FIELD("REMOTE_HOST", host);
+
+ if ((WG(conn)->method == METHOD_POST) || (WG(conn)->method == METHOD_PUT)) {
+ ADD_NUM("CONTENT_LENGTH", bodysize);
+ ADD_FIELD("CONTENT_TYPE", type);
+ }
+
+ if ((WG(conn)->method == METHOD_PUT) || (WG(conn)->method == METHOD_DELETE)) ADD_FIELD("ENTITY_PATH", requesturi);
+
+ if (WG(conn)->pwd) {
+ ADD_STRING("AUTH_TYPE", "basic");
+ ADD_FIELD("REMOTE_USER", authorization);
+ }
+
+ ADD_FIELD("HTTP_COOKIE", cookie);
+ ADD_FIELD("HTTP_USER_AGENT", useragent);
+ ADD_FIELD("HTTP_REFERER", referer);
+ ADD_FIELD("HTTP_ACCEPT", accept);
+ ADD_FIELD("HTTP_ACCEPT_LANGUAGE", acceptlanguage);
+ ADD_FIELD("HTTP_ACCEPT_CHARSET", acceptcharset);
+ ADD_FIELD("HTTP_ACCEPT_ENCODING", acceptencoding);
+}
+
+static void webjames_module_main(TSRMLS_D)
+{
+ zend_file_handle file_handle;
+ FILE *fp=NULL;
+ char *path;
+
+ /* Convert filename to Unix format*/
+ __riscosify_control|=__RISCOSIFY_STRICT_UNIX_SPECS;
+ path = __unixify(WG(conn)->filename,0,NULL,1024,0);
+ if (path) SG(request_info).path_translated = estrdup(path);
+
+ SG(request_info).query_string = WG(conn)->args;
+ SG(request_info).request_uri = WG(conn)->requesturi;
+ SG(request_info).request_method = WG(conn)->methodstr;
+ if (WG(conn)->method==METHOD_HEAD) {
+ SG(request_info).headers_only = 1;
+ } else {
+ SG(request_info).headers_only = 0;
+ }
+ SG(sapi_headers).http_response_code = 200;
+ SG(request_info).content_type = WG(conn)->type;
+ SG(request_info).content_length = WG(conn)->bodysize;
+
+ SG(request_info).auth_user = NULL;
+ SG(request_info).auth_password = NULL;
+ if (WG(conn)->authorization) {
+ char *colon=strchr(WG(conn)->authorization,':');
+ if (colon) {
+ SG(request_info).auth_user = emalloc(colon-WG(conn)->authorization+1);
+ if (SG(request_info).auth_user) {
+ memcpy(SG(request_info).auth_user,WG(conn)->authorization,colon-WG(conn)->authorization);
+ SG(request_info).auth_user[colon-WG(conn)->authorization]='\0';
+ SG(request_info).auth_password = estrdup(colon+1);
+ }
+ }
+ }
+
+ /*ensure that syslog calls get logged separately from WebJames' main log */
+ openlog("PHP",0,0);
+
+ file_handle.type = ZEND_HANDLE_FILENAME;
+ file_handle.filename = SG(request_info).path_translated;
+ file_handle.free_filename = 0;
+ file_handle.opened_path = NULL;
+
+ if (php_request_startup(TSRMLS_C) == FAILURE) {
+ return;
+ }
+
+ php_execute_script(&file_handle TSRMLS_CC);
+ php_request_shutdown(NULL);
+}
+
+static void webjames_php_close(struct connection *conn, int force)
+/*called by webjames if it wants to close the connection*/
+{
+ TSRMLS_FETCH();
+
+ php_request_shutdown(NULL);
+ WG(oldclose)(conn,force);
+}
+
+void webjames_php_request(struct connection *conn)
+/*called by WebJames to start handler*/
+{
+ TSRMLS_FETCH();
+
+ WG(conn) = conn;
+ WG(bodyread) = 0;
+ WG(oldclose) = conn->close;
+ conn->close=webjames_php_close;
+
+ webjames_module_main(TSRMLS_C);
+
+ WG(oldclose)(WG(conn), 0);
+}
+
+static void php_info_webjames(ZEND_MODULE_INFO_FUNC_ARGS)
+{
+ php_info_print_table_start();
+ php_info_print_table_row(2, "SAPI module version", WEBJAMES_SAPI_VERSION);
+ php_info_print_table_row(2, "WebJames version", WEBJAMES_VERSION " (" WEBJAMES_DATE ")");
+ php_info_print_table_end();
+}
+
+static zend_module_entry php_webjames_module = {
+#if ZEND_MODULE_API_NO >= 20010901
+ STANDARD_MODULE_HEADER,
+#endif
+ "WebJames",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ php_info_webjames,
+#if ZEND_MODULE_API_NO >= 20010901
+ WEBJAMES_SAPI_VERSION,
+#endif
+ STANDARD_MODULE_PROPERTIES
+};
+
+
+static int php_webjames_startup(sapi_module_struct *sapi_module)
+{
+ if(php_module_startup(sapi_module, &php_webjames_module, 1) == FAILURE) {
+ return FAILURE;
+ } else {
+ return SUCCESS;
+ }
+}
+
+static sapi_module_struct sapi_module = {
+ "webjames", /* name */
+ "WebJames", /* pretty name */
+
+ php_webjames_startup, /* startup */
+ php_module_shutdown_wrapper, /* shutdown */
+
+ NULL, /* activate */
+ NULL, /* deactivate */
+
+ sapi_webjames_ub_write, /* unbuffered write */
+ NULL, /* flush */
+ NULL, /* get uid */
+ NULL, /* getenv */
+
+ php_error, /* error handler */
+
+ NULL, /* header handler */
+ NULL, /* send headers handler */
+ sapi_webjames_send_header, /* send header handler */
+ sapi_webjames_read_post, /* read POST data */
+ sapi_webjames_read_cookies, /* read Cookies */
+
+ sapi_webjames_register_variables, /* register server variables */
+ NULL, /* Log message */
+ NULL, /* Get request time */
+ NULL, /* Child terminate */
+
+ STANDARD_SAPI_MODULE_PROPERTIES
+};
+
+int webjames_php_init(void)
+/*called when WebJames initialises*/
+{
+ TSRMLS_FETCH();
+ if (strcmp(configuration.webjames_h_revision,WEBJAMES_H_REVISION)!=0) {
+ /*This file was compiled against a different revision of
+ webjames.h than webjames was, which could be bad news*/
+ webjames_writelog(0,"PHP module is compiled for WebJames (%s) and was linked with a different version (%s)",WEBJAMES_H_REVISION,configuration.webjames_h_revision);
+ return 0; /*failed to initialise*/
+ }
+ sapi_startup(&sapi_module);
+ sapi_module.startup(&sapi_module);
+ SG(server_context) = (void *) 1;
+ return 1; /*initialised correctly*/
+}
+
+void webjames_php_shutdown(void)
+/*called when WebJames is about to quit*/
+{
+ sapi_module.shutdown(&sapi_module);
+ sapi_shutdown();
+}