Browse Source

First version of Pike/Roxen module for embedding PHP.

experimetnal/RETURN_REF_PATCH
David Hedbor 27 years ago
parent
commit
8fb4122333
  1. 314
      sapi/roxen/Makefile
  2. 9
      sapi/roxen/README
  3. 26
      sapi/roxen/config.m4
  4. 356
      sapi/roxen/phpmod.pike
  5. 638
      sapi/roxen/roxen.c

314
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:

9
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 <neotron@php.net>

26
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:

356
sapi/roxen/phpmod.pike

@ -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,
]);
}

638
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 <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
Loading…
Cancel
Save