]> granicus.if.org Git - php/commitdiff
First version of Pike/Roxen module for embedding PHP.
authorDavid Hedbor <neotron@php.net>
Tue, 23 Nov 1999 23:08:10 +0000 (23:08 +0000)
committerDavid Hedbor <neotron@php.net>
Tue, 23 Nov 1999 23:08:10 +0000 (23:08 +0000)
sapi/roxen/Makefile [new file with mode: 0644]
sapi/roxen/README [new file with mode: 0644]
sapi/roxen/config.m4 [new file with mode: 0644]
sapi/roxen/phpmod.pike [new file with mode: 0644]
sapi/roxen/roxen.c [new file with mode: 0644]

diff --git a/sapi/roxen/Makefile b/sapi/roxen/Makefile
new file mode 100644 (file)
index 0000000..792a03a
--- /dev/null
@@ -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 (file)
index 0000000..2c46878
--- /dev/null
@@ -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 <neotron@php.net>
+
+
diff --git a/sapi/roxen/config.m4 b/sapi/roxen/config.m4
new file mode 100644 (file)
index 0000000..e49fd6a
--- /dev/null
@@ -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 (file)
index 0000000..45341c3
--- /dev/null
@@ -0,0 +1,356 @@
+/* Roxen PHP module based of the Roxen PHP module for Roxen 1.4. */
+
+#include <roxen.h>
+#include <module.h>
+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 <insert file=...> operation, and we have no threads.
+      blocking = 1;
+#else
+    if(id->misc->orig && this_thread() == roxen.backend_thread)
+      blocking = 1; 
+    // An <insert file=...> 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:"
+        "<pre>"
+        "#!/bin/sh\n\n"
+         "echo Content-type: text/plain\n"
+        "echo ''\n"
+        "env\n"
+        "</pre>)");
+
+  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:<pre>"
+        "NAME=value\n"
+        "NAME=value\n"
+        "</pre>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 &lt;cgi&gt; tag", TYPE_FLAG,
+           "If set, the &lt;cgi&gt; 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 ("<b>&lt;"+tag+" script=path [cache=seconds] [default-argument=value] "
+            "[argument=value]&gt;:</b>");
+
+  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: <font color=red><pre>"+
+          (html_encode_string(describe_backtrace(e))/"\n")[0]+
+          "</pre></font>");
+}
+
+
+mapping query_tag_callers()
+{
+  return ([
+    "cgi":tag_cgi,
+  ]);
+}
diff --git a/sapi/roxen/roxen.c b/sapi/roxen/roxen.c
new file mode 100644 (file)
index 0000000..80abe65
--- /dev/null
@@ -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 <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 "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 <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>
+
+#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("<table border=5 width=600>\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("</table>");
+}
+
+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