From: Sascha Schumann Date: Thu, 26 Oct 2000 17:55:05 +0000 (+0000) Subject: An Apache 2.0 Filter for PHP, completely from scratch. X-Git-Tag: php-4.0.4RC3~529 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d504de0f789a390545fd219b8eebc951012cfa61;p=php An Apache 2.0 Filter for PHP, completely from scratch. TODO: POST, cookies, "flushing", finalizing config framework (it works), http auth, PHP Hooks (apache_sub_req is there). Note that this code depends on some other commits which are pending. --- diff --git a/sapi/apache2filter/Makefile.in b/sapi/apache2filter/Makefile.in new file mode 100644 index 0000000000..d3b4abbd5e --- /dev/null +++ b/sapi/apache2filter/Makefile.in @@ -0,0 +1,7 @@ + +LTLIBRARY_NAME = libsapi.la +LTLIBRARY_SOURCES = sapi_apache2.c apache_config.c + +EXTRA_INCLUDES = $(APACHE_INCLUDE) + +include $(top_srcdir)/build/ltlib.mk diff --git a/sapi/apache2filter/apache_config.c b/sapi/apache2filter/apache_config.c new file mode 100644 index 0000000000..fa704740d3 --- /dev/null +++ b/sapi/apache2filter/apache_config.c @@ -0,0 +1,134 @@ +#include "php.h" +#include "php_ini.h" + +#include "apr_strings.h" +#include "ap_config.h" +#include "util_filter.h" +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" +#include "http_core.h" + +typedef struct { + HashTable config; +} php_conf_rec; + +typedef struct { + char *value; + size_t value_len; + char status; +} php_dir_entry; + +static const char *real_value_hnd(cmd_parms *cmd, void *dummy, const char *name, const char *value, int status) +{ + php_conf_rec *d = dummy; + php_dir_entry e; + php_dir_entry *pe; + size_t str_len; + + fprintf(stderr, "Getting %s=%s for %p (%d)\n", name, value, dummy, zend_hash_num_elements(&d->config)); + e.value = apr_pstrdup(cmd->pool, value); + e.value_len = strlen(value); + e.status = status; + + str_len = strlen(name); + + if (zend_hash_find(&d->config, name, str_len + 1, &pe) == SUCCESS) { + if (pe->status > status) + return NULL; + } + + zend_hash_update(&d->config, name, strlen(name) + 1, &e, sizeof(e), + NULL); + return NULL; +} + +static const char *php_apache_value_handler(cmd_parms *cmd, void *dummy, const char *name, const char *value) +{ + return real_value_hnd(cmd, dummy, name, value, PHP_INI_USER); +} + +static const char *php_apache_admin_value_handler(cmd_parms *cmd, void *dummy, const char *name, const char *value) +{ + return real_value_hnd(cmd, dummy, name, value, PHP_INI_SYSTEM); +} + +void *merge_php_config(apr_pool_t *p, void *base_conf, void *new_conf) +{ + php_conf_rec *d = base_conf, *e = new_conf; + php_dir_entry *pe; + php_dir_entry *data; + char *str; + ulong str_len; + ulong num_index; + char buf[256]; + + fprintf(stderr, "Merge dir (%p) (%p)\n", base_conf, new_conf); + for (zend_hash_internal_pointer_reset(&d->config); + zend_hash_get_current_key_ex(&d->config, &str, &str_len, &num_index, NULL) == HASH_KEY_IS_STRING; + zend_hash_move_forward(&d->config)) { + pe = NULL; + zend_hash_get_current_data(&d->config, &data); + if (zend_hash_find(&e->config, str, str_len, &pe) == SUCCESS) { + if (pe->status >= data->status) continue; + } + zend_hash_update(&e->config, str, str_len, data, sizeof(*data), NULL); + sprintf(buf, "ADDING/OVERWRITING %%%lds (%d vs. %d)\n", str_len, data->status, pe?pe->status:-1); + fprintf(stderr, buf, str); + } + return new_conf; +} + +void apply_config(void *dummy) +{ + php_conf_rec *d = dummy; + char *str; + ulong str_len; + php_dir_entry *data; + + for (zend_hash_internal_pointer_reset(&d->config); + zend_hash_get_current_key_ex(&d->config, &str, &str_len, NULL, NULL) == HASH_KEY_IS_STRING; + zend_hash_move_forward(&d->config)) { + zend_hash_get_current_data(&d->config, &data); + fprintf(stderr, "APPLYING (%s)(%s)\n", str, data->value); + if (php_alter_ini_entry(str, str_len, data->value, data->value_len + 1, + data->status, PHP_INI_STAGE_RUNTIME) == FAILURE) + fprintf(stderr, "..FAILED\n"); + } +} + +const command_rec dir_cmds[] = +{ + AP_INIT_TAKE2("php_value", php_apache_value_handler, NULL, OR_OPTIONS, + "PHP Value Modifier"), + AP_INIT_TAKE2("php_admin_value", php_apache_admin_value_handler, NULL, OR_OPTIONS, + "PHP Value Modifier"), + {NULL} +}; + +static apr_status_t destroy_php_config(void *data) +{ + php_conf_rec *d = data; + + fprintf(stderr, "Destroying config %p\n", data); + zend_hash_destroy(&d->config); + + return APR_SUCCESS; +} + +void *create_php_config(apr_pool_t *p, char *dummy) +{ + php_conf_rec *newx = + (php_conf_rec *) apr_pcalloc(p, sizeof(*newx)); + + fprintf(stderr, "Creating new config (%p) for %s\n", newx, dummy); + zend_hash_init(&newx->config, 0, NULL, NULL, 1); + apr_register_cleanup(p, newx, destroy_php_config, NULL); + return (void *) newx; +} + diff --git a/sapi/apache2filter/config.m4 b/sapi/apache2filter/config.m4 new file mode 100644 index 0000000000..7ffce4f296 --- /dev/null +++ b/sapi/apache2filter/config.m4 @@ -0,0 +1,51 @@ +dnl ## -*- sh -*- + +AC_MSG_CHECKING(for Apache 2.0 module support via DSO through APXS) +AC_ARG_WITH(apxs2, +[ --with-apxs2[=FILE] Build shared Apache 2.0 module. FILE is the optional + pathname to the Apache apxs tool; defaults to "apxs".],[ + if test "$withval" = "yes"; then + APXS=apxs + if $APXS -q CFLAGS >/dev/null 2>&1; then + : + else + if test -x /usr/sbin/apxs ; then #SUSE 6.x + APXS=/usr/sbin/apxs + fi + fi + else + AC_EXPAND_PATH($withval, APXS) + fi + + if $APXS -q CFLAGS >/dev/null 2>&1; then + : + else + AC_MSG_RESULT() + $APXS + AC_MSG_ERROR([Sorry, I cannot run apxs. Either you need to install Perl or you need to pass the absolute path of apxs by using --with-apxs2=/absolute/path/to/apxs]) + fi + + APXS_INCLUDEDIR=`$APXS -q INCLUDEDIR` + APXS_CFLAGS=`$APXS -q CFLAGS` + AC_ADD_INCLUDE($APXS_INCLUDEDIR) + AC_ADD_INCLUDE($APXS_INCLUDEDIR/apr) + if `echo $APXS_CFLAGS|grep USE_HSREGEX>/dev/null`; then + APACHE_HAS_REGEX=yes + fi + if `echo $APXS_CFLAGS|grep EAPI>/dev/null`; then + CPPFLAGS="$CPPFLAGS -DEAPI" + fi + PHP_SAPI=apache2filter + INSTALL_IT="$APXS -i -a -n php4 $SAPI_LIBTOOL" + PHP_BUILD_SHARED + PHP_BUILD_THREAD_SAFE + AC_MSG_RESULT(yes) +],[ + AC_MSG_RESULT(no) +]) + +PHP_SUBST(APXS) + +dnl ## Local Variables: +dnl ## tab-width: 4 +dnl ## End: diff --git a/sapi/apache2filter/php.sym b/sapi/apache2filter/php.sym new file mode 100644 index 0000000000..2dca1256c2 --- /dev/null +++ b/sapi/apache2filter/php.sym @@ -0,0 +1 @@ +php4_module diff --git a/sapi/apache2filter/php_apache.h b/sapi/apache2filter/php_apache.h new file mode 100644 index 0000000000..bd5e277c78 --- /dev/null +++ b/sapi/apache2filter/php_apache.h @@ -0,0 +1,15 @@ +#ifndef PHP_APACHE_H +#define PHP_APACHE_H + +typedef struct php_struct { + int state; + ap_bucket_brigade *bb; + ap_filter_t *f; +} php_struct; + +void *merge_php_config(apr_pool_t *p, void *base_conf, void *new_conf); +void *create_php_config(apr_pool_t *p, char *dummy); +void apply_config(void *); +extern const command_rec dir_cmds[]; + +#endif /* PHP_APACHE_H */ diff --git a/sapi/apache2filter/php_functions.c b/sapi/apache2filter/php_functions.c new file mode 100644 index 0000000000..19c7b7ceb4 --- /dev/null +++ b/sapi/apache2filter/php_functions.c @@ -0,0 +1,23 @@ +#include "php_apache.h" + +PHP_FUNCTION(apache_sub_req) +{ + zval **p1; + request_req *rr; + php_struct *ctx; + SLS_FETCH(); + + if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &p1) == FAILURE) + WRONG_NUM_ARGS; + + convert_to_string_ex(p1); + + ctx = SG(server_context); + rr = ap_sub_req_lookup_uri(Z_STRVAL_PP(p1), ctx->f->r); + if (rr->status == HTTP_OK) { + ap_run_sub_req(rr); + RETURN_TRUE; + } + RETURN_FALSE; +} + diff --git a/sapi/apache2filter/sapi_apache2.c b/sapi/apache2filter/sapi_apache2.c new file mode 100644 index 0000000000..c715418b97 --- /dev/null +++ b/sapi/apache2filter/sapi_apache2.c @@ -0,0 +1,289 @@ +#include + +#include "php.h" +#include "php_main.h" +#include "php_ini.h" +#include "php_variables.h" +#include "SAPI.h" + +#include "ext/standard/php_smart_str.h" + +#include "apr_strings.h" +#include "ap_config.h" +#include "util_filter.h" +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" +#include "http_core.h" + +#include "php_apache.h" + +static int +php_apache_sapi_ub_write(const char *str, uint str_length) +{ + ap_bucket *b; + ap_bucket_brigade *bb; + php_struct *ctx; + SLS_FETCH(); + + ctx = SG(server_context); + + bb = ap_brigade_create(ctx->f->r->pool); + b = ap_bucket_create_transient(str, str_length); + AP_BRIGADE_INSERT_TAIL(bb, b); + ap_pass_brigade(ctx->f->next, bb); + + return str_length; +} + +static int +php_apache_sapi_header_handler(sapi_header_struct *sapi_header, sapi_headers_struct *sapi_headers SLS_DC) +{ + php_struct *ctx = SG(server_context); + char *val; + + val = strchr(sapi_header->header, ':'); + + if (!val) return 0; + + *val = '\0'; + + do { + val++; + } while (*val == ' '); + + if (!strcasecmp(sapi_header->header, "content-type")) + ctx->f->r->content_type = apr_pstrdup(ctx->f->r->pool, val); + else if (sapi_header->replace) + apr_table_set(ctx->f->r->headers_out, sapi_header->header, val); + else + apr_table_add(ctx->f->r->headers_out, sapi_header->header, val); + + sapi_free_header(sapi_header); + + return 0; +} + +static int +php_apache_sapi_send_headers(sapi_headers_struct *sapi_headers SLS_DC) +{ + php_struct *ctx = SG(server_context); + + ctx->f->r->status = SG(sapi_headers).http_response_code; + + return SAPI_HEADER_SENT_SUCCESSFULLY; +} + +static int +php_apache_sapi_read_post(char *buf, uint count_bytes SLS_DC) +{ + + return 0; +} + +static char * +php_apache_sapi_read_cookies(SLS_D) +{ + + return 0; +} + +static void +php_apache_sapi_register_variables(zval *track_vars_array ELS_DC SLS_DC PLS_DC) +{ + php_struct *ctx = SG(server_context); + apr_array_header_t *arr = apr_table_elts(ctx->f->r->subprocess_env); + apr_table_entry_t *elts = (apr_table_entry_t *) arr->elts; + int i; + char *val; + + for (i = 0; i < arr->nelts; i++) { + if (!(val = elts[i].val)) + val = empty_string; + + php_register_variable(elts[i].key, val, track_vars_array ELS_CC PLS_CC); + } + + php_register_variable("PHP_SELF", ctx->f->r->uri, track_vars_array ELS_CC PLS_CC); +} + +static void +php_apache_sapi_flush(void *server_context) +{ + php_struct *ctx = server_context; + return; + + /* This does not work yet. Apparently, the default handler + interpretes bucket flush as EOS */ + + ap_rflush(ctx->f->r); +} + +static sapi_module_struct sapi_module = { + "apache2filter", + "Apache 2.0 Filter", + + php_module_startup, /* startup */ + php_module_shutdown_wrapper, /* shutdown */ + + NULL, /* activate */ + NULL, /* deactivate */ + + php_apache_sapi_ub_write, /* unbuffered write */ + php_apache_sapi_flush, /* flush */ + NULL, /* get uid */ + NULL, /* getenv */ + + php_error, /* error handler */ + + php_apache_sapi_header_handler, /* header handler */ + php_apache_sapi_send_headers, /* send headers handler */ + NULL, /* send header handler */ + + php_apache_sapi_read_post, /* read POST data */ + php_apache_sapi_read_cookies, /* read Cookies */ + + php_apache_sapi_register_variables, + NULL, /* Log message */ + + NULL, /* Block interruptions */ + NULL, /* Unblock interruptions */ + + STANDARD_SAPI_MODULE_PROPERTIES +}; + + +module MODULE_VAR_EXPORT php4_module; + +static int php_filter(ap_filter_t *f, ap_bucket_brigade *bb) +{ + php_struct *ctx = f->ctx; + ap_bucket *b; + apr_status_t rv; + const char *str; + apr_ssize_t n; + void *conf = ap_get_module_config(f->r->per_dir_config, + &php4_module); + SLS_FETCH(); + + if (ctx == NULL) { + SG(server_context) = f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); + ctx->bb = ap_brigade_create(f->c->pool); + } + + ctx->f = f; + + if (ctx->state == 0) { + char *content_type; + CLS_FETCH(); + ELS_FETCH(); + PLS_FETCH(); + SLS_FETCH(); + + apply_config(conf); + php_request_startup(CLS_C ELS_CC PLS_CC SLS_CC); + + ctx->state++; + + PG(during_request_startup) = 0; + SG(sapi_headers).http_response_code = 200; + content_type = sapi_get_default_content_type(SLS_C); + f->r->content_type = apr_pstrdup(f->r->pool, + content_type); + efree(content_type); + apr_table_set(f->r->headers_in, "Connection", "close"); + apr_table_unset(f->r->headers_out, "Content-Length"); + } + + /* moves data from bb to ctx->bb */ + ap_save_brigade(f, &ctx->bb, &bb); + + if (ctx->state == 1 && AP_BUCKET_IS_EOS(AP_BRIGADE_LAST(ctx->bb))) { + int fd; + zend_file_handle zfd; + smart_str content = {0}; + ap_bucket *eos; + CLS_FETCH(); + ELS_FETCH(); + PLS_FETCH(); + + ctx->state = 2; + + AP_BRIGADE_FOREACH(b, ctx->bb) { + rv = ap_bucket_read(b, &str, &n, 1); + if (rv == APR_SUCCESS && n > 0) + smart_str_appendl(&content, str, n); + } + if (!content.c) goto fucked; + smart_str_0(&content); + +#if 1 +#define FFFF "/tmp/really_silly" + fd = open(FFFF, O_WRONLY|O_TRUNC|O_CREAT, 0600); + + write(fd, content.c, content.len); + + close(fd); + + zfd.type = ZEND_HANDLE_FILENAME; + zfd.filename = FFFF; + zfd.free_filename = 0; + zfd.opened_path = NULL; + + php_execute_script(&zfd CLS_CC ELS_CC PLS_CC); +#else + CG(in_compilation) = 1; + zend_eval_string(content, NULL, "foo" CLS_CC ELS_CC); +#endif + + smart_str_free(&content); +fucked: + php_request_shutdown(NULL); + + eos = ap_bucket_create_eos(); + AP_BRIGADE_INSERT_TAIL(bb, eos); + ap_pass_brigade(f->next, bb); + } + + return APR_SUCCESS; +} + +static apr_status_t +php_apache_server_shutdown(void *tmp) +{ + sapi_module.shutdown(&sapi_module); + sapi_shutdown(); + tsrm_shutdown(); + return APR_SUCCESS; +} + +static void +php_apache_server_startup(apr_pool_t *pchild, server_rec *s) +{ + tsrm_startup(1, 1, 0); + sapi_startup(&sapi_module); + sapi_module.startup(&sapi_module); + apr_register_cleanup(pchild, NULL, php_apache_server_shutdown, NULL); +} + +static void php_register_hook(void) +{ + ap_hook_child_init(php_apache_server_startup, NULL, NULL, AP_HOOK_MIDDLE); + ap_register_output_filter("PHP", php_filter, AP_FTYPE_CONTENT); +} + +module MODULE_VAR_EXPORT php4_module = { + STANDARD20_MODULE_STUFF, + create_php_config, /* create per-directory config structure */ + merge_php_config, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + dir_cmds, /* command apr_table_t */ + NULL, /* handlers */ + php_register_hook /* register hooks */ +};