From 8fb41223333bb31f7a3643eeb7ea99096af00c33 Mon Sep 17 00:00:00 2001 From: David Hedbor Date: Tue, 23 Nov 1999 23:08:10 +0000 Subject: [PATCH] First version of Pike/Roxen module for embedding PHP. --- sapi/roxen/Makefile | 314 ++++++++++++++++++++ sapi/roxen/README | 9 + sapi/roxen/config.m4 | 26 ++ sapi/roxen/phpmod.pike | 356 +++++++++++++++++++++++ sapi/roxen/roxen.c | 638 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1343 insertions(+) create mode 100644 sapi/roxen/Makefile create mode 100644 sapi/roxen/README create mode 100644 sapi/roxen/config.m4 create mode 100644 sapi/roxen/phpmod.pike create mode 100644 sapi/roxen/roxen.c diff --git a/sapi/roxen/Makefile b/sapi/roxen/Makefile new file mode 100644 index 0000000000..792a03af2c --- /dev/null +++ b/sapi/roxen/Makefile @@ -0,0 +1,314 @@ +# Generated automatically from Makefile.in by configure. +# Makefile.in generated automatically by automake 1.4a from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +SHELL = /bin/sh + +srcdir = . +top_srcdir = ../.. +prefix = /usr/local +exec_prefix = ${prefix} + +bindir = ${exec_prefix}/bin +sbindir = ${exec_prefix}/sbin +libexecdir = ${exec_prefix}/libexec +datadir = ${prefix}/share +sysconfdir = ${prefix}/etc +sharedstatedir = ${prefix}/com +localstatedir = ${prefix}/var +libdir = ${exec_prefix}/lib +infodir = ${prefix}/info +mandir = ${prefix}/man +includedir = ${prefix}/include +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/php +pkglibdir = $(libdir)/php +pkgincludedir = $(includedir)/php + +top_builddir = ../.. + +ACLOCAL = aclocal +AUTOCONF = autoconf +AUTOMAKE = automake +AUTOHEADER = autoheader + +INSTALL = /usr/bin/install -c +INSTALL_PROGRAM = ${INSTALL} +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_FLAG = +transform = s,x,x, + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = i686-pc-linux-gnu +host_triplet = i686-pc-linux-gnu +AMTAR = gtar +AMTARFLAGS = o +AS = @AS@ +CC = gcc +CFLAGS = -g -O2 -g -O2 +DEBUG_CFLAGS = -g +DLLTOOL = @DLLTOOL@ +EXTRA_LDFLAGS = -avoid-version +EXTRA_LIBS = -ldl -lgd -lm -lresolv -lnsl -lcrypt -lgdbm -lttf -L/usr/lib/mysql -lmysqlclient -lpam +EXT_LIBS = db/libphpext_db.a gd/libphpext_gd.a mysql/libphpext_mysql.a pcre/libphpext_pcre.a session/libphpext_session.a standard/libphpext_standard.a +EXT_LTLIBS = ext/db/libphpext_db.la ext/gd/libphpext_gd.la ext/mysql/libphpext_mysql.la ext/pcre/libphpext_pcre.la ext/session/libphpext_session.la ext/standard/libphpext_standard.la +EXT_SHARED = +EXT_STATIC = db gd mysql pcre session standard +EXT_SUBDIRS = db gd mysql pcre session standard +HSREGEX = +INCLUDES = -I$(top_builddir)/libzend -I$(top_srcdir) -I$(top_srcdir)/libzend -I/tmp/dmalloc-pike//pike/0.7.79/include/pike/ -I/usr/include/mysql +INSTALL_IT = $(SHELL) $(srcdir)/install-sh -m 0755 libs/libphp4.so /tmp/dmalloc-pike//pike/0.7.79/lib/modules/PHP4.so +LD = /usr/bin/ld +LEX = flex +LEX_CFLAGS = -DYY_USE_CONST +LIBTOOL = $(SHELL) $(top_builddir)/libtool --silent +LN_S = ln -s +MAINT = # +MAKEINFO = makeinfo +NATIVE_RPATHS = -Wl,-rpath,/usr/lib/mysql +NM = /usr/bin/nm -B +OBJDUMP = @OBJDUMP@ +PACKAGE = php +PERL_PATH = /usr/bin/perl +PHP_BUILD_DATE = 1999-11-23 +PHP_DEBUG = 1 +PHP_LIBS = +PHP_PROGRAM = +PHP_RPATHS = -R /usr/lib/mysql +PHP_SAPI = roxen +PHP_VERSION = 4.0b4-dev +PROG_SENDMAIL = /usr/sbin/sendmail +RANLIB = ranlib +REGEX_DIR = regex +REGEX_LIB = regex/libregex.la +TSRM_DIR = +TSRM_LIB = +VERSION = 4.0b4-dev +WARNING_LEVEL = +YACC = bison -y +abs_builddir = /home/neotron/src/php4 +abs_srcdir = /home/neotron/src/php4 +phplibdir = /home/neotron/src/php4/modules +phptempdir = /home/neotron/src/php4/libs + + +noinst_LTLIBRARIES = libphpsapi_roxen.la +libphpsapi_roxen_la_SOURCES = roxen.c +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../../php_config.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) + + +DEFS = -DHAVE_CONFIG_H -I. -I$(srcdir) -I../.. +CPPFLAGS = +LDFLAGS = +LIBS = +libphpsapi_roxen_la_LDFLAGS = +libphpsapi_roxen_la_LIBADD = +am_libphpsapi_roxen_la_OBJECTS = roxen.lo +libphpsapi_roxen_la_OBJECTS = $(am_libphpsapi_roxen_la_OBJECTS) +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +DIST_SOURCES = $(libphpsapi_roxen_la_SOURCES) +DIST_COMMON = README Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +GZIP_ENV = --best +SOURCES = $(libphpsapi_roxen_la_SOURCES) +OBJECTS = $(am_libphpsapi_roxen_la_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .c .lo .o +$(srcdir)/Makefile.in: # Makefile.am $(top_srcdir)/$(altdir)configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps sapi/roxen/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-noinstLTLIBRARIES: + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + +distclean-noinstLTLIBRARIES: + +maintainer-clean-noinstLTLIBRARIES: + +.c.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +.c.lo: + $(LIBTOOL) --mode=compile $(COMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + +maintainer-clean-libtool: +roxen.lo: + +libphpsapi_roxen.la: $(libphpsapi_roxen_la_OBJECTS) $(libphpsapi_roxen_la_DEPENDENCIES) + $(LINK) $(libphpsapi_roxen_la_LDFLAGS) $(libphpsapi_roxen_la_OBJECTS) $(libphpsapi_roxen_la_LIBADD) $(LIBS) + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = sapi/roxen + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$d/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: +uninstall: uninstall-am +all-am: Makefile $(LTLIBRARIES) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_STRIP_FLAG=-s install +installdirs: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-noinstLTLIBRARIES mostlyclean-compile \ + mostlyclean-libtool mostlyclean-tags \ + mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-noinstLTLIBRARIES clean-compile clean-libtool \ + clean-tags clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-noinstLTLIBRARIES distclean-compile \ + distclean-libtool distclean-tags distclean-generic \ + clean-am + -rm -f libtool + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-noinstLTLIBRARIES \ + maintainer-clean-compile maintainer-clean-libtool \ + maintainer-clean-tags maintainer-clean-generic \ + distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-noinstLTLIBRARIES distclean-noinstLTLIBRARIES \ +clean-noinstLTLIBRARIES maintainer-clean-noinstLTLIBRARIES \ +mostlyclean-compile distclean-compile clean-compile \ +maintainer-clean-compile mostlyclean-libtool distclean-libtool \ +clean-libtool maintainer-clean-libtool tags mostlyclean-tags \ +distclean-tags clean-tags maintainer-clean-tags distdir info-am info \ +dvi-am dvi check check-am installcheck-am installcheck install-exec-am \ +install-exec install-data-am install-data install-am install \ +uninstall-am uninstall all-redirect all-am all install-strip \ +installdirs mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/sapi/roxen/README b/sapi/roxen/README new file mode 100644 index 0000000000..2c46878bbc --- /dev/null +++ b/sapi/roxen/README @@ -0,0 +1,9 @@ +Roxen PHP support. Very early version. Don't expect to be able to get +it to work. Requires a very late Pike 0.7 and Roxen 1.4. Anything else will be +futile, and even that might be. Beware! It is not thread safe yet. As +a matter of fact it doesn't work when threads are enabled when +compiling PHP + +- The Author, David Hedbor + + diff --git a/sapi/roxen/config.m4 b/sapi/roxen/config.m4 new file mode 100644 index 0000000000..e49fd6a32c --- /dev/null +++ b/sapi/roxen/config.m4 @@ -0,0 +1,26 @@ +dnl ## $Id$ -*- sh -*- + +RESULT=no +AC_MSG_CHECKING(for Roxen/Pike support) +AC_ARG_WITH(roxen, +[ --with-roxen=DIR], +[ + if test ! -d $withval ; then + AC_MSG_ERROR(You did not specify a directory) + fi +/* enable_thread_safety=yes*/ +/* passthru="$passthru --enable-thread-safety"*/ + ROXEN_DIR=$withval + AC_ADD_INCLUDE($ROXEN_DIR/pike/0.7.79/include/pike/) + AC_DEFINE(HAVE_ROXEN) + PHP_SAPI=roxen + PHP_BUILD_SHARED + INSTALL_IT="\$(SHELL) \$(srcdir)/install-sh -m 0755 $SAPI_SHARED $ROXEN_DIR/pike/0.7.79/lib/modules/PHP4.so" + RESULT=yes +]) +AC_MSG_RESULT($RESULT) + +dnl ## Local Variables: +dnl ## tab-width: 4 +dnl ## End: + \ No newline at end of file diff --git a/sapi/roxen/phpmod.pike b/sapi/roxen/phpmod.pike new file mode 100644 index 0000000000..45341c34d0 --- /dev/null +++ b/sapi/roxen/phpmod.pike @@ -0,0 +1,356 @@ +/* Roxen PHP module based of the Roxen PHP module for Roxen 1.4. */ + +#include +#include +inherit "module"; +inherit "roxenlib"; + +constant cvs_version = "$Id$"; +constant thread_safe = 1; + +string trim( string what ) +{ + sscanf(what, "%*[ \t]%s", what); + what = reverse(what); + sscanf(what, "%*[ \t]%s", what); + what = reverse(what); + return what; +} +//#define PHP_DEBUG +#ifdef PHP_DEBUG +#define DWERROR(X) report_debug(X) +#else /* !PHP_DEBUG */ +#define DWERROR(X) +#endif /* PHP_DEBUG */ + +array register_module() +{ + return + ({ + MODULE_FILE_EXTENSION | MODULE_PARSER, + "PHP Script Support", + "This module allows Roxen users to run PHP scripts, optionally in " + "combination with RXML. ", + }); +} + +class PHPScript +{ + object interpretor; + string command; + string buffer=""; + // stderr is handled by run(). + mapping (string:string) environment; + int blocking, written, close_when_done; + object mid; + void done() { + if(strlen(buffer)) { + close_when_done = 1; + if(QUERY(rxml)) { + buffer = parse_rxml(buffer, mid); + write_callback(); + } + } else + destruct(); + } + + void destroy() { + mid->do_not_disconnect = 0; + // destruct(interpretor); + mid->file = ([ "len": written, "raw":1 ]); + mid->do_log(); + } + void write_callback() + { + DWERROR("PHP:Wrapper::write_callback()\n"); + if(!strlen(buffer)) + return; + // int nelems = tofd->write( buffer ); + int nelems; + array err = catch { nelems = mid->my_fd->write(buffer); }; + DWERROR(sprintf("PHP:Wrapper::write_callback(): write(%O) => %d\n", + buffer, nelems)); + if( err || nelems < 0 ) + // if nelems == 0, network buffer is full. We still want to continue. + { + buffer=""; + close_when_done = -1; + } else { + written += nelems; + buffer = buffer[nelems..]; + DWERROR(sprintf("Done: %d %d...\n", strlen(buffer), close_when_done)); + if(close_when_done && !strlen(buffer)) { + destruct(); + } + } + } + + int write( string what ) + { + DWERROR(sprintf("PHP:Wrapper::write(%O)\n", what)); + if(close_when_done == -1) // Remote closed + return -1; + if(buffer == "" ) + { + buffer = what; + if(!QUERY(rxml)) write_callback(); + } else + buffer += what; + return strlen(what); + } + + void send_headers(int code, mapping headers) + { + DWERROR(sprintf("PHP:PHPWrapper::send_headers(%d,%O)\n", code, headers)); + string result = "", post=""; + string code = mid->errors[code||200]; + int ct_received = 0, sv_received = 0; + if(headers) + foreach(indices(headers), string header) + { + string value = headers[header]; + if(!header || !value) + { + // Heavy DWIM. For persons who forget about headers altogether. + continue; + } + header = trim(header); + value = trim(value); + switch(lower_case( header )) + { + case "status": + code = value; + break; + + case "content-type": + ct_received=1; + result += header+": "+value+"\r\n"; + break; + + case "server": + sv_received=1; + result += header+": "+value+"\r\n"; + break; + + case "location": + code = "302 Redirection"; + result += header+": "+value+"\r\n"; + break; + + default: + result += header+": "+value+"\r\n"; + break; + } + } + if(!sv_received) + result += "Server: "+roxen.version()+"/PHP\r\n"; + if(!ct_received) + result += "Content-Type: text/html\r\n"; + write("HTTP/1.0 "+code+"\r\n"+result+"\r\n"); + } + + PHPScript run() + { + DWERROR("PHP:PHPScript::run()\n"); + // if( QUERY(rxml) ) + // stdout = (wrapper = RXMLWrapper( stdout, mid ))->get_fd(); + mapping options = ([ + "env":environment, + ]); + thread_create(interpretor->run, command, options, this_object(), done); + mid->my_fd->set_close_callback(done); + return this_object(); + } + + + void create( object id ) + { + DWERROR("PHP:PHPScript()\n"); + interpretor = PHP4.Interpretor(); + mid = id; + +#ifndef THREADS + if(id->misc->orig) // An operation, and we have no threads. + blocking = 1; +#else + if(id->misc->orig && this_thread() == roxen.backend_thread) + blocking = 1; + // An and we are + // currently in the backend thread. +#endif + if(!id->realfile) + { + id->realfile = id->conf->real_file( id->not_query, id ); + if(!id->realfile) + error("No real file associated with "+id->not_query+ + ", thus it's not possible to run it as a PHP script.\n"); + } + command = id->realfile; + + environment =(QUERY(env)?getenv():([])); + environment |= global_env; + environment |= build_env_vars( id->realfile, id, id->misc->path_info ); + environment |= build_roxen_env_vars(id); + if(id->misc->ssi_env) environment |= id->misc->ssi_env; + if(id->misc->is_redirected) environment["REDIRECT_STATUS"] = "1"; + if(id->rawauth && QUERY(rawauth)) + environment["HTTP_AUTHORIZATION"] = (string)id->rawauth; + else + m_delete(environment, "HTTP_AUTHORIZATION"); + if(QUERY(clearpass) && id->auth && id->realauth ) { + environment["REMOTE_USER"] = (id->realauth/":")[0]; + environment["REMOTE_PASSWORD"] = (id->realauth/":")[1]; + } else { + m_delete(environment, "REMOTE_PASSWORD"); + } + if (id->rawauth) { + environment["AUTH_TYPE"] = (id->rawauth/" ")[0]; + } + // DWERROR(sprintf("%O\n", environment)); + // ffd = id->my_fd; + } +} + +mapping(string:string) global_env = ([]); +void start(int n, object conf) +{ + DWERROR("PHP:start()\n"); + + module_dependencies(conf, ({ "pathinfo" })); + if(conf) + { + string tmp=conf->query("MyWorldLocation"); + sscanf(tmp, "%*s//%s", tmp); + sscanf(tmp, "%s:", tmp); + sscanf(tmp, "%s/", tmp); + global_env["SERVER_NAME"]=tmp; + global_env["SERVER_SOFTWARE"]=roxen.version(); + global_env["GATEWAY_INTERFACE"]="PHP/1.1"; + global_env["SERVER_PROTOCOL"]="HTTP/1.0"; + global_env["SERVER_URL"]=conf->query("MyWorldLocation"); + + array us = ({0,0}); + foreach(query("extra_env")/"\n", tmp) + if(sscanf(tmp, "%s=%s", us[0], us[1])==2) + global_env[us[0]] = us[1]; + } +} +mapping handle_file_extension(object o, string e, object id) +{ + DWERROR("PHP:handle_file_extension()\n"); + id->do_not_disconnect = 1; + PHPScript( id )->run(); + return http_pipe_in_progress(); +} +/* +** Variables et. al. +*/ +array (string) query_file_extensions() +{ + return QUERY(ext); +} + + +void create(object conf) +{ + defvar("env", 0, "Pass environment variables", TYPE_FLAG, + "If this is set, all environment variables roxen has will be " + "passed to PHP scripts, not only those defined in the PHP/1.1 standard. " + "This includes PATH. (For a quick test, try this script with " + "and without this variable set:" + "
"
+	 "#!/bin/sh\n\n"
+         "echo Content-type: text/plain\n"
+	 "echo ''\n"
+	 "env\n"
+	 "
)"); + + defvar("rxml", 0, "Parse RXML in PHP-scripts", TYPE_FLAG, + "If this is set, the output from PHP-scripts handled by this " + "module will be RXMl parsed. NOTE: No data will be returned to the " + "client until the PHP-script is fully parsed."); + + defvar("extra_env", "", "Extra environment variables", TYPE_TEXT_FIELD, + "Extra variables to be sent to the script, format:
"
+	 "NAME=value\n"
+	 "NAME=value\n"
+	 "
Please note that the standard variables will have higher " + "priority."); + + defvar("ext", + ({"php", "php3", "php4" + }), "PHP-script extensions", TYPE_STRING_LIST, + "All files ending with these extensions, will be parsed as "+ + "PHP-scripts."); + + defvar("rawauth", 0, "Raw user info", TYPE_FLAG|VAR_MORE, + "If set, the raw, unparsed, user info will be sent to the script, " + " in the HTTP_AUTHORIZATION environment variable. This is not " + "recommended, but some scripts need it. Please note that this " + "will give the scripts access to the password used."); + + defvar("clearpass", 0, "Send decoded password", TYPE_FLAG|VAR_MORE, + "If set, the variable REMOTE_PASSWORD will be set to the decoded " + "password value."); + + defvar( "cgi_tag", 1, "Provide the <cgi> tag", TYPE_FLAG, + "If set, the <cgi> tag will be available" ); +} + +int|string tag_cgi( string tag, mapping args, object id ) +{ + DWERROR("PHP:tag_cgi()\n"); + + if(!query("cgi_tag")) + return 0; + + if(args->help) + return ("<"+tag+" script=path [cache=seconds] [default-argument=value] " + "[argument=value]>:"); + + if(!args->cache) + NOCACHE(); + else + CACHE( (int)args->cache || 60 ); + + + object fid = id->clone_me(); + string file = args->script; + if(!file) + return "No 'script' argument to the PHP tag"; + fid->not_query = fix_relative( file, id ); + foreach(indices(args), string arg ) + { + if(arg == "script") + continue; + if(arg == "cache") + continue; + if(arg[..7] == "default-") + { + if(!id->variables[arg[8..]]) + fid->variables[arg[8..]] = args[arg]; + } + else + fid->variables[arg] = args[arg]; + } + fid->realfile=0; + fid->method = "GET"; + mixed e = catch + { + string data=handle_file_extension( 0, "cgi", fid )->file->read(); + if(!sscanf(data, "%*s\r\n\r\n%s", data)) + sscanf(data, "%*s\n\n%s", data); + return data; + }; + return ("Failed to run PHP script:
"+
+          (html_encode_string(describe_backtrace(e))/"\n")[0]+
+          "
"); +} + + +mapping query_tag_callers() +{ + return ([ + "cgi":tag_cgi, + ]); +} diff --git a/sapi/roxen/roxen.c b/sapi/roxen/roxen.c new file mode 100644 index 0000000000..80abe6572e --- /dev/null +++ b/sapi/roxen/roxen.c @@ -0,0 +1,638 @@ +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997, 1998, 1999 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 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/2_0.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 | + | Based on aolserver SAPI by Sascha Schumann | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" +#ifdef HAVE_ROXEN + +#include "php_ini.h" +#include "php_globals.h" +#include "SAPI.h" +#include "main.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 + +#ifdef ZTS +#error Roxen PHP module isnt thread safe at the moment. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _REENTRANT +static PIKE_MUTEX_T roxen_php_execution_lock; +#define PHP_INIT_LOCK() mt_init(&roxen_php_execution_lock) +#define PHP_LOCK() fprintf(stderr, "*** php lock.\n");THREADS_ALLOW();mt_lock(&roxen_php_execution_lock);fprintf(stderr, "*** php locked.\n");THREADS_DISALLOW() +#define PHP_UNLOCK() mt_unlock(&roxen_php_execution_lock);fprintf(stderr, "*** php unlocked.\n"); +#define PHP_DESTROY() mt_destroy(&roxen_php_execution_lock) +#else /* !_REENTRANT */ +#define PHP_INIT_LOCK() +#define PHP_LOCK() +#define PHP_UNLOCK() +#define PHP_DESTROY() +#endif /* _REENTRANT */ + +extern int fd_from_object(struct object *o); +static unsigned char roxen_php_initialized; + +#define REALTHIS ((php_roxen_request *)fp->current_storage) +#define GET_THIS() current_request = REALTHIS +#define THIS current_request +#define MY_FD ((struct object *)(THIS->my_fd)) +#define REQUEST_DATA ((struct mapping *)(THIS->request_data)) + +#define THREAD_SAFE_RUN(COMMAND, what) do {\ + struct thread_state *state;\ + fprintf(stderr,"threads: %d disabled: %d id: %d\n",num_threads, threads_disabled, th_self());\ + if((state = thread_state_for_id(th_self()))!=NULL) {\ + if(!state->swapped) {\ + fprintf(stderr, "MT lock (%s).\n", what);\ + COMMAND;\ + } else {\ + fprintf(stderr, "MT nonlock (%s).\n", what); \ + mt_lock(&interpreter_lock);\ + SWAP_IN_THREAD(state);\ + fprintf(stderr, "MT locked.\n", what); \ + COMMAND;\ + fprintf(stderr, "MT locked done.\n", what); \ + SWAP_OUT_THREAD(state);\ + mt_unlock(&interpreter_lock);\ + fprintf(stderr, "MT unlocked.\n", what); \ + }\ + }\ +} while(0) + +#ifndef MUCH_DEBUG +void no_fprintf(){} +#define fprintf no_fprintf +#endif + +/* php_roxen_context is per-server (thus only once at all) */ + +struct program *php_program; + +/* roxen_globals_struct is per-request */ + +typedef struct { + struct mapping *request_data; + struct object *my_fd; + char *filename; +} php_roxen_request; + +static php_roxen_request *current_request = NULL; +static int current_thread = -1; + +INLINE struct svalue *lookup_header(char *headername) +{ + struct svalue *headers, *value; + struct pike_string *sind; + 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 char *lookup_string_header(char *headername, char *default_value) +{ + struct svalue *head; + THREAD_SAFE_RUN(head = lookup_header(headername), "header lookup"); + if(!head || head->type != PIKE_T_STRING) { + fprintf(stderr, "Header lookup for %s: default (%s)\n", headername, + default_value); + return default_value; + } + fprintf(stderr, "Header lookup for %s: %s(%d)\n", headername, + head->u.string->str, head->u.string->len); + 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 int lookup_integer_header(char *headername, int default_value) +{ + struct svalue *head; + THREAD_SAFE_RUN(head = lookup_header(headername), "header lookup"); + if(!head || head->type != PIKE_T_INT) { + fprintf(stderr, "Header lookup for %s: default (%d)\n", headername, + default_value); + return default_value; + } + fprintf(stderr, "Header lookup for %s: %d \n", headername, + head->u.integer); + return head->u.integer; +} + +/* + * php_roxen_low_ub_write() writes data to the client connection. + */ + +static int +php_roxen_low_ub_write(const char *str, uint str_length) { + int sent_bytes = 0; + struct pike_string *to_write; + to_write = make_shared_binary_string(str, str_length); + push_string(to_write); + safe_apply(MY_FD, "write", 1); + if(sp[-1].type == PIKE_T_INT) + sent_bytes = sp[-1].u.integer; + //free_string(to_write); + pop_stack(); + if(sent_bytes != str_length) { + /* This means the connection is closed. Dead. Gone. *sniff* */ + PG(connection_status) = PHP_CONNECTION_ABORTED; + zend_bailout(); + } + fprintf(stderr, "low_write done.\n"); +} + +/* + * 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) +{ + int sent_bytes = 0; + THREAD_SAFE_RUN(sent_bytes = php_roxen_low_ub_write(str, str_length), "write"); + fprintf(stderr, "write done.\n"); + return sent_bytes; +} + +/* php_roxen_set_header() sets a header in the header mapping. + Set Header +*/ +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; + 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); + + fprintf(stderr, "Setting header %s to %s\n", hind->str, value); + 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 SLS_DC) +{ + char *header_name, *header_content; + char *p; + struct pike_string *hind; + header_name = sapi_header->header; + header_content = p = strchr(header_name, ':'); + + if(!p) return 0; + do { + header_content++; + } while(*header_content == ' '); + THREAD_SAFE_RUN(php_roxen_set_header(header_name, header_content, p), "header handler"); + efree(sapi_header->header); + return 1; +} + +/* + * 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 SLS_DC) +{ + struct pike_string *ind; + struct svalue *s_headermap; + ind = make_shared_string(" _headers"); + s_headermap = low_mapping_string_lookup(REQUEST_DATA, ind); + free_string(ind); + fprintf(stderr, "Send Headers (%d)...\n", SG(sapi_headers).http_response_code); + + 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); + fprintf(stderr, "Send Headers 1 (%d)...\n", SG(sapi_headers).http_response_code); + safe_apply(MY_FD, "send_headers", 2); + fprintf(stderr, "Send Headers 1 (%d)...\n", SG(sapi_headers).http_response_code); + pop_stack(); + fprintf(stderr, "Send Headers 2 (%d)...\n", SG(sapi_headers).http_response_code); + + /* + 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; +} + +static int +php_roxen_sapi_send_headers(sapi_headers_struct *sapi_headers SLS_DC) +{ + THREAD_SAFE_RUN(php_roxen_low_send_headers(sapi_headers SLS_CC), "send headers"); + return SAPI_HEADER_SENT_SUCCESSFULLY; +} + +/* + * 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 max_read; + uint total_read = 0; + fprintf(stderr, "read post (%d bytes max)\n", count_bytes); + + push_int(count_bytes); + safe_apply(MY_FD, "read_post", 1); + if(sp[-1].type == T_STRING) { + MEMCPY(buf, sp[-1].u.string->str, total_read = sp[-1].u.string->len); + buf[total_read] = '\0'; + } else + total_read = -1; + pop_stack(); + return total_read; +} + +static int +php_roxen_sapi_read_post(char *buf, uint count_bytes SLS_DC) +{ + uint total_read; + 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(SLS_D) +{ + int i; + char *cookies; + cookies = lookup_string_header("HTTP_COOKIE", NULL); + return cookies; +} + +static void php_info_roxen(ZEND_MODULE_INFO_FUNC_ARGS) +{ + char buf[512]; + // int uptime = Ns_InfoUptime(); + + PUTS("\n"); + 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); + */ + PUTS("
"); +} + +static zend_module_entry php_roxen_module = { + "Roxen", + NULL, + NULL, + NULL, + NULL, + NULL, + php_info_roxen, + STANDARD_MODULE_PROPERTIES +}; + +static int php_roxen_startup(sapi_module_struct *sapi_module) +{ + if(php_module_startup(sapi_module) == FAILURE + || zend_register_module(&php_roxen_module) == FAILURE) { + return FAILURE; + } else { + return SUCCESS; + } +} + +/* this structure is static (as in "it does not change") */ + +void pike_module_exit(void); + +static sapi_module_struct sapi_module = { + "PHP Language", + + php_module_startup, /* startup */ + pike_module_exit, /* shutdown */ + + php_roxen_sapi_ub_write, /* unbuffered write */ + + 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 */ + + 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(pval); \ + pval->type = IS_STRING; \ + pval->value.str.len = strlen(buf); \ + pval->value.str.val = estrndup(buf, pval->value.str.len); \ + zend_hash_update(&EG(symbol_table), name, sizeof(name), \ + &pval, sizeof(zval *), NULL) + +static void +php_roxen_hash_environment(CLS_D ELS_DC PLS_DC SLS_DC) +{ + int i; + char buf[512]; + zval *pval; + struct svalue *headers; + struct pike_string *sind; + struct array *indices; + struct svalue *ind, *val; + 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) { + char *p; + char c; + 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(pval); + pval->type = IS_STRING; + pval->value.str.len = val->u.string->len; + pval->value.str.val = estrndup(val->u.string->str, pval->value.str.len); + /* fprintf(stderr, "Header: %s(%d)=%s\n", buf, buf_len, val->u.string->str);*/ + + zend_hash_update(&EG(symbol_table), buf, buf_len + 1, &pval, sizeof(zval *), NULL); + } + } + free_array(indices); + } + + // MAKE_STD_ZVAL(pval); + // pval->type = IS_LONG; + // pval->value.lval = Ns_InfoBootTime(); + // zend_hash_update(&EG(symbol_table), "SERVER_BOOTTIME", sizeof("SERVER_BOOTTIME"), &pval, sizeof(zval *), NULL); + + // fprintf(stderr, "Set up header environment.\n"); +} + +/* + * php_roxen_module_main() is called by the per-request handler and + * "executes" the script + */ + +int php_roxen_module_main(SLS_D) +{ + zend_file_handle file_handle; + file_handle.type = ZEND_HANDLE_FILENAME; + file_handle.filename = THIS->filename; + if(php_request_startup(CLS_C ELS_CC PLS_CC SLS_CC) == FAILURE) { + return 0; + } + php_roxen_hash_environment(CLS_C ELS_CC PLS_CC SLS_CC); + THREADS_ALLOW(); + php_execute_script(&file_handle CLS_CC ELS_CC PLS_CC); + php_request_shutdown(NULL); + THREADS_DISALLOW(); + return 1; +} + +/* + * php_roxen_request_ctor() initializes the per-request data structure + * and fills it with data provided by the web server + */ + +static void +php_roxen_request_ctor(SLS_D) +{ + char *server; + char *root; + int index; + char *tmp; + + SG(request_info).auth_user = NULL; + SG(request_info).auth_password = NULL; + /* + 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_roxen_request_dtor() destroys all data associated with + * the per-request structure + */ + +static void +php_roxen_request_dtor(SLS_D) +{ + // free(SG(request_info).path_translated); +} + +/* + * 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; + struct mapping *request_data; + struct svalue *done_callback; + struct pike_string *script; + int opened_fd = 1, status = 1; + int f = 0; + + if(current_thread == th_self()) + error("PHP4.Interpetor->run: Tried to run a PHP-script from a PHP " + "callback!"); + SLS_FETCH(); + get_all_args("PHP4.Interpretor->run", args, "%S%m%O%*", &script, + &request_data, &my_fd, &done_callback); + REALTHIS->request_data = request_data; + REALTHIS->my_fd = my_fd; + REALTHIS->filename = script->str; + if(done_callback->type != PIKE_T_FUNCTION) + error("PHP4.Interpretor->run: Bad argument 4, expected function.\n"); + PHP_LOCK(); + current_thread = th_self(); + GET_THIS(); + SG(request_info).query_string = lookup_string_header("QUERY_STRING", 0);; + SG(server_context) = (void *)1; // avoid server_context == NULL + /* path_translated is the absolute path to the file */ + SG(request_info).path_translated = + lookup_string_header("PATH_TRANSLATED", 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("CONTENT_LENGTH", 0); + SG(request_info).content_type = "text/html"; + SG(request_info).auth_user = NULL; + SG(request_info).auth_password = NULL; + status = php_roxen_module_main(SLS_C); + + php_roxen_request_dtor(SLS_C); + current_thread = -1; + PHP_UNLOCK(); + + apply_svalue(done_callback, 0); + pop_stack(); + pop_n_elems(args); + push_int(status); + +} + +void clear_struct(struct object *o) +{ + MEMSET(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() +{ + if (!roxen_php_initialized) { + sapi_startup(&sapi_module); + sapi_module.startup(&sapi_module); + 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("Interpretor", (php_program = end_program()), 0); + + /* TSRM is used to allocate a per-thread structure */ + /* read the configuration */ + //php_roxen_config(ctx); + +} + +/* + * 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; + sapi_module.shutdown(&sapi_module); + if(php_program) free_program(php_program); + PHP_DESTROY(); +} +#endif -- 2.40.0