Download | Plain Text | Line Numbers
diff -Naur htscanner-cvs/ap_fnmatch.c htscanner/ap_fnmatch.c
--- htscanner-cvs/ap_fnmatch.c 1970-01-01 01:00:00.000000000 +0100
+++ htscanner/ap_fnmatch.c 2007-07-09 02:11:27.000000000 +0200
@@ -0,0 +1,263 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+ * Compares a filename or pathname to a pattern.
+ */
+
+#include "ap_fnmatch.h"
+#include <string.h>
+#include <stdio.h>
+
+#define ap_tolower(c) (tolower(((unsigned char)(c))))
+#define EOS '\0'
+
+static const char *rangematch(const char *, int, int);
+
+int ap_fnmatch(const char *pattern, const char *string, int flags)
+{
+ const char *stringstart;
+ char c, test;
+
+ for (stringstart = string;;) {
+ switch (c = *pattern++) {
+ case EOS:
+ return (*string == EOS ? 0 : FNM_NOMATCH);
+ case '?':
+ if (*string == EOS) {
+ return (FNM_NOMATCH);
+ }
+ if (*string == '/' && (flags & FNM_PATHNAME)) {
+ return (FNM_NOMATCH);
+ }
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) {
+ return (FNM_NOMATCH);
+ }
+ ++string;
+ break;
+ case '*':
+ c = *pattern;
+ /* Collapse multiple stars. */
+ while (c == '*') {
+ c = *++pattern;
+ }
+
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) {
+ return (FNM_NOMATCH);
+ }
+
+ /* Optimize for pattern with * at end or before /. */
+ if (c == EOS) {
+ if (flags & FNM_PATHNAME) {
+ return (strchr(string, '/') == NULL ? 0 : FNM_NOMATCH);
+ }
+ else {
+ return (0);
+ }
+ }
+ else if (c == '/' && flags & FNM_PATHNAME) {
+ if ((string = strchr(string, '/')) == NULL) {
+ return (FNM_NOMATCH);
+ }
+ break;
+ }
+
+ /* General case, use recursion. */
+ while ((test = *string) != EOS) {
+ if (!ap_fnmatch(pattern, string, flags & ~FNM_PERIOD)) {
+ return (0);
+ }
+ if (test == '/' && flags & FNM_PATHNAME) {
+ break;
+ }
+ ++string;
+ }
+ return (FNM_NOMATCH);
+ case '[':
+ if (*string == EOS) {
+ return (FNM_NOMATCH);
+ }
+ if (*string == '/' && flags & FNM_PATHNAME) {
+ return (FNM_NOMATCH);
+ }
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) {
+ return (FNM_NOMATCH);
+ }
+ if ((pattern = rangematch(pattern, *string, flags)) == NULL) {
+ return (FNM_NOMATCH);
+ }
+ ++string;
+ break;
+ case '\\':
+ if (!(flags & FNM_NOESCAPE)) {
+ if ((c = *pattern++) == EOS) {
+ c = '\\';
+ --pattern;
+ }
+ }
+ /* FALLTHROUGH */
+ default:
+ if (flags & FNM_CASE_BLIND) {
+ if (ap_tolower(c) != ap_tolower(*string)) {
+ return (FNM_NOMATCH);
+ }
+ }
+ else if (c != *string) {
+ return (FNM_NOMATCH);
+ }
+ string++;
+ break;
+ }
+ /* NOTREACHED */
+ }
+}
+
+static const char *rangematch(const char *pattern, int test, int flags)
+{
+ int negate, ok;
+ char c, c2;
+
+ /*
+ * A bracket expression starting with an unquoted circumflex
+ * character produces unspecified results (IEEE 1003.2-1992,
+ * 3.13.2). This implementation treats it like '!', for
+ * consistency with the regular expression syntax.
+ * J.T. Conklin (conklin@ngai.kaleida.com)
+ */
+ if ((negate = (*pattern == '!' || *pattern == '^'))) {
+ ++pattern;
+ }
+
+ for (ok = 0; (c = *pattern++) != ']';) {
+ if (c == '\\' && !(flags & FNM_NOESCAPE)) {
+ c = *pattern++;
+ }
+ if (c == EOS) {
+ return (NULL);
+ }
+ if (*pattern == '-' && (c2 = *(pattern + 1)) != EOS && c2 != ']') {
+ pattern += 2;
+ if (c2 == '\\' && !(flags & FNM_NOESCAPE)) {
+ c2 = *pattern++;
+ }
+ if (c2 == EOS) {
+ return (NULL);
+ }
+ if ((c <= test && test <= c2)
+ || ((flags & FNM_CASE_BLIND)
+ && ((ap_tolower(c) <= ap_tolower(test))
+ && (ap_tolower(test) <= ap_tolower(c2))))) {
+ ok = 1;
+ }
+ }
+ else if ((c == test)
+ || ((flags & FNM_CASE_BLIND)
+ && (ap_tolower(c) == ap_tolower(test)))) {
+ ok = 1;
+ }
+ }
+ return (ok == negate ? NULL : pattern);
+}
+
+
+/* This function is an Apache addition */
+/* return non-zero if pattern has any glob chars in it */
+int ap_is_fnmatch(const char *pattern)
+{
+ int nesting;
+
+ nesting = 0;
+ while (*pattern) {
+ switch (*pattern) {
+ case '?':
+ case '*':
+ return 1;
+
+ case '\\':
+ if (*pattern++ == '\0') {
+ return 0;
+ }
+ break;
+
+ case '[': /* '[' is only a glob if it has a matching ']' */
+ ++nesting;
+ break;
+
+ case ']':
+ if (nesting) {
+ return 1;
+ }
+ break;
+ }
+ ++pattern;
+ }
+ return 0;
+}
+/*
+ * Local variables:
+ * tab-width: 8
+ * c-basic-offset: 8
+ * End:
+ * vim600: noet sw=8 ts=8 fdm=marker
+ * vim<600: noet sw=8 ts=8
+ */
diff -Naur htscanner-cvs/ap_fnmatch.h htscanner/ap_fnmatch.h
--- htscanner-cvs/ap_fnmatch.h 1970-01-01 01:00:00.000000000 +0100
+++ htscanner/ap_fnmatch.h 2007-07-09 02:11:48.000000000 +0200
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fnmatch.h 8.1 (Berkeley) 6/2/93
+ */
+
+/* This file has been modified by the Apache Group. */
+
+#ifndef _FNMATCH_H_
+#define _FNMATCH_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FNM_NOMATCH 1 /* Match failed. */
+
+#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
+#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
+#define FNM_PERIOD 0x04 /* Period must be matched by period. */
+/* This flag is an Apache addition */
+#define FNM_CASE_BLIND 0x08 /* Compare characters case-insensitively. */
+
+int ap_fnmatch(const char *, const char *, int);
+
+/* this function is an Apache addition */
+extern int ap_is_fnmatch(const char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_FNMATCH_H_ */
+/*
+ * Local variables:
+ * tab-width: 8
+ * c-basic-offset: 8
+ * End:
+ * vim600: noet sw=8 ts=8 fdm=marker
+ * vim<600: noet sw=8 ts=8
+ */
diff -Naur htscanner-cvs/ap_util.c htscanner/ap_util.c
--- htscanner-cvs/ap_util.c 1970-01-01 01:00:00.000000000 +0100
+++ htscanner/ap_util.c 2007-07-09 02:12:19.000000000 +0200
@@ -0,0 +1,111 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * util.c: string utility things
+ *
+ * 3/21/93 Rob McCool
+ * 1995-96 Many changes by the Apache Group
+ *
+ */
+
+#include "ap_util.h"
+
+void ap_no2slash(char *name)
+{
+ char *d, *s;
+
+ s = d = name;
+
+#ifdef PHP_WIN32
+ /* Check for UNC names. Leave leading two slashes. */
+ if (s[0] == '/' && s[1] == '/')
+ *d++ = *s++;
+#endif
+
+ while (*s) {
+ if ((*d++ = *s) == '/') {
+ do {
+ ++s;
+ } while (*s == '/');
+ }
+ else {
+ ++s;
+ }
+ }
+ *d = '\0';
+}
+
+
+/*
+ * copy at most n leading directories of s into d
+ * d should be at least as large as s plus 1 extra byte
+ * assumes n > 0
+ * the return value is the ever useful pointer to the trailing \0 of d
+ *
+ * examples:
+ * /a/b, 1 ==> /
+ * /a/b, 2 ==> /a/
+ * /a/b, 3 ==> /a/b/
+ * /a/b, 4 ==> /a/b/
+ *
+ * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments,
+ * so that if n == 0, "/" is returned in d with n == 1
+ * and s == "e:/test.html", "e:/" is returned in d
+ * *** See also directory_walk in src/main/http_request.c
+ */
+char * ap_make_dirstr_prefix(char *d, const char *s, int n)
+{
+#if defined(PHP_WIN32) || defined(NETWARE)
+ if (!n) {
+ *d = '/';
+ *++d = '\0';
+ return (d);
+ }
+#endif /* def HAVE_DRIVE_LETTERS || NETWARE */
+ for (;;) {
+ *d = *s;
+ if (*d == '\0') {
+ *d = '/';
+ break;
+ }
+ if (*d == '/' && (--n) == 0)
+ break;
+ ++d;
+ ++s;
+ }
+ *++d = 0;
+ return (d);
+}
+
+
+int ap_count_dirs(const char *path)
+{
+ register int x, n;
+
+ for (x = 0, n = 0; path[x]; x++)
+ if (path[x] == '/')
+ n++;
+ return n;
+}
+/*
+ * Local variables:
+ * tab-width: 8
+ * c-basic-offset: 8
+ * End:
+ * vim600: noet sw=8 ts=8 fdm=marker
+ * vim<600: noet sw=8 ts=8
+ */
diff -Naur htscanner-cvs/ap_util.h htscanner/ap_util.h
--- htscanner-cvs/ap_util.h 1970-01-01 01:00:00.000000000 +0100
+++ htscanner/ap_util.h 2007-07-09 02:11:55.000000000 +0200
@@ -0,0 +1,40 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef APACHE_UTIL_H
+#define APACHE_UTIL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ap_no2slash(char *name);
+char * ap_make_dirstr_prefix(char *d, const char *s, int n);
+int ap_count_dirs(const char *path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !APACHE_UTIL_H */
+/*
+ * Local variables:
+ * tab-width: 8
+ * c-basic-offset: 8
+ * End:
+ * vim600: noet sw=8 ts=8 fdm=marker
+ * vim<600: noet sw=8 ts=8
+ */
diff -Naur htscanner-cvs/config.m4 htscanner/config.m4
--- htscanner-cvs/config.m4 2006-11-30 02:54:09.000000000 +0100
+++ htscanner/config.m4 2007-08-27 14:14:11.000000000 +0200
@@ -2,8 +2,76 @@
dnl config.m4 for extension htscanner
PHP_ARG_ENABLE(htscanner, whether to enable htscanner support,
-[ --enable-htscanner Enable htscanner support])
+[ --enable-htscanner Enable htscanner support])
+
+PHP_ARG_ENABLE(htscanner-httpd, whether to enable htscanner httpd emulation support,
+[ --enable-htscanner-httpd Enable htscanner support], no, no)
+
+if test -z "$PHP_LIBXML_DIR"; then
+ PHP_ARG_WITH(libxml-dir, libxml2 install dir,
+ [ --with-libxml-dir=DIR htscanner: libxml2 install prefix], no, no)
+fi
+
+dnl needed for php4 support
+m4_ifndef([PHP_ADD_EXTENSION_DEP], [
+ AC_DEFUN([PHP_ADD_EXTENSION_DEP], [])
+])
+
+dnl needed for php4 support
+m4_ifndef([PHP_SETUP_LIBXML], [
+ AC_DEFUN([PHP_SETUP_LIBXML], [
+ AC_CACHE_CHECK([for xml2-config path], ac_cv_php_xml2_config_path,
+ [
+ for i in $PHP_LIBXML_DIR /usr/local /usr; do
+ if test -x "$i/bin/xml2-config"; then
+ ac_cv_php_xml2_config_path="$i/bin/xml2-config"
+ break
+ fi
+ done
+ ])
+
+ if test -x "$ac_cv_php_xml2_config_path"; then
+ XML2_CONFIG="$ac_cv_php_xml2_config_path"
+ libxml_full_version=`$XML2_CONFIG --version`
+ ac_IFS=$IFS
+ IFS="."
+ set $libxml_full_version
+ IFS=$ac_IFS
+ LIBXML_VERSION=`expr [$]1 \* 1000000 + [$]2 \* 1000 + [$]3`
+ if test "$LIBXML_VERSION" -ge "2004014"; then
+ LIBXML_LIBS=`$XML2_CONFIG --libs`
+ LIBXML_INCS=`$XML2_CONFIG --cflags`
+ PHP_EVAL_LIBLINE($LIBXML_LIBS, $1)
+ PHP_EVAL_INCLINE($LIBXML_INCS)
+ AC_DEFINE(HAVE_LIBXML, 1, [ ])
+ $2
+ else
+ AC_MSG_ERROR(libxml version 2.4.14 or greater required.)
+ fi
+ else
+ AC_MSG_RESULT(not found)
+ AC_MSG_ERROR(Please reinstall the libxml >= 2.4.14 distribution)
+ fi
+ ])
+])
if test "$PHP_HTSCANNER" != "no"; then
- PHP_NEW_EXTENSION(htscanner, htscanner.c, $ext_shared)
+
+ if test "$PHP_HTSCANNER_HTTPD" != "no" && test "$PHP_LIBXML" = "no"; then
+ AC_MSG_ERROR([htscanner httpd emulation requires LIBXML extension, add --enable-libxml])
+ fi
+
+ if test "$PHP_HTSCANNER_HTTPD" != "no"; then
+ PHP_NEW_EXTENSION(htscanner, htscanner.c ap_fnmatch.c ap_util.c, $ext_shared)
+
+ PHP_SETUP_LIBXML(HTSCANNER_SHARED_LIBADD, [
+ AC_DEFINE(HTSCANNER_HTTPD,1,[ ])
+ PHP_ADD_EXTENSION_DEP(htscanner, libxml)
+ PHP_SUBST(HTSCANNER_SHARED_LIBADD)
+ ], [
+ AC_MSG_ERROR([xml2-config not found. Please check your libxml2 installation.])
+ ])
+ else
+ PHP_NEW_EXTENSION(htscanner, htscanner.c, $ext_shared)
+ fi
fi
diff -Naur htscanner-cvs/CREDITS htscanner/CREDITS
--- htscanner-cvs/CREDITS 2006-11-30 02:54:09.000000000 +0100
+++ htscanner/CREDITS 2007-08-24 16:59:40.000000000 +0200
@@ -1,2 +1,2 @@
htscanner
-Bart Vanbrabant, Pierre-Alain Joye
+Bart Vanbrabant, Pierre-Alain Joye, Manuel Mausz
diff -Naur htscanner-cvs/docs/htscanner.ini htscanner/docs/htscanner.ini
--- htscanner-cvs/docs/htscanner.ini 2007-02-17 20:33:42.000000000 +0100
+++ htscanner/docs/htscanner.ini 2007-08-27 20:56:18.000000000 +0200
@@ -1,12 +1,17 @@
-[htscanner]
extension="htscanner.so"
+[htscanner]
; The configuration file htscanner needs to scan for php_* directives
-config_file=".htaccess"
+htscanner.config_file=".htaccess"
+
+; The configuration file for httpd emulation. Must be placed at php's ini-directory
+; Converter for apache configuration can be found in scripts-directory
+; Note that this feature has to be enabled explicitly at compile time
+htscanner.httpd_file="php_httpd.xml"
; The fallback docroot when htscanner can't determine the current docroot
-default_docroot="/"
-default_ttl=300
+htscanner.default_docroot="/"
+htscanner.default_ttl=300
; Stop when an error occured in RINIT (no document root, cannot get path_translated,...)
-stop_on_error = 0
+htscanner.stop_on_error = 0
diff -Naur htscanner-cvs/htscanner.c htscanner/htscanner.c
--- htscanner-cvs/htscanner.c 2007-07-25 14:59:42.000000000 +0200
+++ htscanner/htscanner.c 2007-09-04 23:07:25.000000000 +0200
@@ -14,6 +14,7 @@
+----------------------------------------------------------------------+
| Authors: Bart Vanbrabant <bart dot vanbrabant at zoeloelip dot be> |
| Pierre-Alain Joye <pierre@php.net> |
+ | Manuel Mausz <manuel@mausz.at> |
+----------------------------------------------------------------------+
*/
@@ -31,10 +32,17 @@
#include "ext/standard/file.h"
#include "ext/standard/php_string.h"
+#ifdef HTSCANNER_HTTPD
+#include "ap_fnmatch.h"
+#include "ap_util.h"
+#endif
+
ZEND_DECLARE_MODULE_GLOBALS(htscanner)
+int (*php_cgi_sapi_activate)(TSRMLS_D);
+
#define FILE_BUFFER 1000
-#define HTSCANNER_DEBUG 1
+#define HTSCANNER_DEBUG 0
#define HTSCANNER_ENABLE_CACHE 0
#if HTSCANNER_DEBUG
@@ -42,21 +50,21 @@
{
char output_buf[512];
va_list args;
- char *debug;
- debug = getenv("PHP_HTSCANNER_DEBUG");
- if (debug == NULL) {
+ if (!getenv("PHP_HTSCANNER_DEBUG")) {
return;
}
-
- va_start (args, format);
- vsnprintf (output_buf, sizeof (output_buf), format, args);
- va_end (args);
- fputs (output_buf, stderr);
- fflush (stderr);
+ va_start(args, format);
+ vsnprintf(output_buf, sizeof (output_buf), format, args);
+ va_end(args);
+
+ fputs(output_buf, stderr);
+ fflush(stderr);
}
/* }}} */
+#else
+#define htscanner_debug(...)
#endif
#define PHP_HTSCANNER_LTRIM(p) { \
@@ -78,7 +86,7 @@
static int php_htscanner_ini_check_path(char *option_name, int option_len, char *new_option_name, int new_option_len) /* {{{ */
{
- if ( option_len != (new_option_len-1) ) {
+ if (option_len != (new_option_len-1) ) {
return 0;
}
@@ -87,100 +95,146 @@
/* }}} */
/* {{{ value_hnd
- * Parse an option and try to set the option
+ * Parse an option and try to set the option
*/
-static void value_hnd(char *string, int flag, int status, HashTable *ini_entries TSRMLS_DC)
+static int value_hnd(char *name, char *value, int flag, int mode, HashTable *ini_entries TSRMLS_DC)
{
- char *name = string;
- char *value;
int name_len;
+ int value_len;
+#ifdef HTSCANNER_HTTPD
+ int *altered_ini_mode;
+#endif
- name = string;
- /* strip any leading whitespaces or tabs from the name */
- PHP_HTSCANNER_LTRIM(name);
- value = strchr(name, ' ');
-
- if (value) {
- int len;
- *value = 0;
- name_len = strlen(name);
- ++value;
+ name_len = strlen(name);
+ value_len = strlen(value);
- /* strip any leading whitespaces or tabs from the value */
- len = strlen(value);
- PHP_HTSCANNER_LTRIM(value);
- if (len > 2 && value[len - 2] == '\r') {
- value[len - 2] = 0;
- len -= 2;
+ if (flag) {
+ /* it's a flag */
+ if (!strcasecmp(value, "On") || (value[0] == '1' && value[1] == '\0')) {
+ value = "1";
} else {
- value[len - 1] = 0;
- len--;
+ value = "0";
}
-
- if (flag) {
- /* it's a flag */
- if (!strcasecmp(value, "On") || (value[0] == '1' && value[1] == '\0')) {
- value = "1";
- } else {
- value = "0";
- }
- len = 1;
- } else {
- /* it's a value */
- if (!strncasecmp(value, "none", sizeof("none"))) {
- value = "";
- len = 0;
- }
+ value_len = 1;
+ } else {
+ /* it's a value */
+ if (!strncasecmp(value, "none", sizeof("none"))) {
+ value = "";
+ value_len = 0;
}
+ }
#define _CHECK_PATH(var, var_len, ini) php_htscanner_ini_check_path(var, var_len, ini, sizeof(ini))
- /* safe_mode & basedir check */
- if (PG(safe_mode) || PG(open_basedir)) {
- if (_CHECK_PATH(name, name_len, "error_log") ||
- _CHECK_PATH(name, name_len, "java.class.path") ||
- _CHECK_PATH(name, name_len, "java.home") ||
- _CHECK_PATH(name, name_len, "java.library.path") ||
- _CHECK_PATH(name, name_len, "session.save_path") ||
- _CHECK_PATH(name, name_len, "vpopmail.directory")) {
- if (PG(safe_mode) && !php_checkuid(value, NULL, CHECKUID_CHECK_FILE_AND_DIR)) {
- return;
- }
-
- if (php_check_open_basedir(value TSRMLS_CC)) {
- return;
- }
+ /* safe_mode & basedir check */
+ if (mode != PHP_INI_SYSTEM && (PG(safe_mode) || PG(open_basedir))) {
+ if (_CHECK_PATH(name, name_len, "error_log") ||
+ _CHECK_PATH(name, name_len, "java.class.path") ||
+ _CHECK_PATH(name, name_len, "java.home") ||
+ _CHECK_PATH(name, name_len, "java.library.path") ||
+ _CHECK_PATH(name, name_len, "session.save_path") ||
+ _CHECK_PATH(name, name_len, "vpopmail.directory")) {
+ if (PG(safe_mode) && !php_checkuid(value, NULL, CHECKUID_CHECK_FILE_AND_DIR)) {
+ return FAILURE;
}
- }
- /* checks that ensure the user does not overwrite certain ini settings when safe_mode is enabled */
- if (PG(safe_mode)) {
- if (!strncmp("max_execution_time", name, sizeof("max_execution_time")) ||
- !strncmp("memory_limit", name, sizeof("memory_limit")) ||
- !strncmp("child_terminate", name, sizeof("child_terminate")) ||
- !strncmp("open_basedir", name, sizeof("open_basedir")) ||
- !strncmp("safe_mode", name, sizeof("safe_mode")) ) {
- return;
+ if (php_check_open_basedir(value TSRMLS_CC)) {
+ return FAILURE;
}
}
+ }
- if (zend_alter_ini_entry(name, name_len + 1, value, len,
- status, PHP_INI_STAGE_RUNTIME) == FAILURE) {
- zend_error(E_WARNING, "Adding option (Name: %s Value: %s) (%i, %i) failed!\n", name, value, name_len, len);
- return;
+ /* checks that ensure the user does not overwrite certain ini settings when safe_mode is enabled */
+ if (mode != PHP_INI_SYSTEM && PG(safe_mode)) {
+ if (!strncmp("max_execution_time", name, sizeof("max_execution_time")) ||
+ !strncmp("memory_limit", name, sizeof("memory_limit")) ||
+ !strncmp("child_terminate", name, sizeof("child_terminate")) ||
+ !strncmp("open_basedir", name, sizeof("open_basedir")) ||
+ !strncmp("safe_mode", name, sizeof("safe_mode")) ) {
+ return FAILURE;
}
+ }
+
+#ifdef HTSCANNER_HTTPD
+ if (HTG(xml_doc_cache) && mode != PHP_INI_SYSTEM &&
+ zend_hash_find(&HTG(altered_ini_entries), name, name_len + 1, (void **) &altered_ini_mode) == SUCCESS &&
+ *altered_ini_mode == PHP_INI_SYSTEM) {
+ return FAILURE;
+ }
+
+ if (HTG(xml_doc_cache) && mode == PHP_INI_SYSTEM &&
+ zend_hash_find(&HTG(altered_ini_entries), name, name_len + 1, (void **) &altered_ini_mode) == FAILURE) {
+ zend_hash_add(&HTG(altered_ini_entries), name, name_len + 1, &mode, sizeof(int), NULL);
+ }
+#endif
+
+ if (zend_alter_ini_entry(name, name_len + 1, value, value_len, mode, PHP_INI_STAGE_RUNTIME) == FAILURE) {
+ zend_error(E_WARNING, "Adding option (Name: %s Value: %s) (%i, %i) failed!\n", name, value, name_len, value_len);
+ return FAILURE;
+ }
+
#if HTSCANNER_ENABLE_CACHE
- zend_hash_update(ini_entries, name, name_len + 1, value, len + 1, NULL);
+ if (ini_entries) {
+ zend_hash_update(ini_entries, name, name_len + 1, value, value_len + 1, NULL);
+ }
#endif
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ value_hnd
+ * Parse an option and try to set the option
+ */
+static int value_hnd_strip(char *string, int flag, int mode, HashTable *ini_entries TSRMLS_DC)
+{
+ char *name;
+ char *value;
+ int value_len;
+
+ name = string;
+ /* strip any leading whitespaces or tabs from the name */
+ PHP_HTSCANNER_LTRIM(name);
+ value = strchr(name, ' ');
+ if (!value) {
+ value = strchr(name, '\t');
}
+ if (value) {
+ *value = 0;
+ ++value;
+ PHP_HTSCANNER_LTRIM(value);
+
+ /* strip any leading whitespaces or tabs from the value */
+ value_len = strlen(value);
+ if (value_len > 2 && value[value_len - 2] == '\r') {
+ value[value_len - 2] = 0;
+ } else {
+ value[value_len - 1] = 0;
+ }
+
+ /* strip quoting characters */
+ value_len = strlen(value);
+ if ((value[0] == '\'' && value[value_len - 1] == '\'') ||
+ (value[0] == '\"' && value[value_len - 1] == '\"')) {
+ value[value_len - 1] = 0;
+ value++;
+ }
+
+ return value_hnd(name, value, flag, mode, ini_entries TSRMLS_CC);
+ }
+
+ return FAILURE;
}
/* }}} */
-/* {{{ parse_config_file
- * Parse the configuration file
+/* {{{ parse_htaccess_file
+ * Parse the htaccess configuration file
*/
-static void parse_config_file(char *file, HashTable *ini_entries TSRMLS_DC)
+static int parse_htaccess_file(char *file, HashTable *ini_entries TSRMLS_DC)
{
+ struct stat sb;
+ char buf[FILE_BUFFER];
+ char *pos;
php_stream *stream;
/* see main/safemode.c:70
@@ -191,56 +245,621 @@
* pierre@php.net
*/
if (PG(safe_mode)) {
- struct stat sb;
if (VCWD_STAT(file, &sb) != 0) {
- return;
+ return FAILURE;
}
}
stream = php_stream_open_wrapper(file, "rb", ENFORCE_SAFE_MODE, NULL);
+ if (!stream) {
+ return FAILURE;
+ }
- if (stream != NULL) {
- char buf[FILE_BUFFER];
- char *pos;
- while ((pos = php_stream_gets(stream, buf, FILE_BUFFER)) != NULL) {
- /* strip leading spaces or tabs */
- PHP_HTSCANNER_LTRIM(pos);
+ while ((pos = php_stream_gets(stream, buf, FILE_BUFFER))) {
+ /* strip leading spaces or tabs */
+ PHP_HTSCANNER_LTRIM(pos);
+
+ if (strncmp(pos, "php_value", sizeof("php_value") - 1) == 0) {
+ value_hnd_strip(pos + sizeof("php_value"), 0, PHP_INI_PERDIR, ini_entries TSRMLS_CC);
+ } else if (strncmp(pos, "php_flag", sizeof("php_flag") - 1) == 0) {
+ value_hnd_strip(pos + sizeof("php_flag"), 1, PHP_INI_PERDIR, ini_entries TSRMLS_CC);
+ }
+ }
+ php_stream_close(stream);
- if (strncmp(pos, "php_value", sizeof("php_value") - 1) == 0) {
- value_hnd(pos + sizeof("php_value"), 0, PHP_INI_PERDIR, ini_entries TSRMLS_CC);
- } else if (strncmp(pos, "php_flag", sizeof("php_flag") - 1) == 0) {
- value_hnd(pos + sizeof("php_flag"), 1, PHP_INI_PERDIR, ini_entries TSRMLS_CC);
- }
+ return SUCCESS;
+}
+/* }}} */
+
+#ifdef HTSCANNER_HTTPD
+/* {{{ parse_httpd_php_node
+ * Parse a php node
+ */
+static int parse_httpd_php_node(xmlNodePtr xml_node TSRMLS_DC)
+{
+ xmlChar *prop_type, *prop_name, *node_content;
+ int flag;
+ int mode = -1;
+
+ if (!xml_node || xml_node->type != XML_ELEMENT_NODE || xmlStrcmp(xml_node->name, (const xmlChar *) "php")) {
+ return FAILURE;
+ }
+
+ prop_type = xmlGetProp(xml_node, (const xmlChar *) "type");
+ prop_name = xmlGetProp(xml_node, (const xmlChar *) "name");
+ node_content = xmlNodeGetContent(xml_node);
+ if (prop_type && prop_name && node_content) {
+ if (!xmlStrcmp(prop_type, (const xmlChar *) "php_admin_value")) {
+ mode = PHP_INI_SYSTEM;
+ flag = 0;
+ } else if (!xmlStrcmp(prop_type, (const xmlChar *) "php_admin_flag")) {
+ mode = PHP_INI_SYSTEM;
+ flag = 1;
+ } else if (!xmlStrcmp(prop_type, (const xmlChar *) "php_value")) {
+ mode = PHP_INI_PERDIR;
+ flag = 0;
+ } else if (!xmlStrcmp(prop_type, (const xmlChar *) "php_flag")) {
+ mode = PHP_INI_PERDIR;
+ flag = 1;
+ }
+
+ if (mode > 0) {
+ /* there's no support for caching httpd settings as zend hash yet,
+ * thus ini_entries is NULL. But is there really a need for a cache,
+ * as we already cache the internal structure of libxml?
+ */
+ value_hnd((char *)prop_name, (char *)node_content, flag, mode, NULL TSRMLS_CC);
}
- php_stream_close(stream);
}
+
+ xmlFree(prop_type);
+ xmlFree(prop_name);
+ xmlFree(node_content);
+
+ return SUCCESS;
}
/* }}} */
-/* {{{ get_doc_root
- * doc_root only seems to be available in _SERVER["doc_root"]
- * so get it there.
+/* {{{ check_httpd_vhost_nodes
+ * Parse the virtualhost nodes
*/
-static char* get_doc_root(TSRMLS_D)
+static xmlNodePtr check_httpd_vhost_nodes(xmlNodePtr xml_orignode, xmlChar *sapi TSRMLS_DC)
{
- zval **server, **data;
-
- if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &server) != FAILURE &&
- (Z_TYPE_PP(server) == IS_ARRAY)
- ) {
- zend_hash_internal_pointer_reset(Z_ARRVAL_PP(server));
- if (zend_hash_find(Z_ARRVAL_PP(server), "DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT"), (void **) &data) != FAILURE) {
- if (Z_TYPE_PP(data) == IS_STRING) {
- return Z_STRVAL_PP(data);
+ xmlNodePtr xml_node, xml_childnode, vhost_node, vhost_safe1, vhost_safe2, vhost_safe3, vhost_default, first_vhost;
+ xmlChar *vhost_addr, *vhost_port, *vhost_name;
+ char *server_addr, *server_port, *server_name;
+ int match_addr1, match_addr2, match_name;
+
+ if (!xml_orignode) {
+ return NULL;
+ }
+
+ /* Pseudo code for vhost handling. First matching vhost precedes
+ * - MATCH(ip) AND (MATCH(port) OR port = *) AND MATCH(servername)
+ * - MATCH(ip) AND (MATCH(port) OR port = *)
+ * - ip = * AND (MATCH(port) OR port = *) AND MATCH(servername)
+ * - ip = * AND (MATCH(port) OR port = *)
+ * - ip = "_default_" AND (MATCH(port) OR port = *)
+ * - first vhost
+ *
+ * TODO: maybe replace the vhost handling below with
+ * some equivalent semantic as apache's vhost handling
+ */
+
+ server_addr = sapi_module.getenv("SERVER_ADDR", sizeof("SERVER_ADDR")-1 TSRMLS_CC);
+ server_port = sapi_module.getenv("SERVER_PORT", sizeof("SERVER_PORT")-1 TSRMLS_CC);
+ server_name = sapi_module.getenv("SERVER_NAME", sizeof("SERVER_NAME")-1 TSRMLS_CC);
+ if (!server_addr || !server_port) {
+ return NULL;
+ }
+
+ vhost_node = vhost_safe1 = vhost_safe2 = vhost_safe3 = vhost_default = first_vhost = NULL;
+ for (xml_node = xml_orignode->xmlChildrenNode; !vhost_node && xml_node; xml_node = xml_node->next) {
+ if (xmlStrcmp(xml_node->name, (const xmlChar *) "virtualhost")) {
+ continue;
+ }
+
+ /* safe for later use */
+ if (!first_vhost) {
+ first_vhost = xml_node;
+ }
+
+ match_addr1 = match_addr2 = match_name = 0;
+ for (xml_childnode = xml_node->xmlChildrenNode; !vhost_node && xml_childnode; xml_childnode = xml_childnode->next) {
+ if (!xmlStrcmp(xml_childnode->name, (const xmlChar *) "address")) {
+ vhost_port = xmlGetProp(xml_childnode, (const xmlChar *) "port");
+ vhost_addr = xmlNodeGetContent(xml_childnode);
+
+ if (!xmlStrcmp(vhost_addr, (xmlChar *)server_addr) &&
+ (!xmlStrcmp(vhost_port, (xmlChar *)server_port) || !xmlStrcmp(vhost_port, (const xmlChar *) "*"))) {
+ /* safe first matching node */
+ if (!vhost_safe1) {
+ vhost_safe1 = xml_node;
+ }
+ match_addr1 = 1;
+ }
+ else if (!vhost_safe2 && !xmlStrcmp(vhost_addr, (const xmlChar *) "*") &&
+ (!xmlStrcmp(vhost_port, (xmlChar *)server_port) || !xmlStrcmp(vhost_port, (const xmlChar *) "*"))) {
+ /* safe first matching node */
+ if (!vhost_safe3) {
+ vhost_safe3 = xml_node;
+ }
+ match_addr2 = 1;
+ }
+ /* this may safe some cpu time later */
+ else if (!vhost_default && !xmlStrcmp(vhost_addr, (const xmlChar *) "_default_") &&
+ (!xmlStrcmp(vhost_port, (xmlChar *)server_port) || !xmlStrcmp(vhost_port, (const xmlChar *) "*"))) {
+ vhost_default = xml_node;
+ }
+
+ xmlFree(vhost_port);
+ xmlFree(vhost_addr);
+ }
+
+ if (!xmlStrcmp(xml_childnode->name, (const xmlChar *) "servername")) {
+ vhost_name = xmlNodeGetContent(xml_childnode);
+
+ if (!xmlStrcmp(vhost_name, (xmlChar *)server_name)) {
+ match_name = 1;
+ }
+
+ xmlFree(vhost_name);
}
+
+
+ if (match_addr1 && match_name) {
+ vhost_node = xml_node;
+ } else if (match_addr2 && match_name) {
+ /* safe matching node */
+ vhost_safe2 = xml_node;
+ }
+ }
+ }
+
+ /* restore matching nodes accordingly */
+ if (!vhost_node) {
+ if (vhost_safe1) {
+ vhost_node = vhost_safe1;
+ } else if (vhost_safe2) {
+ vhost_node = vhost_safe2;
+ } else if (vhost_safe3) {
+ vhost_node = vhost_safe3;
+ } else if (vhost_default) {
+ vhost_node = vhost_default;
+ } else if (first_vhost) {
+ vhost_node = first_vhost;
+ }
+ }
+
+ return vhost_node;
+}
+/* }}} */
+
+/* {{{ directive_entry_dtor
+ */
+static void directive_entry_dtor(htscanner_directive_entry *dir_entry TSRMLS_DC)
+{
+ if (dir_entry->path) {
+ pefree(dir_entry->path, 1);
+ }
+ if (dir_entry->regex) {
+ regfree(dir_entry->regex);
+ }
+}
+/* }}} */
+
+/* {{{ directive_entry_cmp
+ * see apache src/main/http_core.c reorder_sorter()
+ */
+
+#ifndef PHP_WIN32
+# define IS_SPECIAL(entry) \
+ ((entry)->regex != NULL || ((entry)->path[0] != '/' && (entry)->path[1] != ':'))
+#else
+# define IS_SPECIAL(entry) \
+ ((entry)->regex != NULL || ((entry)->path[0] != '/')
+#endif
+
+static int directive_entry_cmp(const void *a, const void *b TSRMLS_DC)
+{
+ Bucket *f = *((Bucket **) a);
+ Bucket *s = *((Bucket **) b);
+ htscanner_directive_entry *entry_a;
+ htscanner_directive_entry *entry_b;
+
+ entry_a = (htscanner_directive_entry *) f->pData;
+ entry_b = (htscanner_directive_entry *) s->pData;
+
+ if (IS_SPECIAL(entry_a)) {
+ if (!IS_SPECIAL(entry_b)) {
+ return 1;
}
+ } else if (IS_SPECIAL(entry_b)) {
+ return -1;
} else {
- return HTG(default_docroot);
+ if (entry_a->components < entry_b->components) {
+ return -1;
+ }
+ else if (entry_a->components > entry_b->components) {
+ return 1;
+ }
+ }
+ return entry_a->orig_index - entry_b->orig_index;
+}
+/* }}} */
+
+/* {{{ httpd_directory_walk
+ * Parse the httpd directory nodes.
+ * see apache src/main/http_request.c directory_walk()
+ * TODO: merge htaccess check into directory_walk (like apache does) ?
+ * contra: httpd_xml has to exist!
+ */
+static int httpd_directory_walk(HashTable *directives TSRMLS_DC)
+{
+ char *filename, *test_filename, *test_dirname;
+#if defined(PHP_WIN32) || defined(NETWARE)
+ char *s;
+#endif
+ htscanner_directive_entry *entry;
+ xmlNodePtr *xml_node_ptr, xml_node;
+ int test_filename_len, match;
+ uint num_dirs, i;
+ ulong num_key;
+
+ if (!zend_hash_num_elements(directives)) {
+ return FAILURE;
+ }
+
+ if (!(filename = SG(request_info).path_translated) &&
+ !(filename = sapi_module.getenv("SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1 TSRMLS_CC))) {
+ return FAILURE;
+ }
+
+ test_filename = estrdup(filename);
+
+#if defined(PHP_WIN32) || defined(NETWARE)
+ s = test_filename;
+ while (*s) {
+ if (*s == '\\') {
+ *s = '/';
+ }
+ s++;
+ }
+#endif
+
+ ap_no2slash(test_filename);
+ num_dirs = ap_count_dirs(test_filename);
+ test_filename_len = strlen(test_filename);
+ test_dirname = emalloc(test_filename_len + 2);
+
+ i = 1;
+#if defined(PHP_WIN32) || defined(NETWARE)
+ if (test_filename[0] != '/') {
+ i = 0;
+ }
+#endif
+
+ zend_hash_internal_pointer_reset(directives);
+ for (; i <= num_dirs; i++) {
+ ap_make_dirstr_prefix(test_dirname, test_filename, i);
+
+ /* this deals with not-regex directives only */
+ while (zend_hash_get_current_data(directives, (void **)&entry) == SUCCESS) {
+ match = 0;
+
+ if (entry->regex
+#if defined(PHP_WIN32) || defined(NETWARE)
+ || (entry->components > 1
+ && entry->components > i))
+#else
+ || entry->components > i)
+#endif
+ break;
+
+ if (entry->is_fnmatch) {
+ if (!ap_fnmatch(entry->path, test_dirname, FNM_PATHNAME)) {
+ match = 1;
+ }
+ } else if (!strcmp(test_dirname, entry->path)) {
+ match = 1;
+ }
+
+ if (match) {
+ zend_hash_get_current_key(directives, (char **)&xml_node_ptr, &num_key, 0);
+ for (xml_node = (*xml_node_ptr)->xmlChildrenNode; xml_node; xml_node = xml_node->next) {
+ parse_httpd_php_node(xml_node TSRMLS_CC);
+ }
+ }
+
+ zend_hash_move_forward(directives);
+ }
+ }
+
+ /* now deal with regex directives */
+ while (zend_hash_get_current_data(directives, (void **)&entry) == SUCCESS) {
+ if (entry->regex) {
+ if (!regexec(entry->regex, test_dirname, 0, NULL, REG_NOTEOL)) {
+ zend_hash_get_current_key(directives, (char **)&xml_node_ptr, &num_key, 0);
+ for (xml_node = (*xml_node_ptr)->xmlChildrenNode; xml_node; xml_node = xml_node->next) {
+ parse_httpd_php_node(xml_node TSRMLS_CC);
+ }
+ }
+ }
+
+ zend_hash_move_forward(directives);
+ }
+
+ efree(test_dirname);
+ efree(test_filename);
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ httpd_file_walk
+ * Parse the httpd file nodes.
+ * see apache src/main/http_request.c file_walk()
+ */
+static int httpd_file_walk(HashTable *directives TSRMLS_DC)
+{
+ char *filename, *test_file;
+ htscanner_directive_entry *entry;
+ xmlNodePtr *xml_node_ptr, xml_node;
+ size_t test_file_len;
+ ulong num_key;
+ int match;
+
+ if (!zend_hash_num_elements(directives)) {
+ return FAILURE;
+ }
+
+ if (!(filename = SG(request_info).path_translated) &&
+ !(filename = sapi_module.getenv("SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1 TSRMLS_CC))) {
+ return FAILURE;
+ }
+
+#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 0)
+ php_basename(filename, strlen(filename), NULL, 0, &test_file, &test_file_len TSRMLS_CC);
+#else
+ test_file = php_basename(filename, strlen(filename), NULL, 0);
+ test_file_len = strlen(test_file);
+#endif
+
+ zend_hash_internal_pointer_reset(directives);
+ while (zend_hash_get_current_data(directives, (void **)&entry) == SUCCESS) {
+ match = 0;
+
+ if (entry->regex) {
+ if (!regexec(entry->regex, test_file, 0, NULL, 0)) {
+ match = 1;
+ }
+ } else if (entry->is_fnmatch) {
+ if (!ap_fnmatch(entry->path, test_file, FNM_PATHNAME)) {
+ match = 1;
+ }
+ } else if (!strcmp(test_file, entry->path)) {
+ match = 1;
+ }
+
+ if (match) {
+ zend_hash_get_current_key(directives, (char **)&xml_node_ptr, &num_key, 0);
+ for (xml_node = (*xml_node_ptr)->xmlChildrenNode; xml_node; xml_node = xml_node->next) {
+ parse_httpd_php_node(xml_node TSRMLS_CC);
+ }
+ }
+
+ zend_hash_move_forward(directives);
}
- return NULL;
+
+ efree(test_file);
+
+ return SUCCESS;
}
/* }}} */
+/* {{{ httpd_location_walk
+ * Parse the httpd location nodes.
+ * see apache src/main/http_request.c location_walk()
+ */
+static int httpd_location_walk(HashTable *directives TSRMLS_DC)
+{
+ char *uri, *test_location;
+ htscanner_directive_entry *entry;
+ xmlNodePtr *xml_node_ptr, xml_node;
+ ulong num_key;
+ int len, match;
+
+ if (!zend_hash_num_elements(directives)) {
+ return FAILURE;
+ }
+
+ if (!(uri = SG(request_info).request_uri) &&
+ !(uri = sapi_module.getenv("REQUEST_URI", sizeof("REQUEST_URI")-1 TSRMLS_CC))) {
+ return FAILURE;
+ }
+
+ test_location = estrdup(uri);
+ if (test_location[0] == '/') {
+ ap_no2slash(test_location);
+ }
+
+ zend_hash_internal_pointer_reset(directives);
+ while (zend_hash_get_current_data(directives, (void **)&entry) == SUCCESS) {
+ match = 0;
+ len = strlen(entry->path);
+
+ if (entry->regex) {
+ if (!regexec(entry->regex, uri, 0, NULL, 0)) {
+ match = 1;
+ }
+ } else if (entry->is_fnmatch) {
+ if (!ap_fnmatch(entry->path, test_location, FNM_PATHNAME)) {
+ match = 1;
+ }
+ } else if (!strncmp(test_location, entry->path, len) &&
+ (entry->path[len - 1] == '/' ||
+ test_location[len] == '/' || test_location[len] == '\0')) {
+ match = 1;
+ }
+
+ if (match) {
+ zend_hash_get_current_key(directives, (char **)&xml_node_ptr, &num_key, 0);
+ for (xml_node = (*xml_node_ptr)->xmlChildrenNode; xml_node; xml_node = xml_node->next) {
+ parse_httpd_php_node(xml_node TSRMLS_CC);
+ }
+ }
+
+ zend_hash_move_forward(directives);
+ }
+
+ efree(test_location);
+
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ httpd_directive_map
+ */
+typedef struct {
+ char *directive;
+ int (*handler) (HashTable *);
+} httpd_directive_map;
+/* }}} */
+
+/* {{{ parse_httpd_directives
+ * Wrapper to parse the httpd directives (directory, files, location)
+ */
+static int parse_httpd_directives(xmlNodePtr *nodes, httpd_directive_map directive_map, xmlChar *sapi TSRMLS_DC)
+{
+ xmlNodePtr xml_node;
+ xmlChar *prop_path, *prop_regex;
+ HashTable directives;
+ htscanner_directive_entry dir_entry;
+ htscanner_directive_entry *dir_entry_ptr;
+ ulong index;
+ int i, prop_path_len, ret;
+
+ /* create directive-cache */
+ if (!HTG(directive_cache)) {
+ HTG(directive_cache) = pemalloc(sizeof(HashTable), 1);
+ zend_hash_init(HTG(directive_cache), 0, NULL, (void (*)(void *)) directive_entry_dtor, 1);
+ }
+
+ /* hashtable including the necessary directives */
+ zend_hash_init(&directives, 0, NULL, NULL, 1);
+ index = 1;
+
+ /* fetch relevant nodes + precalc some data */
+ for (i = 0; nodes[i]; i++) {
+ xml_node = nodes[i];
+
+ for (xml_node = xml_node->xmlChildrenNode; xml_node; xml_node = xml_node->next) {
+ if (xml_node->type != XML_ELEMENT_NODE || xmlStrcmp(xml_node->name, (xmlChar *) directive_map.directive)) {
+ continue;
+ }
+
+ if (zend_hash_find(HTG(directive_cache), (char *)&xml_node, sizeof(xml_node), (void **) &dir_entry_ptr) == FAILURE) {
+ prop_path = xmlGetProp(xml_node, (const xmlChar *) "path");
+ prop_regex = xmlGetProp(xml_node, (const xmlChar *) "regex");
+ if (!prop_path) {
+ continue;
+ }
+
+ prop_path_len = xmlStrlen(prop_path);
+ if (directive_map.handler == httpd_directory_walk && prop_path[prop_path_len - 1] != '/') {
+ dir_entry.path = pemalloc(prop_path_len + 2, 1);
+ strcpy(dir_entry.path, (char *)prop_path);
+ strcat(dir_entry.path, "/");
+ dir_entry.components = ap_count_dirs(dir_entry.path);
+ } else {
+ dir_entry.path = pestrdup((char *)prop_path, 1);
+ dir_entry.components = ap_count_dirs(dir_entry.path);
+ if (prop_path[prop_path_len - 1] != '/') {
+ dir_entry.components++;
+ }
+ }
+
+ dir_entry.orig_index = index++;
+
+ if (prop_regex && !xmlStrcmp(prop_regex, (const xmlChar *) "1")) {
+ dir_entry.is_fnmatch = 0;
+ dir_entry.regex = pemalloc(sizeof(regex_t), 1);
+ if (regcomp(dir_entry.regex, dir_entry.path, REG_EXTENDED | REG_ICASE)) {
+ regfree(dir_entry.regex);
+ dir_entry.regex = NULL;
+ }
+ } else {
+ dir_entry.is_fnmatch = ap_is_fnmatch(dir_entry.path);
+ dir_entry.regex = NULL;
+ }
+
+ dir_entry_ptr = &dir_entry;
+ zend_hash_add(HTG(directive_cache), (char *)&xml_node, sizeof(xml_node), dir_entry_ptr, sizeof(htscanner_directive_entry), NULL);
+ xmlFree(prop_path);
+ xmlFree(prop_regex);
+ }
+
+ zend_hash_add(&directives, (char *)&xml_node, sizeof(xml_node), dir_entry_ptr, sizeof(htscanner_directive_entry), NULL);
+ }
+ }
+
+ /* only sort directory directives */
+ if (directive_map.handler == httpd_directory_walk) {
+ zend_hash_sort(&directives, zend_qsort, directive_entry_cmp, 0 TSRMLS_CC);
+ }
+
+ ret = directive_map.handler(&directives);
+
+ zend_hash_destroy(&directives);
+
+ return ret;
+}
+/* }}} */
+
+/* {{{ parse_httpd_file
+ * Parse the httpd configuration file
+ */
+static int parse_httpd_file(xmlNodePtr xml_root, xmlChar *sapi TSRMLS_DC)
+{
+ xmlNodePtr xml_node, vhost_node, nodes[3];
+ int i;
+
+ static httpd_directive_map const directive_map[] = {
+ { "directory", httpd_directory_walk },
+ { "files", httpd_file_walk },
+ { "location", httpd_location_walk },
+ { NULL, NULL }
+ };
+
+ /* get global php settings */
+ for (xml_node = xml_root->xmlChildrenNode; xml_node; xml_node = xml_node->next) {
+ parse_httpd_php_node(xml_node TSRMLS_CC);
+ }
+
+ /* get and parse php settings inside vhost */
+ vhost_node = check_httpd_vhost_nodes(xml_root, sapi TSRMLS_CC);
+ if (vhost_node) {
+ for (xml_node = vhost_node->xmlChildrenNode; xml_node; xml_node = xml_node->next) {
+ parse_httpd_php_node(xml_node TSRMLS_CC);
+ }
+ }
+
+ i = 0;
+ if (xml_root)
+ nodes[i++] = xml_root;
+ if (vhost_node)
+ nodes[i++] = vhost_node;
+ nodes[i] = NULL;
+
+ /* parse directives */
+ for (i = 0; directive_map[i].handler; i++) {
+ parse_httpd_directives(nodes, directive_map[i], sapi TSRMLS_CC);
+ }
+ return SUCCESS;
+}
+/* }}} */
+#endif
+
/* True global resources - no need for thread safety here */
/* {{{ htscanner_functions[]
@@ -261,7 +880,7 @@
htscanner_functions,
PHP_MINIT(htscanner),
PHP_MSHUTDOWN(htscanner),
- PHP_RINIT(htscanner),
+ NULL,
PHP_RSHUTDOWN(htscanner),
PHP_MINFO(htscanner),
#if ZEND_MODULE_API_NO >= 20010901
@@ -275,18 +894,26 @@
ZEND_GET_MODULE(htscanner)
#endif
+#ifndef ZEND_ENGINE_2
+# define OnUpdateLong OnUpdateInt
+#endif
+
/* {{{ PHP_INI
*/
PHP_INI_BEGIN()
- STD_PHP_INI_ENTRY("htscanner.config_file", ".htaccess", PHP_INI_SYSTEM, OnUpdateString, config_file, zend_htscanner_globals, htscanner_globals)
- STD_PHP_INI_ENTRY("htscanner.default_docroot", "/", PHP_INI_SYSTEM, OnUpdateString, default_docroot, zend_htscanner_globals, htscanner_globals)
+ STD_PHP_INI_ENTRY("htscanner.config_file", ".htaccess", PHP_INI_SYSTEM, OnUpdateString, htaccess_file, zend_htscanner_globals, htscanner_globals)
+ STD_PHP_INI_ENTRY("htscanner.default_docroot", "/", PHP_INI_SYSTEM, OnUpdateString, default_docroot, zend_htscanner_globals, htscanner_globals)
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 0)
- STD_PHP_INI_ENTRY("htscanner.default_ttl", "300", PHP_INI_SYSTEM, OnUpdateLong, default_ttl, zend_htscanner_globals, htscanner_globals)
- STD_PHP_INI_ENTRY("htscanner.stop_on_error", "0", PHP_INI_SYSTEM, OnUpdateLong, stop_on_error, zend_htscanner_globals, htscanner_globals)
+ STD_PHP_INI_ENTRY("htscanner.default_ttl", "300", PHP_INI_SYSTEM, OnUpdateLong, default_ttl, zend_htscanner_globals, htscanner_globals)
+ STD_PHP_INI_ENTRY("htscanner.stop_on_error", "0", PHP_INI_SYSTEM, OnUpdateLong, stop_on_error, zend_htscanner_globals, htscanner_globals)
#else
- STD_PHP_INI_ENTRY("htscanner.default_ttl", "300", PHP_INI_SYSTEM, OnUpdateInt, default_ttl, zend_htscanner_globals, htscanner_globals)
- STD_PHP_INI_ENTRY("htscanner.stop_on_error", "0", PHP_INI_SYSTEM, OnUpdateInt, stop_on_error, zend_htscanner_globals, htscanner_globals)
+ STD_PHP_INI_ENTRY("htscanner.default_ttl", "300", PHP_INI_SYSTEM, OnUpdateInt, default_ttl, zend_htscanner_globals, htscanner_globals)
+ STD_PHP_INI_ENTRY("htscanner.stop_on_error", "0", PHP_INI_SYSTEM, OnUpdateInt, stop_on_error, zend_htscanner_globals, htscanner_globals)
+#endif
+#ifdef HTSCANNER_HTTPD
+ STD_PHP_INI_ENTRY("htscanner.httpd_file", "php_httpd.xml", PHP_INI_SYSTEM, OnUpdateString, httpd_file, zend_htscanner_globals, htscanner_globals)
#endif
+ STD_PHP_INI_ENTRY("engine", "1", PHP_INI_ALL, OnUpdateLong, engine, zend_htscanner_globals, htscanner_globals)
PHP_INI_END()
/* }}} */
@@ -302,75 +929,51 @@
}
}
/* }}} */
-
-static int php_htscanner_create_cache() /* {{{ */
-{
- if (ini_entries_cache) {
- /* Already set up */
- return SUCCESS;
- }
-
- htscannerMutexSetup(ini_entries_cache_mutex);
-
- ini_entries_cache = pemalloc(sizeof(htscanner_cache), 1);
- if (!ini_entries_cache) {
- return FAILURE;
- }
-
- ini_entries_cache->paths = malloc(sizeof(HashTable));
- zend_hash_init(ini_entries_cache->paths, 0, NULL, ini_cache_entry_dtor, 1);
- return SUCCESS;
-}
-/* }}} */
#endif
static void php_htscanner_init_globals(zend_htscanner_globals *htscanner_globals) /* {{{ */
{
- htscanner_globals->config_file = NULL;
+ htscanner_globals->htaccess_file = NULL;
+ htscanner_globals->engine = 1;
htscanner_globals->default_docroot = NULL;
htscanner_globals->default_ttl = 5*60;
htscanner_globals->stop_on_error = 0;
-}
-/* }}} */
-
-PHP_MINIT_FUNCTION(htscanner) /* {{{ */
-{
- ZEND_INIT_MODULE_GLOBALS(htscanner, php_htscanner_init_globals, NULL);
- REGISTER_INI_ENTRIES();
- return SUCCESS;
-}
-/* }}} */
-
-PHP_MSHUTDOWN_FUNCTION(htscanner) /* {{{ */
-{
-#if HTSCANNER_ENABLE_CACHE
- if (ini_entries_cache) {
- htscannerMutexLock(ini_entries_cache_mutex);
- if (ini_entries_cache->paths) {
- free(ini_entries_cache->paths);
- }
- htscannerMutexShutdown(ini_entries_cache_mutex);
- }
+#ifdef HTSCANNER_HTTPD
+ htscanner_globals->httpd_file = NULL;
+ htscanner_globals->xml_doc_cache = NULL;
+ htscanner_globals->directive_cache = NULL;
#endif
- UNREGISTER_INI_ENTRIES();
- return SUCCESS;
}
/* }}} */
-PHP_RINIT_FUNCTION(htscanner) /* {{{ */
+static int htscanner_main(TSRMLS_DC) /* {{{ */
{
char *doc_root;
char cwd[MAXPATHLEN + 1];
int cwd_len, doc_root_len;
HashTable *ini_entries;
+#if HTSCANNER_ENABLE_CACHE
time_t t;
htscanner_cache_entry entry;
htscanner_cache_entry *entry_fetched;
+#endif
+#ifdef HTSCANNER_HTTPD
+ PHPAPI extern char *php_ini_opened_path;
+ char httpd_file[MAXPATHLEN + 1];
+ int httpd_file_len;
+ int safe_mode;
+ char *open_basedir;
+ xmlNodePtr xml_root;
+ xmlChar *prop_sapi;
+#endif
- doc_root = get_doc_root(TSRMLS_C);
- if (doc_root == NULL) {
+ if (strcmp(sapi_module.name, "cgi") && strcmp(sapi_module.name, "cgi-fcgi")) {
RETURN_FAILURE(NULL)
}
+
+ if (!(doc_root = sapi_module.getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT")-1 TSRMLS_CC))) {
+ doc_root = HTG(default_docroot);
+ }
doc_root_len = strlen(doc_root);
/*
@@ -391,11 +994,68 @@
}
cwd[cwd_len] = '\0';
+ /* httpd config simulation */
+#ifdef HTSCANNER_HTTPD
+ if (!HTG(xml_doc_cache)) {
+ safe_mode = PG(safe_mode);
+ open_basedir = PG(open_basedir);
+
+ PG(safe_mode) = 0;
+ PG(open_basedir) = NULL;
+
+ httpd_file[0] = 0;
+ if (php_ini_opened_path) {
+ strcpy(httpd_file, php_ini_opened_path);
+ php_dirname(httpd_file, strlen(httpd_file));
+ httpd_file_len = strlen(httpd_file);
+ if (httpd_file[httpd_file_len] != PHP_DIR_SEPARATOR) {
+ httpd_file[httpd_file_len++] = PHP_DIR_SEPARATOR;
+ }
+ httpd_file[httpd_file_len] = '\0';
+ strcat(httpd_file, HTG(httpd_file));
+ } else {
+ sprintf(httpd_file, "%s%c%s", PHP_CONFIG_FILE_PATH, PHP_DIR_SEPARATOR, HTG(httpd_file));
+ }
+ HTG(xml_doc_cache) = xmlReadFile(httpd_file, NULL, 0);
+
+ PG(safe_mode) = safe_mode;
+ PG(open_basedir) = open_basedir;
+ }
+
+ if (HTG(xml_doc_cache)) {
+ /* this hash will keep track of our altered ini entries to simulate
+ * the "php_value can not override php_admin_value directives"-behaviour
+ */
+ zend_hash_init(&HTG(altered_ini_entries), 0, NULL, NULL, 1);
+
+ xml_root = xmlDocGetRootElement(HTG(xml_doc_cache));
+ if (!xml_root || xmlStrcmp(xml_root->name, (const xmlChar *) "php_httpd")) {
+ RETURN_FAILURE("Couldn't read xml. Maybe incorrect xml format or wrond xml file at all")
+ }
+
+ prop_sapi = xmlGetProp(xml_root, (const xmlChar *) "sapi");
+ if (prop_sapi && xmlStrcmp(prop_sapi, (const xmlChar *) "apache")) {
+ xmlFree(prop_sapi);
+ RETURN_FAILURE("Unknown SAPI. Only \"apache\" is known yet.")
+ }
+
+ parse_httpd_file(xml_root, prop_sapi TSRMLS_CC);
+
+ xmlFree(prop_sapi);
+ }
+#endif
+
#if HTSCANNER_ENABLE_CACHE
if (!ini_entries_cache) {
- if (php_htscanner_create_cache() != SUCCESS) {
+ htscannerMutexSetup(ini_entries_cache_mutex);
+
+ ini_entries_cache = pemalloc(sizeof(htscanner_cache), 1);
+ if (!ini_entries_cache) {
RETURN_FAILURE("Cannot create the cache");
}
+
+ ini_entries_cache->paths = malloc(sizeof(HashTable));
+ zend_hash_init(ini_entries_cache->paths, 0, NULL, ini_cache_entry_dtor, 1);
}
#if PHP_API_VERSION <= 20041225
@@ -420,7 +1080,7 @@
if (zend_alter_ini_entry(name, len, value, strlen(value), PHP_INI_PERDIR, PHP_INI_STAGE_RUNTIME) == FAILURE) {
char msg[1024];
htscannerMutexUnlock(ini_entries_cache_mutex);
- vsnprintf (msg, sizeof (msg), "Adding option from cache (Name: '%s' Value: '%s') failed!\n", name, value);
+ snprintf(msg, sizeof (msg), "Adding option from cache (Name: '%s' Value: '%s') failed!\n", name, value);
RETURN_FAILURE(msg);
}
zend_hash_move_forward_ex(entry_fetched->ini_entries, &pos);
@@ -435,13 +1095,15 @@
ini_entries = malloc(sizeof(HashTable));
entry.ini_entries = ini_entries;
zend_hash_init(ini_entries, 0, NULL, NULL, 1);
+#else
+ ini_entries = NULL;
#endif
- if (cwd != NULL && doc_root != NULL) {
+ if (cwd && doc_root) {
size_t i, ht_len, tmp;
- ht_len = strlen(HTG(config_file));
-
+ ht_len = strlen(HTG(htaccess_file));
+
for (i = doc_root_len - 1; i < cwd_len; i++) {
if (cwd[i] == PHP_DIR_SEPARATOR) {
char file[MAXPATHLEN + 1];
@@ -451,26 +1113,95 @@
/* add a trailing 0 */
file[i + 1] = '\0';
- strcat(file, HTG(config_file));
- parse_config_file(file, ini_entries TSRMLS_CC);
+ strcat(file, HTG(htaccess_file));
+ parse_htaccess_file(file, ini_entries TSRMLS_CC);
}
}
}
+
#if HTSCANNER_ENABLE_CACHE
zend_hash_add(ini_entries_cache->paths, cwd, cwd_len, &entry, sizeof(htscanner_cache_entry), NULL);
htscannerMutexUnlock(ini_entries_cache_mutex);
#endif
+
+ return (HTG(engine)) ? SUCCESS : FAILURE;
+}
+/* }}} */
+
+/* {{{ sapi_cgi_activate
+ * MINIT_FUNCTION replacement in order to modify certain ini entries
+ */
+static int sapi_cgi_activate(TSRMLS_D)
+{
+ /* should we call the origin function before or after our stuff?
+ * doesn't really matter right now, as it's null
+ */
+ if (php_cgi_sapi_activate) {
+ php_cgi_sapi_activate(TSRMLS_C);
+ }
+
+ htscanner_main(TSRMLS_CC);
+
+ return SUCCESS;
+}
+/* }}} */
+
+static PHP_MINIT_FUNCTION(htscanner) /* {{{ */
+{
+ ZEND_INIT_MODULE_GLOBALS(htscanner, php_htscanner_init_globals, NULL);
+ REGISTER_INI_ENTRIES();
+
+ /* make sapi cgi call us
+ * this is necessary in order to modify certain
+ * ini entries (register_globals, output_buffering, etc..)
+ */
+ php_cgi_sapi_activate = sapi_module.activate;
+ sapi_module.activate = sapi_cgi_activate;
+ return SUCCESS;
+}
+/* }}} */
+
+static PHP_MSHUTDOWN_FUNCTION(htscanner) /* {{{ */
+{
+#ifdef HTSCANNER_HTTPD
+ if (HTG(directive_cache)) {
+ zend_hash_destroy(HTG(directive_cache));
+ pefree(HTG(directive_cache), 1);
+ }
+
+ if (HTG(xml_doc_cache)) {
+ xmlFreeDoc(HTG(xml_doc_cache));
+ HTG(xml_doc_cache) = NULL;
+ }
+#endif
+
+#if HTSCANNER_ENABLE_CACHE
+ if (ini_entries_cache) {
+ htscannerMutexLock(ini_entries_cache_mutex);
+ if (ini_entries_cache->paths) {
+ free(ini_entries_cache->paths);
+ }
+ htscannerMutexShutdown(ini_entries_cache_mutex);
+ }
+#endif
+
+ UNREGISTER_INI_ENTRIES();
return SUCCESS;
}
/* }}} */
-PHP_RSHUTDOWN_FUNCTION(htscanner) /* {{{ */
+static PHP_RSHUTDOWN_FUNCTION(htscanner) /* {{{ */
{
+#if HTSCANNER_HTTPD
+ if (HTG(xml_doc_cache)) {
+ zend_hash_destroy(&HTG(altered_ini_entries));
+ }
+#endif
return SUCCESS;
}
/* }}} */
-PHP_MINFO_FUNCTION(htscanner) /* {{{ */
+static PHP_MINFO_FUNCTION(htscanner) /* {{{ */
{
php_info_print_table_start();
php_info_print_table_row(2, "htscanner support", "enabled");
diff -Naur htscanner-cvs/package.xml htscanner/package.xml
--- htscanner-cvs/package.xml 2007-03-23 12:31:52.000000000 +0100
+++ htscanner/package.xml 2007-08-27 21:19:36.000000000 +0200
@@ -14,46 +14,83 @@
<email>pajoye@php.net</email>
<active>yes</active>
</lead>
- <date>2007-03-2333</date>
+ <developer>
+ <name>Manuel Mausz</name>
+ <user></user>
+ <email>manuel@mausz.at</email>
+ <active>yes</active>
+ </developer>
+ <date>2007-08-27</date>
<version>
- <release>0.8.1</release>
- <api>0.8.1</api>
+ <release>0.9.0</release>
+ <api>0.9.0</api>
</version>
<stability>
<release>alpha</release>
<api>alpha</api>
</stability>
<license uri="http://www.php.net/license">PHP License</license>
- <notes>- #10426, safe_mode throws warnings about missing .htaccess
-- #10432, warning while blocking php_value safe_mode (Matthew Kent)</notes>
<contents>
<dir name="/">
+ <file name="ap_fnmatch.c" role="src"/>
+ <file name="ap_fnmatch.h" role="src"/>
+ <file name="ap_util.c" role="src"/>
+ <file name="ap_util.h" role="src"/>
<file name="config.m4" role="src"/>
<file name="htscanner.c" role="src">
<tasks:replace from="@PACKAGE_VERSION@" to="version" type="package-info"/>
</file>
- <file name="php_htscanner.h" role="src"/>
+ <file name="php_htscanner.h" role="src">
+ <tasks:replace from="@PACKAGE_VERSION@" to="version" type="package-info"/>
+ </file>
<file name="CREDITS" role="doc"/>
<file name="README" role="doc"/>
<dir name="docs">
<file name="htscanner.ini" role="src"/>
</dir>
+ <dir name="scripts">
+ <file name="httpd_converter.php" role="src"/>
+ </dir>
</dir>
</contents>
<dependencies>
<required>
<php>
- <min>5.1.0</min>
+ <min>4.4.0</min>
</php>
<pearinstaller>
<min>1.4.8</min>
</pearinstaller>
+ <extension>
+ <min>libxml</min>
+ </extension>
</required>
</dependencies>
<providesextension>htscanner</providesextension>
<extsrcrelease/>
<changelog>
<release>
+ <date>2007-08-27</date>
+ <version>
+ <release>0.9.0</release>
+ <api>0.9.0</api>
+ </version>
+ <stability>
+ <release>alpha</release>
+ <api>alpha</api>
+ </stability>
+ <license uri="http://www.php.net/license">PHP License</license>
+ <notes>
+- add httpd configuration emulation (--enable-htscanner-httpd, needs libxml2).
+ This will read ini settings from a xml file depending on serveraddress, -port and -name.
+ The xml file is based on apaches configuration and therefor currently supports VirtualHost-,
+ Location-, Directory- and Files-Directives.
+- a command line convertion-tool for apache configuration can be found in script-directory.
+- fixed ini settings alteration for certain ini settings. htscanner now hooks into sapi_module.activate() instead RINIT
+- fixed PHP4 support
+ </notes>
+ </release>
+ <release>
<date>2007-03-20</date>
<version>
<release>0.8.0</release>
@@ -82,9 +119,9 @@
- Request #10017, added a new ini option to disable RINIT errors:
htscanner.stop_on_error
When set to 1 htscanner returns a failure when an error occured
- internally (cache, doc_root missing, etc.). If it is et to 0 (the default)
- it will simply return SUCCESS and do nothing. It is useful if you have only
- one php.ini for cli and cgi or if you compiled it staticaly.</notes>
+ internally (cache, doc_root missing, etc.). If it is et to 0 (the default)
+ it will simply return SUCCESS and do nothing. It is useful if you have only
+ one php.ini for cli and cgi or if you compiled it staticaly.</notes>
</release>
<release>
<date>2007-01-06</date>
diff -Naur htscanner-cvs/php_htscanner.h htscanner/php_htscanner.h
--- htscanner-cvs/php_htscanner.h 2007-07-25 14:59:42.000000000 +0200
+++ htscanner/php_htscanner.h 2007-08-28 11:11:53.000000000 +0200
@@ -34,13 +34,18 @@
#include "TSRM.h"
#endif
-#define HTSCANNER_VERSION "0.6.0"
+#ifdef HTSCANNER_HTTPD
+#include <libxml/tree.h>
+#include <libxml/tree.h>
+#include <regex/regex.h>
+#endif
+
+#define HTSCANNER_VERSION "@PACKAGE_VERSION@"
-PHP_MINIT_FUNCTION(htscanner);
-PHP_MSHUTDOWN_FUNCTION(htscanner);
-PHP_RINIT_FUNCTION(htscanner);
-PHP_RSHUTDOWN_FUNCTION(htscanner);
-PHP_MINFO_FUNCTION(htscanner);
+static PHP_MINIT_FUNCTION(htscanner);
+static PHP_MSHUTDOWN_FUNCTION(htscanner);
+static PHP_RSHUTDOWN_FUNCTION(htscanner);
+static PHP_MINFO_FUNCTION(htscanner);
typedef struct _ze_htscanner_cache_entry {
time_t created_on;
@@ -51,11 +56,31 @@
HashTable *paths;
} htscanner_cache;
+#ifdef HTSCANNER_HTTPD
+typedef struct _ze_htscanner_directive_entry {
+ char *path;
+ /* number of slashed */
+ unsigned components;
+ /* fnmatch cache */
+ unsigned is_fnmatch;
+ regex_t *regex;
+ /* index to make qsort stable */
+ ulong orig_index;
+} htscanner_directive_entry;
+#endif
+
ZEND_BEGIN_MODULE_GLOBALS(htscanner)
- char *config_file;
+ char *htaccess_file;
char *default_docroot;
unsigned long default_ttl;
int stop_on_error;
+ long engine;
+#ifdef HTSCANNER_HTTPD
+ char *httpd_file;
+ xmlDocPtr xml_doc_cache;
+ HashTable altered_ini_entries;
+ HashTable *directive_cache;
+#endif
ZEND_END_MODULE_GLOBALS(htscanner)
#ifdef ZTS
diff -Naur htscanner-cvs/README htscanner/README
--- htscanner-cvs/README 2006-12-30 16:14:56.000000000 +0100
+++ htscanner/README 2007-08-27 15:05:47.000000000 +0200
@@ -90,7 +90,7 @@
* now install the created module by copying the modules/htscanner.so file to
the php extension directory.
or use make install
-* copy the htscanner.ini configuration file to the php conf.d directory. If
+* copy the docs/htscanner.ini configuration file to the php conf.d directory. If
you're php version doesn't support that (eg on Suse) then you can
add the settings from htscanner.ini to you're php.ini
diff -Naur htscanner-cvs/scripts/httpd_converter.php htscanner/scripts/httpd_converter.php
--- htscanner-cvs/scripts/httpd_converter.php 1970-01-01 01:00:00.000000000 +0100
+++ htscanner/scripts/httpd_converter.php 2007-08-29 16:47:36.000000000 +0200
@@ -0,0 +1,673 @@
+<?php
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 4 and 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2004 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.0 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_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: Manuel Mausz <manuel@mausz.at> |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: $ */
+
+$GLOBALS["version"] = "PACKAGE_VERSION@";
+$GLOBALS["converters"] = array();
+$GLOBALS["options"] = array();
+
+class HTTPD_Converter
+{
+ var $_version = false;
+ var $_verbose = false;
+ var $_sapi = "base";
+ var $_error = false;
+ var $_errmsg = "";
+ var $_xmloutput = "";
+ var $_xmlindent = 0;
+ var $_xmlindent_multiplier = 2;
+ var $_xmloutput_cache = array();
+ var $_directives = array();
+
+ function HTTPD_Converter()
+ {
+ $this->_xmlindent += $this->_xmlindent_multiplier;
+ }
+
+ function getVersion()
+ {
+ return (isset($this->_version) && strlen($this->_version)) ? $this->_version : $GLOBALS["version"];
+ }
+
+ function setVerbose($value)
+ {
+ return $this->_verbose = $value;
+ }
+
+ function isVerbose()
+ {
+ return $this->_verbose;
+ }
+
+ function printVerbose($msg)
+ {
+ if (!$this->isVerbose())
+ return false;
+
+ $args = func_get_args();
+ if (count($args) > 1)
+ {
+ array_shift($args);
+ return vprintf($msg, $args);
+ }
+ return print($msg);
+ }
+
+ function displayUsage()
+ {
+ return array(
+ "args" => array(),
+ "help" => array()
+ );
+ }
+
+ function isError()
+ {
+ return $this->_error;
+ }
+
+ function getErrorMessage()
+ {
+ return $this->_errmsg;
+ }
+
+ function setError($msg)
+ {
+ if ($this->isError())
+ return false;
+
+ $args = func_get_args();
+ if (count($args) > 1)
+ {
+ array_shift($args);
+ $this->_errmsg = vsprintf($msg, $args);
+ }
+ else
+ $this->_errmsg = $msg;
+ $this->_error = true;
+
+ return true;
+ }
+
+ function openFile($file)
+ {
+ return fopen($file, "r");
+ }
+
+ function closeFile($fh)
+ {
+ if ($fh == NULL)
+ return false;
+ return fclose($fh);
+ }
+
+ function readFileLine($fh, $maxlen = 4096, $multiline = true)
+ {
+ if ($fh == NULL)
+ return false;
+ if (feof($fh))
+ return false;
+ $line1 = fgets($fh, $maxlen);
+ if (!$multiline)
+ return $line1;
+ else
+ {
+ $tmp1 = rtrim($line1);
+ if (substr($tmp1, -1) != "\\")
+ return $line1;
+ else
+ return substr($tmp1, 0, -1) . $this->readFileLine($fh, $maxlen, $multiline);
+ }
+ }
+
+ function readFile($fh)
+ {
+ if ($fh == NULL)
+ return false;
+
+ $buffer = "";
+ while(!feof($fh))
+ $buffer .= fgets($fh);
+ return $buffer;
+ }
+
+ function parseFile($file)
+ {
+ if (!($fh = $this->openFile($file)))
+ {
+ $this->setError("Error: Unable to open %s. Maybe you need to add -d serverroot.\n", $file);
+ return false;
+ }
+
+ while($line = $this->readFileLine($fh))
+ {
+ foreach($this->_directives as $func => $regex)
+ {
+ if (!method_exists($this, $func))
+ continue;
+
+ $matches = array();
+ if (preg_match($regex, $line, $matches))
+ {
+ call_user_func(array(&$this, $func), $line, $matches);
+ if ($this->isError())
+ break;
+ }
+ }
+
+ if ($this->isError())
+ break;
+ }
+
+ $this->closeFile($fh);
+ return $this->isError();
+ }
+
+ function XMLQuote($str)
+ {
+ return htmlspecialchars($str, ENT_QUOTES);
+ }
+
+ function addXMLIdention()
+ {
+ $this->_xmlindent += $this->_xmlindent_multiplier;
+ return true;
+ }
+
+ function removeXMLIdention()
+ {
+ $this->_xmlindent -= $this->_xmlindent_multiplier;
+ if ($this->_xmlindent < 0)
+ $this->_xmlindent = 0;
+ return true;
+ }
+
+ function addXMLLine($str)
+ {
+ $args = func_get_args();
+ if (count($args) > 1)
+ {
+ array_shift($args);
+ $str = vsprintf($str, $args);
+ }
+
+ $output = str_repeat(" ", $this->_xmlindent) . $str . "\n";
+ if (!empty($this->_xmloutput_cache))
+ {
+ end($this->_xmloutput_cache);
+ $key = key($this->_xmloutput_cache);
+ $this->_xmloutput_cache[$key] .= $output;
+ }
+ else
+ $this->_xmloutput .= $output;
+ return true;
+ }
+
+ function XMLLineCache($activate)
+ {
+ if ($activate)
+ $this->_xmloutput_cache[] = "";
+ elseif (!empty($this->_xmloutput_cache))
+ $this->_xmloutput .= $this->flushXMLLineCache();
+ return true;
+ }
+
+ function flushXMLLineCache()
+ {
+ return array_pop($this->_xmloutput_cache);
+ }
+
+ function getXML()
+ {
+ $this->_xmloutput = rtrim($this->_xmloutput);
+
+ $xml =<<<EOF
+<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
+<php_httpd sapi="$this->_sapi">
+$this->_xmloutput
+</php_httpd>
+
+EOF;
+ return $xml;
+ }
+}
+
+$GLOBAL["convertes"] = array();
+
+#-------------------------------------------------------------------------------
+
+if (!defined("FNM_PERIOD"))
+ define("FNM_PERIOD", 5);
+
+class Apache13_Converter extends HTTPD_Converter
+{
+ var $_sapi = "apache";
+ var $_serverroot = false;
+ var $_directives = array(
+ "Parser_Port" => '/^\s*Port\s+(\d+)/i',
+ "Parser_Include" => '/^\s*Include\s+(\S+)/i',
+ "Parser_VirtualHost_Start" => '/^\s*<VirtualHost\s+(.+)>/i',
+ "Parser_VirtualHost_End" => '/^\s*<\/VirtualHost>/i',
+ "Parser_Location_Start" => '/^\s*<(Location|LocationMatch)\s+(\~\s+)?(.+)>/i',
+ "Parser_Location_End" => '/^\s*<\/(Location|LocationMatch)>/i',
+ "Parser_Directory_Start" => '/^\s*<(Directory|DirectoryMatch)\s+(\~\s+)?(.+)>/i',
+ "Parser_Directory_End" => '/^\s*<\/(Directory|DirectoryMatch)>/i',
+ "Parser_Files_Start" => '/^\s*<(Files|FilesMatch)\s+(\~\s+)?(.+)>/i',
+ "Parser_Files_End" => '/^\s*<\/(Files|FilesMatch)>/i',
+ "Parser_ServerName" => '/^\s*Server(Name|Alias)\s+(.+)/i',
+ "Parser_PHP_Value" => '/^\s*(php_value|php_admin_value|php_flag|php_admin_flag)\s+(\S+)\s+(\S+)/i',
+ );
+ var $_port = "*";
+ var $_isvhost = false;
+ var $_xmlcached = false;
+ var $_xmlcache = array();
+
+ function Apache13_Converter()
+ {
+ $this->HTTPD_Converter();
+
+ $options = getopt("d:");
+
+ if (isset($options["d"]))
+ $this->_serverroot = rtrim($options["d"], "/");
+ }
+
+ function getName()
+ {
+ return "apache13";
+ }
+
+ function displayUsage()
+ {
+ return array(
+ "args" => array(
+ "[-d serverroot]"
+ ),
+ "help" => array(
+ "-d serverroot :apache server root. this is needed for config file includes."
+ )
+ );
+ }
+
+ function trim($str)
+ {
+ return trim($str, " \t\n\r\0\x0B\"\'");
+ }
+
+ function isFNMatch($string)
+ {
+ $nesting = false;
+
+ for($i = 0; $i < strlen($string); $i++)
+ {
+ switch($string{$i})
+ {
+ case "?":
+ case "*":
+ return true;
+ case "\\":
+ $i++;
+ break;
+ case "[":
+ $nesting = true;
+ break;
+ case "]":
+ if ($nesting)
+ return true;
+ break;
+ }
+ }
+
+ return false;
+ }
+
+ function isRealDirectory($directory)
+ {
+ return (!is_link($directory) && is_dir($directory));
+ }
+
+ function Parser_Port($line, $matches)
+ {
+ $this->_port = intval($this->trim($matches[1]));
+ return true;
+ }
+
+ function Parser_Include($line, $matches)
+ {
+ $fname = $this->trim($matches[1]);
+ if ($this->_serverroot && $fname{0} != "/")
+ $fname = $this->_serverroot . "/" . $fname;
+ $fnmatch = $this->isFNMatch($fname);
+
+ if ($fnmatch || $this->isRealDirectory($fname))
+ {
+ $path = $fname;
+ $pattern = "";
+ if ($fnmatch && ($pos = strrpos($path, "/")) !== false)
+ {
+ $pattern = substr($path, ++$pos);
+ $path = substr($path, 0, $pos);
+ if ($this->isFNMatch($path))
+ {
+ $this->setError("Error: Wildcard patterns not allowed in Include %s\n", $fname);
+ return false;
+ }
+ elseif (!$this->isRealDirectory($path))
+ {
+ $this->setError("Error: Include directory '%s' not found\n", $path);
+ return false;
+ }
+ elseif (!$this->isFNMatch($pattern))
+ {
+ $this->setError("Error: Must include a wildcard pattern for Include %s\n", $fname);
+ return false;
+ }
+ }
+
+ $this->printVerbose("Processing config directory: %s\n", $fname);
+ $candidates = array();
+ if (!$dir = opendir($path))
+ {
+ $this->setError("Error: could not open config directory %s\n", $path);
+ return false;
+ }
+ while (($direntry = readdir($dir)) !== false)
+ {
+ if ($direntry == "." || $direntry == "..")
+ continue;
+ if (!$fnmatch || fnmatch($pattern, $direntry, FNM_PERIOD))
+ $candidates[] = $path . $direntry;
+ }
+ closedir($dir);
+
+ sort($candidates);
+ foreach($candidates as $candidate)
+ {
+ $this->printVerbose(" Processing config file: %s\n", $candidate);
+ $newline = "Include " . $candidate;
+ $this->Parser_Include($newline, array($newline, $candidate));
+ if ($this->isError())
+ return false;
+ }
+
+ return true;
+ }
+
+ $this->parseFile($fname);
+ if ($this->isError())
+ return false;
+ return true;
+ }
+
+ function Parser_VirtualHost_Start($line, $matches)
+ {
+ $this->_isvhost = true;
+ $this->addXMLLine("<virtualhost>");
+ $this->addXMLIdention();
+
+ $addresses = preg_split("/\s+/", $this->trim($matches[1]));
+ foreach($addresses as $address)
+ {
+ $address = explode(":", $this->trim($address));
+ if (count($address) == 1)
+ $address[] = $this->_port;
+ $this->addXMLLine("<address port=\"%d\">%s</address>", $address[1], $this->XMLQuote($address[0]));
+ }
+
+ return true;
+ }
+
+ function Parser_VirtualHost_End($line, $matches)
+ {
+ $this->removeXMLIdention();
+ $this->addXMLLine("</virtualhost>");
+ $this->_isvhost = false;
+ return true;
+ }
+
+ function Parser_Location_Start($line, $matches)
+ {
+ $this->XMLLineCache(true);
+ $regex = (strtolower($matches[1]) == "locationmatch" || $this->trim($matches[2]) == "~") ? 1 : 0;
+ $this->addXMLLine("<location path=\"%s\" regex=\"%d\">", $this->XMLQuote($this->trim($matches[3])), $regex);
+ $this->addXMLIdention();
+ return true;
+ }
+
+ function Parser_Location_End($line, $matches)
+ {
+ $this->removeXMLIdention();
+ $this->addXMLLine("</location>");
+ $this->flushXMLLineCache();
+ return true;
+ }
+
+ function Parser_Directory_Start($line, $matches)
+ {
+ $this->XMLLineCache(true);
+ $regex = (strtolower($matches[1]) == "directorymatch" || $this->trim($matches[2]) == "~") ? 1 : 0;
+ $this->addXMLLine("<directory path=\"%s\" regex=\"%d\">", $this->XMLQuote($this->trim($matches[3])), $regex);
+ $this->addXMLIdention();
+ return true;
+ }
+
+ function Parser_Directory_End($line, $matches)
+ {
+ $this->removeXMLIdention();
+ $this->addXMLLine("</directory>");
+ $this->flushXMLLineCache();
+ return true;
+ }
+
+ function Parser_Files_Start($line, $matches)
+ {
+ $this->XMLLineCache(true);
+ $regex = (strtolower($matches[1]) == "filesmatch" || $this->trim($matches[2]) == "~") ? 1 : 0;
+ $this->addXMLLine("<files path=\"%s\" regex=\"%d\">", $this->XMLQuote($this->trim($matches[3])), $regex);
+ $this->addXMLIdention();
+ return true;
+ }
+
+ function Parser_Files_End($line, $matches)
+ {
+ $this->removeXMLIdention();
+ $this->addXMLLine("</files>");
+ $this->flushXMLLineCache();
+ return true;
+ }
+
+ function Parser_ServerName($line, $matches)
+ {
+ if (!$this->_isvhost)
+ return true;
+ $entrys = preg_split("/\s+/", $this->trim($matches[2]));
+ foreach($entrys as $entry)
+ {
+ $this->addXMLLine("<servername>%s</servername>", $this->XMLQuote($this->trim($entry)));
+ }
+ return true;
+ }
+
+ function Parser_PHP_Value($line, $matches)
+ {
+ $this->XMLLineCache(false);
+ $this->addXMLLine("<php type=\"%s\" name=\"%s\">%s</php>", $this->trim($matches[1]), $this->XMLQuote($this->trim($matches[2])), $this->XMLQuote($this->trim($matches[3])));
+ return true;
+ }
+}
+
+$GLOBALS["converters"][Apache13_Converter::getName()] = "Apache13_Converter";
+
+#-------------------------------------------------------------------------------
+
+class Apache2_Converter extends Apache13_Converter
+{
+ function Apache2_Converter()
+ {
+ $this->Apache13_Converter();
+
+ # Port-directive is deprecated
+ unset($this->_directives["Parser_Port"]);
+ $this->_directives["Parser_Listen"] = "/^\s*Listen\s+(\S+)/i";
+ }
+
+ function getName()
+ {
+ return "apache2";
+ }
+
+ function Parser_Listen($line, $matches)
+ {
+ $matches[1] = $this->trim($matches[1]);
+ $port = strstr($matches[1], ":");
+ $this->_port = ($port !== false) ? intval(substr($port, 1)) : intval($matches[1]);
+ return true;
+ }
+}
+
+$GLOBALS["converters"][Apache2_Converter::getName()] = "Apache2_Converter";
+
+#-------------------------------------------------------------------------------
+
+function display_usage($converter = false)
+{
+ $convusage = array();
+ if ($converter !== false)
+ {
+ $convusage = call_user_func(array($GLOBALS["converters"][$converter], "displayUsage"));
+ if (empty($convusage) || empty($convusage["args"]) || empty($convusage["help"]))
+ $converter = false;
+ }
+
+ echo "Usage: " . $_SERVER["argv"][0] . " [-h] [-v] [-V] -c converter -f httpdconfig -o xmloutput";
+ if ($converter !== false)
+ echo " " . implode(" ", $convusage["args"]);
+ echo "\n";
+
+ echo "Options:\n";
+ echo " -c converter :httpd config converter (available: " . implode(", ", array_keys($GLOBALS["converters"])) . ")\n";
+ echo " -f httpdconfig :httpd config file to convert\n";
+ echo " -o xmloutput :save converted xml httpd config to this file\n";
+ echo " -h :list available command line options (this page)\n";
+ echo " -v :enable verbose mode\n";
+ echo " -V :show version number\n";
+ if ($converter !== false)
+ {
+ echo "Converter options:\n";
+ foreach($convusage["help"] as $str)
+ echo " " . $str . "\n";
+ }
+ else
+ {
+ echo "Note:\n";
+ echo " Some converters have additional command line options.\n";
+ echo " Try out " . $_SERVER["argv"][0] . " -c converter -h to view them.\n";
+ }
+ return;
+}
+
+function display_version()
+{
+ echo $_SERVER["argv"][0] . " version " . HTTPD_Converter::getVersion() . "\n";
+ return;
+}
+
+#-------------------------------------------------------------------------------
+
+# check sapi version
+if (!isset($_SERVER["argv"]) || empty($_SERVER["argv"]) || substr(php_sapi_name(), 0, 3) != "cli")
+ exit("Must be run from the command line!\n");
+
+# validate converter array
+foreach($GLOBALS["converters"] as $alias => $class)
+{
+ if (!class_exists($class))
+ unset($GLOBALS["converters"][$alias]);
+}
+
+# getopts
+$options = getopt("c:f:o:hvV");
+
+# display version
+if (isset($options["V"]))
+{
+ display_version();
+ exit;
+}
+
+# check converter
+if (isset($options["c"]) && !array_key_exists($options["c"], $GLOBALS["converters"]))
+{
+ echo "Error: Selected converter isn't available!\n";
+ echo "Try " . $_SERVER["argv"][0] . " -h for avilable converters.\n";
+ exit(1);
+}
+
+# display usage
+if (isset($options["h"]))
+{
+ display_usage(isset($options["c"]) ? $options["c"] : false);
+ exit;
+}
+
+# check required arguments
+if (!isset($options["c"]) || !isset($options["f"]) || !isset($options["o"]))
+{
+ echo "Error: Missing command line arguments!\n";
+ echo "Try " . $_SERVER["argv"][0] . " -h for more information.\n";
+ exit(1);
+}
+
+# create converter object
+$converter = new $GLOBALS["converters"][$options["c"]];
+$converter->setVerbose(isset($options["v"]));
+if ($converter->isError())
+{
+ echo $converter->getErrorMessage();
+ exit(1);
+}
+
+# parse file
+$converter->parseFile($options["f"]);
+if ($converter->isError())
+{
+ echo $converter->getErrorMessage();
+ exit(1);
+}
+
+# write xml file
+if (($fh = fopen($options["o"], "w")) === false)
+{
+ echo "Error: Couldn't open file for writting!\n";
+ echo "Maybe you don't have permission to write to " . $tmpfile . "\n";
+ exit(1);
+}
+fwrite($fh, $converter->getXML());
+fclose($fh);
+
+/*
+ * Local variables:
+ * tab-width: 2
+ * c-basic-offset: 2
+ * End:
+ * vim600: et sw=2 ts=2 fdm=marker
+ * vim<600: et sw=2 ts=2
+ */
+?>
diff -Naur htscanner-cvs/TODO htscanner/TODO
--- htscanner-cvs/TODO 2007-07-25 16:04:24.000000000 +0200
+++ htscanner/TODO 2007-09-04 23:09:26.000000000 +0200
@@ -1,38 +1,6 @@
|----------------------------------------------------------------------|
TODO:
-
-Support for all ini options
-Requires either a merge or change the order of the init sections. As it
-is technically possible, it requires some work on other extensions (like
-session) and will break binary compatibility.
-NB: it will be usefull for unicode to set the default enconding
-NB#2: __NOT__ for the crappy flag
-
-
-* ISP options
-
-Add virtual host like options:
-
- * a config file to define the vhost options for a given path (virtual
- host)
- ex: open_basedir, safemode, tmp dir or disable_functions
-
- * list of allowed (or not allowed) options, like disable_functions but
- for the ini settings.
-
-These options are read and permanently cached at INIT (aka MINIT or
-sapi init) time and cached. What would rock is to have a
-`kill -HUP`-like to reload it without having to restart the server.
-
-A possible solution could be to use a single file (same dir as ini_dir):
-php.vhost.ini:
-[/path/a/b/c]
-option1 "abc"
-
-[/path/a/b/d]
-option1 "abcd"
-
-[/path/a/b/e]
-option1 "abcde"
+- ttl for xml cache? - really necessary?
+- check PHP6 Support