diff options
Diffstat (limited to 'data/lighttpd/lighttpd-1.4.53/src')
180 files changed, 79759 insertions, 0 deletions
diff --git a/data/lighttpd/lighttpd-1.4.53/src/CMakeLists.txt b/data/lighttpd/lighttpd-1.4.53/src/CMakeLists.txt new file mode 100644 index 000000000..db5f8a5ad --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/CMakeLists.txt @@ -0,0 +1,1080 @@ +include(CheckCSourceCompiles) +include(CheckIncludeFiles) +include(CheckFunctionExists) +include(CheckSymbolExists) +include(CheckVariableExists) +include(CheckTypeSize) +include(CheckLibraryExists) +include(CMakeDetermineCCompiler) +include(FindThreads) +include(FindPkgConfig) + +include(LighttpdMacros) + +add_definitions(-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGE_FILES) + +option(WITH_XATTR "with xattr-support for the stat-cache [default: off]") +option(WITH_MYSQL "with mysql-support for mod_vhostdb_mysql [default: off]") +option(WITH_PGSQL "with postgres-support for mod_vhostdb_pgsql [default: off]") +option(WITH_DBI "with dbi-support for mod_vhostdb_dbi [default: off]") +option(WITH_OPENSSL "with openssl-support [default: off]") +option(WITH_WOLFSSL "with wolfSSL-support [default: off]") +option(WITH_PCRE "with regex support [default: on]" ON) +option(WITH_WEBDAV_PROPS "with property-support for mod_webdav [default: off]") +option(WITH_WEBDAV_LOCKS "locks in webdav [default: off]") +option(WITH_BZIP "with bzip2-support for mod_compress [default: off]") +option(WITH_ZLIB "with deflate-support for mod_compress [default: on]" ON) +option(WITH_KRB5 "with Kerberos5-support for mod_auth [default: off]") +option(WITH_LDAP "with LDAP-support for mod_auth mod_vhostdb_ldap [default: off]") +option(WITH_PAM "with PAM-support for mod_auth [default: off]") +option(WITH_LUA "with lua 5.1 for mod_magnet [default: off]") +# option(WITH_VALGRIND "with internal support for valgrind [default: off]") +option(WITH_FAM "fam/gamin for reducing number of stat() calls [default: off]") +option(WITH_GDBM "gdbm storage for mod_trigger_b4_dl [default: off]") +option(WITH_MEMCACHED "memcached storage for mod_trigger_b4_dl [default: off]") +option(WITH_LIBEV "libev support for fdevent handlers [default: off]") +option(WITH_LIBUNWIND "with libunwind to print backtraces in asserts [default: off]") +option(WITH_GEOIP "with GeoIP-support mod_geoip [default: off]") +option(WITH_SASL "with SASL-support for mod_authn_sasl [default: off]") + +if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") + option(BUILD_EXTRA_WARNINGS "extra warnings") + + if(BUILD_EXTRA_WARNINGS) + set(WARN_CFLAGS "-g -g2 -Wall -Wmissing-declarations -Wdeclaration-after-statement -Wcast-align -Wsign-compare -Wnested-externs -Wpointer-arith -D_FORTIFY_SOURCE=2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security") + set(WARN_LDFLAGS "-Wl,--as-needed") + # -Werror -Wbad-function-cast -Wmissing-prototypes + endif() +endif() + +option(BUILD_STATIC "build a static lighttpd with all modules added") + +if(BUILD_STATIC) + set(LIGHTTPD_STATIC 1) +elseif(APPLE) + set(CMAKE_SHARED_MODULE_PREFIX "") +else() + set(CMAKE_SHARED_LIBRARY_PREFIX "") +endif() + +if(WITH_LIBEV) + find_package(LibEV REQUIRED) + set(HAVE_LIBEV 1) +endif() + +if(WITH_LIBUNWIND) + pkg_check_modules(LIBUNWIND REQUIRED libunwind) + set(HAVE_LIBUNWIND 1) +endif() + +if(WITH_WEBDAV_PROPS) + set(WITH_XML 1) + set(WITH_SQLITE3 1) +endif() + +if(WITH_WEBDAV_LOCKS) + set(WITH_UUID 1) +endif() + +check_include_files(sys/devpoll.h HAVE_SYS_DEVPOLL_H) +check_include_files(sys/epoll.h HAVE_SYS_EPOLL_H) +check_include_files(sys/event.h HAVE_SYS_EVENT_H) +check_include_files(sys/mman.h HAVE_SYS_MMAN_H) +check_include_files(sys/poll.h HAVE_SYS_POLL_H) +check_include_files(sys/port.h HAVE_SYS_PORT_H) +check_include_files(sys/prctl.h HAVE_SYS_PRCTL_H) +check_include_files(sys/resource.h HAVE_SYS_RESOURCE_H) +check_include_files(sys/sendfile.h HAVE_SYS_SENDFILE_H) +check_include_files(sys/select.h HAVE_SYS_SELECT_H) +check_include_files(sys/types.h HAVE_SYS_TYPES_H) +check_include_files(sys/uio.h HAVE_SYS_UIO_H) +check_include_files(sys/un.h HAVE_SYS_UN_H) +check_include_files(sys/wait.h HAVE_SYS_WAIT_H) +check_include_files(sys/time.h HAVE_SYS_TIME_H) +check_include_files(unistd.h HAVE_UNISTD_H) +check_include_files(pthread.h HAVE_PTHREAD_H) +check_include_files(getopt.h HAVE_GETOPT_H) +check_include_files(inttypes.h HAVE_INTTYPES_H) +check_include_files(poll.h HAVE_POLL_H) +check_include_files(pwd.h HAVE_PWD_H) +check_include_files(stddef.h HAVE_STDDEF_H) +check_include_files(stdint.h HAVE_STDINT_H) +check_include_files(strings.h HAVE_STRINGS_H) +check_include_files(syslog.h HAVE_SYSLOG_H) + +# check for fastcgi lib, for the tests only +check_include_files(fastcgi.h HAVE_FASTCGI_H) +check_include_files(fastcgi/fastcgi.h HAVE_FASTCGI_FASTCGI_H) + +# will be needed for auth +check_include_files(crypt.h HAVE_CRYPT_H) +# check if we need libcrypt for crypt_r() +check_library_exists(crypt crypt_r "" HAVE_LIBCRYPT_CRYPT_R) +if(HAVE_LIBCRYPT_CRYPT_R) + set(HAVE_CRYPT_R 1) + set(HAVE_LIBCRYPT 1) +else() + check_library_exists(crypt crypt "" HAVE_LIBCRYPT) +endif() +check_function_exists(crypt_r HAVE_CRYPT_R) +check_function_exists(crypt HAVE_CRYPT) + +check_include_files(sys/inotify.h HAVE_SYS_INOTIFY_H) +if(HAVE_SYS_INOTIFY_H) + check_function_exists(inotify_init HAVE_INOTIFY_INIT) +endif() + +set(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h) +check_type_size(socklen_t HAVE_SOCKLEN_T) +set(CMAKE_EXTRA_INCLUDE_FILES) + +check_include_files(sys/random.h HAVE_SYS_RANDOM_H) +set(CMAKE_EXTRA_INCLUDE_FILES sys/random.h) +check_function_exists(getentropy HAVE_GETENTROPY) +set(CMAKE_EXTRA_INCLUDE_FILES) + +check_include_files(linux/random.h HAVE_LINUX_RANDOM_H) +set(CMAKE_EXTRA_INCLUDE_FILES linux/random.h) +check_function_exists(getrandom HAVE_GETRANDOM) +set(CMAKE_EXTRA_INCLUDE_FILES) + +check_type_size(long SIZEOF_LONG) +check_type_size(off_t SIZEOF_OFF_T) + +check_function_exists(arc4random_buf HAVE_ARC4RANDOM_BUF) +check_function_exists(chroot HAVE_CHROOT) +check_function_exists(epoll_ctl HAVE_EPOLL_CTL) +check_function_exists(fork HAVE_FORK) +check_function_exists(getloadavg HAVE_GETLOADAVG) +check_function_exists(getrlimit HAVE_GETRLIMIT) +check_function_exists(getuid HAVE_GETUID) +check_function_exists(gmtime_r HAVE_GMTIME_R) +check_function_exists(inet_ntop HAVE_INET_NTOP) +check_function_exists(jrand48 HAVE_JRAND48) +check_function_exists(kqueue HAVE_KQUEUE) +check_function_exists(localtime_r HAVE_LOCALTIME_R) +check_function_exists(lstat HAVE_LSTAT) +check_function_exists(madvise HAVE_MADVISE) +check_function_exists(memcpy HAVE_MEMCPY) +check_function_exists(memset HAVE_MEMSET) +check_function_exists(mmap HAVE_MMAP) +check_function_exists(pathconf HAVE_PATHCONF) +check_function_exists(pipe2 HAVE_PIPE2) +check_function_exists(poll HAVE_POLL) +check_function_exists(port_create HAVE_PORT_CREATE) +check_function_exists(prctl HAVE_PRCTL) +check_function_exists(pread HAVE_PREAD) +check_function_exists(posix_fadvise HAVE_POSIX_FADVISE) +check_function_exists(select HAVE_SELECT) +check_function_exists(sendfile HAVE_SENDFILE) +check_function_exists(send_file HAVE_SEND_FILE) +check_function_exists(sendfile64 HAVE_SENDFILE64) +check_function_exists(sendfilev HAVE_SENDFILEV) +check_function_exists(sigaction HAVE_SIGACTION) +check_function_exists(signal HAVE_SIGNAL) +check_function_exists(sigtimedwait HAVE_SIGTIMEDWAIT) +check_function_exists(srandom HAVE_SRANDOM) +check_function_exists(strptime HAVE_STRPTIME) +check_function_exists(syslog HAVE_SYSLOG) +check_function_exists(writev HAVE_WRITEV) +check_function_exists(inet_aton HAVE_INET_ATON) +check_function_exists(issetugid HAVE_ISSETUGID) +check_function_exists(inet_pton HAVE_INET_PTON) +check_function_exists(memset_s HAVE_MEMSET_S) +check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO) +check_symbol_exists(clock_gettime "time.h" HAVE_CLOCK_GETTIME) +if (NOT HAVE_CLOCK_GETTIME) + check_library_exists(rt clock_gettime "time.h" HAVE_CLOCK_GETTIME) +endif() +check_c_source_compiles(" + #include <sys/types.h> + #include <sys/socket.h> + #include <netinet/in.h> + + int main() { + struct sockaddr_in6 s; struct in6_addr t=in6addr_any; int i=AF_INET6; s; t.s6_addr[0] = 0; + return 0; + }" HAVE_IPV6) +check_c_source_compiles(" + __attribute__((weak)) void __dummy(void *x) { } + int main() { + void *x; + __dummy(x); + } + " HAVE_WEAK_SYMBOLS) +check_c_source_compiles(" + #include <time.h> + int main(void) { + struct tm t; + t.tm_gmtoff = 0; + return 0; + } + " HAVE_STRUCT_TM_GMTOFF) + +## refactor me +macro(XCONFIG _package _include_DIR _link_DIR _link_FLAGS _cflags) +# reset the variables at the beginning + set(${_include_DIR}) + set(${_link_DIR}) + set(${_link_FLAGS}) + set(${_cflags}) + + find_program(${_package}CONFIG_EXECUTABLE NAMES ${_package} PATHS /usr/local/bin ) + + # if pkg-config has been found + if(${_package}CONFIG_EXECUTABLE) + set(XCONFIG_EXECUTABLE "${${_package}CONFIG_EXECUTABLE}") + message(STATUS "found ${_package}: ${XCONFIG_EXECUTABLE}") + + exec_program(${XCONFIG_EXECUTABLE} ARGS --libs OUTPUT_VARIABLE __link_FLAGS) + string(REPLACE "\n" "" ${_link_FLAGS} ${__link_FLAGS}) + exec_program(${XCONFIG_EXECUTABLE} ARGS --cflags OUTPUT_VARIABLE __cflags) + string(REPLACE "\n" "" ${_cflags} ${__cflags}) + else() + message(STATUS "found ${_package}: no") + endif() +endmacro(XCONFIG _package _include_DIR _link_DIR _link_FLAGS _cflags) + +if(WITH_XATTR) + check_include_files("sys/types.h;attr/attributes.h" HAVE_ATTR_ATTRIBUTES_H) + if(HAVE_ATTR_ATTRIBUTES_H) + check_library_exists(attr attr_get "" HAVE_XATTR) + endif() +else() + unset(HAVE_ATTR_ATTRIBUTES_H) + unset(HAVE_XATTR) +endif() + +if(WITH_MYSQL) + xconfig(mysql_config MYSQL_INCDIR MYSQL_LIBDIR MYSQL_LDFLAGS MYSQL_CFLAGS) + + set(CMAKE_REQUIRED_INCLUDES /usr/include/mysql) + check_include_files(mysql.h HAVE_MYSQL_H) + set(CMAKE_REQUIRED_INCLUDES) + if(HAVE_MYSQL_H) + check_library_exists(mysqlclient mysql_real_connect "" HAVE_MYSQL) + endif() +else() + unset(HAVE_MYSQL_H) + unset(HAVE_MYSQL) +endif() + +if(WITH_PGSQL) + xconfig(pg_config PGSQL_INCDIR PGSQL_LIBDIR PGSQL_LDFLAGS PGSQL_CFLAGS) + + check_include_files(libpq-fe.h HAVE_PGSQL_H) + if(HAVE_PGSQL_H) + check_library_exists(pq PQsetdbLogin "" HAVE_PGSQL) + endif() +else() + unset(HAVE_PGSQL_H) + unset(HAVE_PGSQL) +endif() + +if(WITH_DBI) + check_include_files(dbi/dbi.h HAVE_DBI_H) + if(HAVE_DBI_H) + check_library_exists(dbi dbi_conn_connect "" HAVE_DBI) + endif() +else() + unset(HAVE_DBI_H) + unset(HAVE_DBI) +endif() + +set(CRYPTO_LIBRARY "") + +if(WITH_OPENSSL) + if(APPLE) + set(CMAKE_REQUIRED_INCLUDES /opt/local/include) + endif() + check_include_files(openssl/ssl.h HAVE_OPENSSL_SSL_H) + if(APPLE) + set(CMAKE_REQUIRED_INCLUDES) + endif() + if(HAVE_OPENSSL_SSL_H) + check_library_exists(crypto BIO_f_base64 "" HAVE_LIBCRYPTO) + if(HAVE_LIBCRYPTO) + set(CRYPTO_LIBRARY crypto) + check_library_exists(ssl SSL_new "" HAVE_LIBSSL) + endif() + endif() +else() + unset(HAVE_OPENSSL_SSL_H) + unset(HAVE_LIBSSL) +endif() + +if(WITH_WOLFSSL) + if(NOT ${WITH_WOLFSSL} EQUAL "") + find_path(WOLFSSL_INCLUDE_DIR wolfssl/ssl.h ${WITH_WOLFSSL}/include) + elseif(APPLE) + find_path(WOLFSSL_INCLUDE_DIR wolfssl/ssl.h /opt/local/include) + else() + find_path(WOLFSSL_INCLUDE_DIR wolfssl/ssl.h /usr/local/include) + endif() + + if(NOT ${WITH_WOLFSSL} EQUAL "") + find_library(WOLFSSL_LIBRARY + NAMES wolfssl + PATHS ${WITH_WOLFSSL}/lib + ) + else() + find_library(WOLFSSL_LIBRARY + NAMES wolfssl + PATHS /usr/local/lib /opt/local/lib /usr/lib + ) + endif() + + if(WOLFSSL_INCLUDE_DIR AND WOLFSSL_LIBRARY) + set(CMAKE_REQUIRED_INCLUDES ${WOLFSSL_INCLUDE_DIR}) + check_include_files(wolfssl/ssl.h HAVE_WOLFSSL_SSL_H) + + check_c_source_compiles(" + #include <wolfssl/options.h> + #if !defined(HAVE_LIGHTY) && !defined(OPENSSL_ALL) + #error HAVE_LIGHTY macro not defined + #endif + int main() { return 0; } + " CHECK_HAVE_LIGHTY) + if (NOT CHECK_HAVE_LIGHTY) + message(FATAL_ERROR "wolfssl must be built with ./configure --enable-lighty") + endif() + unset(CHECK_HAVE_LIGHTY) + + set(CMAKE_REQUIRED_LIBRARIES ${WOLFSSL_LIBRARY}) + check_library_exists(${WOLFSSL_LIBRARY} wolfSSL_Init "" HAVE_LIBCRYPTO) + if(HAVE_LIBCRYPTO) + set(CRYPTO_LIBRARY ${WOLFSSL_LIBRARY}) + add_definitions(-DHAVE_WOLFSSL_SSL_H) + endif() + set(CMAKE_REQUIRED_INCLUDES) + set(CMAKE_REQUIRED_LIBRARIES) + include_directories(${WOLFSSL_INCLUDE_DIR} ${WOLFSSL_INCLUDE_DIR}/wolfssl) + endif() + unset(WOLFSSL_LIBRARY) + unset(WOLFSSL_INCLUDE_DIR) +else() + unset(HAVE_WOLFSSL_SSL_H) +endif() + +if(WITH_OPENSSL AND WITH_WOLFSSL) + message(FATAL_ERROR "lighttpd should not be built with both --with-openssl and --with-wolfssl") +endif() + +if(WITH_PCRE) + ## if we have pcre-config, use it + xconfig(pcre-config PCRE_INCDIR PCRE_LIBDIR PCRE_LDFLAGS PCRE_CFLAGS) + if(PCRE_LDFLAGS OR PCRE_CFLAGS) + message(STATUS "found pcre at: LDFLAGS: ${PCRE_LDFLAGS} CFLAGS: ${PCRE_CFLAGS}") + + if(NOT PCRE_CFLAGS STREQUAL "\n") + ## if it is empty we'll get newline returned + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${PCRE_CFLAGS}") + endif() + + set(HAVE_PCRE_H 1) + set(HAVE_LIBPCRE 1) + else() + if(NOT WIN32) + check_include_files(pcre.h HAVE_PCRE_H) + check_library_exists(pcre pcre_exec "" HAVE_LIBPCRE) + set(PCRE_LDFLAGS -lpcre) + else() + find_path(PCRE_INCLUDE_DIR pcre.h + /usr/local/include + /usr/include + ) + + set(PCRE_NAMES pcre) + find_library(PCRE_LIBRARY + NAMES ${PCRE_NAMES} + PATHS /usr/lib /usr/local/lib + ) + + if(PCRE_INCLUDE_DIR AND PCRE_LIBRARY) + set(CMAKE_REQUIRED_INCLUDES ${PCRE_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${PCRE_LIBRARY}) + check_include_files(pcre.h HAVE_PCRE_H) + check_library_exists(pcre pcre_exec "" HAVE_LIBPCRE) + set(CMAKE_REQUIRED_INCLUDES) + set(CMAKE_REQUIRED_LIBRARIES) + include_directories(${PCRE_INCLUDE_DIR}) + endif() + endif() + endif() + + if(NOT HAVE_PCRE_H) + message(FATAL_ERROR "pcre.h couldn't be found") + endif() + if(NOT HAVE_LIBPCRE) + message(FATAL_ERROR "libpcre couldn't be found") + endif() +else() + unset(HAVE_PCRE_H) + unset(HAVE_LIBPCRE) +endif() + +if(WITH_SASL) + check_include_files(sasl/sasl.h HAVE_SASL_SASL_H) + if(HAVE_SASL_SASL_H) + check_library_exists(sasl2 sasl_server_init "" HAVE_SASL) + endif() +else() + unset(HAVE_SASL_SASL_H) + unset(HAVE_SASL) +endif() + + +if(WITH_XML) + xconfig(xml2-config XML2_INCDIR XML2_LIBDIR XML2_LDFLAGS XML2_CFLAGS) + if(XML2_LDFLAGS OR XML2_CFLAGS) + message(STATUS "found xml2 at: LDFLAGS: ${XML2_LDFLAGS} CFLAGS: ${XML2_CFLAGS}") + + ## if it is empty we'll get newline returned + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${XML2_CFLAGS}") + + check_include_files(libxml/tree.h HAVE_LIBXML_H) + + set(CMAKE_REQUIRED_FLAGS ${XML2_LDFLAGS}) + check_library_exists(xml2 xmlParseChunk "" HAVE_LIBXML) + set(CMAKE_REQUIRED_FLAGS) + else() + check_include_files(libxml.h HAVE_LIBXML_H) + check_library_exists(xml2 xmlParseChunk "" HAVE_LIBXML) + endif() + + if(NOT HAVE_LIBXML_H) + message(FATAL_ERROR "libxml/tree.h couldn't be found") + endif() + if(NOT HAVE_LIBXML) + message(FATAL_ERROR "libxml2 couldn't be found") + endif() +else() + unset(HAVE_LIBXML_H) + unset(HAVE_LIBXML) +endif() + +if(WITH_SQLITE3) + check_include_files(sqlite3.h HAVE_SQLITE3_H) + check_library_exists(sqlite3 sqlite3_reset "" HAVE_SQLITE3) +else() + unset(HAVE_SQLITE3_H) + unset(HAVE_SQLITE3) +endif() + +if(WITH_UUID) + check_include_files(uuid/uuid.h HAVE_UUID_UUID_H) + check_library_exists(uuid uuid_generate "" NEED_LIBUUID) + if(NOT NEED_LIBUUID) + check_function_exists(uuid_generate HAVE_LIBUUID) + else() + set(HAVE_LIBUUID 1) + endif() +else() + unset(HAVE_UUID_UUID_H) + unset(NEED_LIBUUID) + unset(HAVE_LIBUUID) +endif() + +if(WITH_ZLIB) + if(NOT WIN32) + check_include_files(zlib.h HAVE_ZLIB_H) + check_library_exists(z deflate "" HAVE_LIBZ) + set(ZLIB_LIBRARY z) + else() + find_path(ZLIB_INCLUDE_DIR zlib.h + /usr/local/include + /usr/include + ) + + set(ZLIB_NAMES z zlib zdll) + find_library(ZLIB_LIBRARY + NAMES ${ZLIB_NAMES} + PATHS /usr/lib /usr/local/lib + ) + + if(ZLIB_INCLUDE_DIR AND ZLIB_LIBRARY) + set(CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${ZLIB_LIBRARY}) + get_filename_component(ZLIB_NAME ${ZLIB_LIBRARY} NAME) + check_include_files(zlib.h HAVE_ZLIB_H) + check_library_exists(${ZLIB_NAME} deflate "" HAVE_LIBZ) + set(CMAKE_REQUIRED_INCLUDES) + set(CMAKE_REQUIRED_LIBRARIES) + include_directories(${ZLIB_INCLUDE_DIR}) + endif() + endif() +else() + unset(HAVE_ZLIB_H) + unset(HAVE_LIBZ) + unset(ZLIB_INCLUDE_DIR) + unset(ZLIB_LIBRARY) +endif() + +if(WITH_BZIP) + check_include_files(bzlib.h HAVE_BZLIB_H) + check_library_exists(bz2 BZ2_bzCompress "" HAVE_LIBBZ2) +else() + unset(HAVE_BZLIB_H) + unset(HAVE_LIBBZ2) +endif() + +if(WITH_LDAP) + check_include_files(ldap.h HAVE_LDAP_H) + check_library_exists(ldap ldap_bind "" HAVE_LIBLDAP) + check_include_files(lber.h HAVE_LBER_H) + check_library_exists(lber ber_printf "" HAVE_LIBLBER) +else() + unset(HAVE_LDAP_H) + unset(HAVE_LIBLDAP) + unset(HAVE_LBER_H) + unset(HAVE_LIBLBER) +endif() + +if(WITH_PAM) + check_include_files(security/pam_appl.h HAVE_SECURITY_PAM_APPL_H) + check_library_exists(pam pam_start "" HAVE_PAM) +else() + unset(HAVE_SECURITY_PAM_APPL_H) + unset(HAVE_PAM) +endif() + +if(WITH_LUA) + pkg_search_module(LUA REQUIRED lua5.3 lua-5.3 lua5.2 lua-5.2 lua5.1 lua-5.1 lua) + message(STATUS "found lua at: INCDIR: ${LUA_INCLUDE_DIRS} LIBDIR: ${LUA_LIBRARY_DIRS} LDFLAGS: ${LUA_LDFLAGS} CFLAGS: ${LUA_CFLAGS}") + set(HAVE_LIBLUA 1 "Have liblua") + set(HAVE_LUA_H 1 "Have liblua header") +else() + unset(HAVE_LIBLUA) + unset(HAVE_LUA_H) +endif() + +if(WITH_FAM) + check_include_files(fam.h HAVE_FAM_H) + check_library_exists(fam FAMOpen2 "" HAVE_LIBFAM) + if(HAVE_LIBFAM) + set(CMAKE_REQUIRED_LIBRARIES fam) + check_function_exists(FAMNoExists HAVE_FAMNOEXISTS) + endif() +else() + unset(HAVE_FAM_H) + unset(HAVE_LIBFAM) + unset(HAVE_FAMNOEXISTS) +endif() + +if(WITH_GDBM) + check_include_files(gdbm.h HAVE_GDBM_H) + check_library_exists(gdbm gdbm_open "" HAVE_GDBM) +else() + unset(HAVE_GDBM_H) + unset(HAVE_GDBM) +endif() + +if(WITH_MEMCACHED) + check_include_files(libmemcached/memcached.h HAVE_LIBMEMCACHED_MEMCACHED_H) + check_library_exists(memcached memcached "" HAVE_LIBMEMCACHED) + if(HAVE_LIBMEMCACHED_MEMCACHED_H AND HAVE_LIBMEMCACHED) + set(USE_MEMCACHED 1) + else() + message(FATAL_ERROR "didn't find libmemcached") + endif() +endif() + +if(WITH_GEOIP) + check_library_exists(geoip GeoIP_country_name_by_addr "" HAVE_GEOIP) +endif() + +if(NOT BUILD_STATIC) + check_include_files(dlfcn.h HAVE_DLFCN_H) +else() + unset(HAVE_DLFCN_H) +endif() + +if(HAVE_DLFCN_H) + check_library_exists(dl dlopen "" HAVE_LIBDL) +else() + unset(HAVE_LIBDL) +endif() + +set(LIGHTTPD_VERSION_ID 10400) +set(PACKAGE_NAME "${CMAKE_PROJECT_NAME}") +set(PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}") + +if(NOT SBINDIR) + set(SBINDIR "sbin") +endif() + +if(NOT LIGHTTPD_MODULES_DIR) + set(LIGHTTPD_MODULES_DIR "lib${LIB_SUFFIX}/lighttpd") +endif() + +if(NOT WIN32) + set(LIGHTTPD_LIBRARY_DIR "${CMAKE_INSTALL_PREFIX}/${LIGHTTPD_MODULES_DIR}") +else() + ## We use relative path in windows + set(LIGHTTPD_LIBRARY_DIR "lib") +endif() + +## Write out config.h +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +add_definitions(-DHAVE_CONFIG_H) + +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) + +set(COMMON_SRC + base64.c buffer.c burl.c log.c + http_header.c http_kv.c keyvalue.c chunk.c + http_chunk.c stream.c fdevent.c gw_backend.c + stat_cache.c plugin.c joblist.c etag.c array.c + data_string.c data_array.c + data_integer.c algo_sha1.c md5.c + vector.c + fdevent_select.c fdevent_libev.c + fdevent_poll.c fdevent_linux_sysepoll.c + fdevent_solaris_devpoll.c fdevent_solaris_port.c + fdevent_freebsd_kqueue.c + data_config.c + crc32.c + connections-glue.c + configfile-glue.c + http-header-glue.c + http_auth.c + http_vhostdb.c + request.c + sock_addr.c + splaytree.c + rand.c + safe_memclear.c +) + +if(WIN32) + message(STATUS "Adding local getopt implementation.") + set(COMMON_SRC ${COMMON_SRC} xgetopt.c) +endif() + +add_executable(lemon lemon.c) + +## Build parsers by using lemon... +lemon_parser(configparser.y) +lemon_parser(mod_ssi_exprparser.y) + +set(L_INSTALL_TARGETS) + +add_executable(lighttpd-angel lighttpd-angel.c) +set(L_INSTALL_TARGETS ${L_INSTALL_TARGETS} lighttpd-angel) +add_target_properties(lighttpd-angel COMPILE_FLAGS "-DSBIN_DIR=\\\\\"${CMAKE_INSTALL_PREFIX}/${SBINDIR}\\\\\"") + +add_executable(lighttpd + server.c + response.c + connections.c + inet_ntop_cache.c + network.c + network_write.c + configfile.c + configparser.c + ${COMMON_SRC} +) +set(L_INSTALL_TARGETS ${L_INSTALL_TARGETS} lighttpd) + +add_and_install_library(mod_access mod_access.c) +add_and_install_library(mod_accesslog mod_accesslog.c) +add_and_install_library(mod_alias mod_alias.c) +add_and_install_library(mod_auth "mod_auth.c") +add_and_install_library(mod_authn_file "mod_authn_file.c") +if(NOT WIN32) + add_and_install_library(mod_cgi mod_cgi.c) +endif() +add_and_install_library(mod_compress mod_compress.c) +add_and_install_library(mod_deflate mod_deflate.c) +add_and_install_library(mod_dirlisting mod_dirlisting.c) +add_and_install_library(mod_evasive mod_evasive.c) +add_and_install_library(mod_evhost mod_evhost.c) +add_and_install_library(mod_expire mod_expire.c) +add_and_install_library(mod_extforward mod_extforward.c) +add_and_install_library(mod_fastcgi mod_fastcgi.c) +add_and_install_library(mod_flv_streaming mod_flv_streaming.c) +add_and_install_library(mod_indexfile mod_indexfile.c) +add_and_install_library(mod_proxy mod_proxy.c) +add_and_install_library(mod_redirect mod_redirect.c) +add_and_install_library(mod_rewrite mod_rewrite.c) +add_and_install_library(mod_rrdtool mod_rrdtool.c) +add_and_install_library(mod_scgi mod_scgi.c) +add_and_install_library(mod_secdownload mod_secdownload.c) +add_and_install_library(mod_setenv mod_setenv.c) +add_and_install_library(mod_simple_vhost mod_simple_vhost.c) +add_and_install_library(mod_sockproxy mod_sockproxy.c) +add_and_install_library(mod_ssi "mod_ssi_exprparser.c;mod_ssi_expr.c;mod_ssi.c") +add_and_install_library(mod_staticfile mod_staticfile.c) +add_and_install_library(mod_status mod_status.c) +add_and_install_library(mod_uploadprogress mod_uploadprogress.c) +add_and_install_library(mod_userdir mod_userdir.c) +add_and_install_library(mod_usertrack mod_usertrack.c) +add_and_install_library(mod_vhostdb mod_vhostdb.c) +add_and_install_library(mod_webdav mod_webdav.c) +add_and_install_library(mod_wstunnel mod_wstunnel.c) + +add_executable(test_array + t/test_array.c + array.c + data_array.c + data_integer.c + data_string.c + buffer.c +) +add_test(NAME test_array COMMAND test_array) + +add_executable(test_buffer + t/test_buffer.c + buffer.c +) +add_test(NAME test_buffer COMMAND test_buffer) + +add_executable(test_burl + t/test_burl.c + burl.c + buffer.c + base64.c +) +add_test(NAME test_burl COMMAND test_burl) + +add_executable(test_base64 + t/test_base64.c + buffer.c + base64.c +) +add_test(NAME test_base64 COMMAND test_base64) + +add_executable(test_configfile + t/test_configfile.c + buffer.c + array.c + data_config.c + data_integer.c + data_string.c + http_header.c + http_kv.c + vector.c + log.c + sock_addr.c +) +add_test(NAME test_configfile COMMAND test_configfile) + +add_executable(test_keyvalue + t/test_keyvalue.c + burl.c + buffer.c + base64.c + array.c + data_integer.c + data_string.c + log.c +) +add_test(NAME test_keyvalue COMMAND test_keyvalue) + +add_executable(test_mod_access + t/test_mod_access.c + configfile-glue.c + buffer.c + array.c + data_config.c + data_integer.c + data_string.c + http_header.c + http_kv.c + vector.c + log.c + sock_addr.c +) +add_test(NAME test_mod_access COMMAND test_mod_access) + +add_executable(test_mod_evhost + t/test_mod_evhost.c + configfile-glue.c + buffer.c + array.c + data_config.c + data_integer.c + data_string.c + http_header.c + http_kv.c + vector.c + log.c + sock_addr.c +) +add_test(NAME test_mod_evhost COMMAND test_mod_evhost) + +add_executable(test_mod_simple_vhost + t/test_mod_simple_vhost.c + configfile-glue.c + buffer.c + array.c + data_config.c + data_integer.c + data_string.c + http_header.c + http_kv.c + vector.c + log.c + sock_addr.c +) +add_test(NAME test_mod_simple_vhost COMMAND test_mod_simple_vhost) + +add_executable(test_request + t/test_request.c + request.c + buffer.c + array.c + data_integer.c + data_string.c + http_header.c + http_kv.c + log.c + sock_addr.c +) +add_test(NAME test_request COMMAND test_request) + +if(HAVE_PCRE_H) + target_link_libraries(lighttpd ${PCRE_LDFLAGS}) + add_target_properties(lighttpd COMPILE_FLAGS ${PCRE_CFLAGS}) + target_link_libraries(mod_rewrite ${PCRE_LDFLAGS}) + add_target_properties(mod_rewrite COMPILE_FLAGS ${PCRE_CFLAGS}) + target_link_libraries(mod_dirlisting ${PCRE_LDFLAGS}) + add_target_properties(mod_dirlisting COMPILE_FLAGS ${PCRE_CFLAGS}) + target_link_libraries(mod_redirect ${PCRE_LDFLAGS}) + add_target_properties(mod_redirect COMPILE_FLAGS ${PCRE_CFLAGS}) + target_link_libraries(test_configfile ${PCRE_LDFLAGS}) + add_target_properties(test_configfile COMPILE_FLAGS ${PCRE_CFLAGS}) + target_link_libraries(test_keyvalue ${PCRE_LDFLAGS}) + add_target_properties(test_keyvalue COMPILE_FLAGS ${PCRE_CFLAGS}) + target_link_libraries(test_mod_access ${PCRE_LDFLAGS}) + add_target_properties(test_mod_access COMPILE_FLAGS ${PCRE_CFLAGS}) + target_link_libraries(test_mod_evhost ${PCRE_LDFLAGS}) + add_target_properties(test_mod_evhost COMPILE_FLAGS ${PCRE_CFLAGS}) + target_link_libraries(test_mod_simple_vhost ${PCRE_LDFLAGS}) + add_target_properties(test_mod_simple_vhost COMPILE_FLAGS ${PCRE_CFLAGS}) +endif() + +if(WITH_PCRE AND (WITH_MEMCACHED OR WITH_GDBM)) + add_and_install_library(mod_trigger_b4_dl mod_trigger_b4_dl.c) + target_link_libraries(mod_trigger_b4_dl ${PCRE_LDFLAGS}) + add_target_properties(mod_trigger_b4_dl COMPILE_FLAGS ${PCRE_CFLAGS}) +endif() + +if(WITH_LUA) + add_and_install_library(mod_magnet "mod_magnet.c;mod_magnet_cache.c") + target_link_libraries(mod_magnet ${LUA_LDFLAGS}) + add_target_properties(mod_magnet COMPILE_FLAGS ${LUA_CFLAGS}) + + add_and_install_library(mod_cml "mod_cml.c;mod_cml_lua.c;mod_cml_funcs.c") + target_link_libraries(mod_cml ${LUA_LDFLAGS}) + add_target_properties(mod_cml COMPILE_FLAGS ${LUA_CFLAGS}) + if(WITH_MEMCACHED) + target_link_libraries(mod_cml memcached) + endif() +endif() + +if(WITH_GEOIP) + add_and_install_library(mod_geoip mod_geoip.c) + target_link_libraries(mod_geoip GeoIP) +endif() + +if(HAVE_MYSQL_H AND HAVE_MYSQL) + add_and_install_library(mod_mysql_vhost "mod_mysql_vhost.c") + target_link_libraries(mod_mysql_vhost mysqlclient) + add_and_install_library(mod_vhostdb_mysql "mod_vhostdb_mysql.c") + target_link_libraries(mod_vhostdb_mysql mysqlclient) + include_directories(/usr/include/mysql) + + add_and_install_library(mod_authn_mysql "mod_authn_mysql.c") + set(L_MOD_AUTHN_MYSQL) + if(HAVE_LIBCRYPT) + set(L_MOD_AUTHN_MYSQL ${L_MOD_AUTHN_MYSQL} crypt) + endif() + target_link_libraries(mod_authn_mysql ${L_MOD_AUTHN_MYSQL} mysqlclient) +endif() + +if(HAVE_PGSQL_H AND HAVE_PGSQL) + add_and_install_library(mod_vhostdb_pgsql "mod_vhostdb_pgsql.c") + target_link_libraries(mod_vhostdb_pgsql pq) +endif() + +if(HAVE_DBI_H AND HAVE_DBI) + add_and_install_library(mod_vhostdb_dbi "mod_vhostdb_dbi.c") + target_link_libraries(mod_vhostdb_dbi dbi) +endif() + +set(L_MOD_WEBDAV) +if(HAVE_SQLITE3_H) + set(L_MOD_WEBDAV ${L_MOD_WEBDAV} sqlite3) +endif() +if(HAVE_LIBXML_H) + target_link_libraries(mod_webdav ${XML2_LDFLAGS}) +endif() +if(HAVE_UUID_H) + if(NEED_LIBUUID) + set(L_MOD_WEBDAV ${L_MOD_WEBDAV} uuid) + endif() +endif() + +target_link_libraries(mod_webdav ${L_MOD_WEBDAV}) + +set(L_MOD_AUTHN_FILE) +if(HAVE_LIBCRYPT) + set(L_MOD_AUTHN_FILE ${L_MOD_AUTHN_FILE} crypt) +endif() +target_link_libraries(mod_authn_file ${L_MOD_AUTHN_FILE}) + +if(WITH_KRB5) + check_library_exists(krb5 krb5_init_context "" HAVE_KRB5) + add_and_install_library(mod_authn_gssapi "mod_authn_gssapi.c") + set(L_MOD_AUTHN_GSSAPI ${L_MOD_AUTHN_GSSAPI} krb5 gssapi_krb5) + target_link_libraries(mod_authn_gssapi ${L_MOD_AUTHN_GSSAPI}) +endif() + +if(WITH_LDAP) + set(L_MOD_AUTHN_LDAP ${L_MOD_AUTHN_LDAP} ldap lber) + add_and_install_library(mod_authn_ldap "mod_authn_ldap.c") + target_link_libraries(mod_authn_ldap ${L_MOD_AUTHN_LDAP}) + add_and_install_library(mod_vhostdb_ldap "mod_vhostdb_ldap.c") + target_link_libraries(mod_vhostdb_ldap ${L_MOD_AUTHN_LDAP}) +endif() + +if(WITH_PAM) + add_and_install_library(mod_authn_pam "mod_authn_pam.c") + set(L_MOD_AUTHN_PAM ${L_MOD_AUTHN_PAM} pam) + target_link_libraries(mod_authn_pam ${L_MOD_AUTHN_PAM}) +endif() + +if(WITH_SASL) + add_and_install_library(mod_authn_sasl "mod_authn_sasl.c") + set(L_MOD_AUTHN_SASL ${L_MOD_AUTHN_SASL} sasl2) + target_link_libraries(mod_authn_sasl ${L_MOD_AUTHN_SASL}) +endif() + +if(HAVE_ZLIB_H) + if(HAVE_BZLIB_H) + target_link_libraries(mod_compress ${ZLIB_LIBRARY} bz2) + target_link_libraries(mod_deflate ${ZLIB_LIBRARY} bz2) + else() + target_link_libraries(mod_compress ${ZLIB_LIBRARY}) + target_link_libraries(mod_deflate ${ZLIB_LIBRARY}) + endif() +endif() + +if(HAVE_LIBFAM) + target_link_libraries(lighttpd fam) +endif() + +if(HAVE_GDBM_H) + target_link_libraries(mod_trigger_b4_dl gdbm) +endif() + +if(WITH_MEMCACHED) + target_link_libraries(mod_trigger_b4_dl memcached) +endif() + +if(HAVE_XATTR) + target_link_libraries(lighttpd attr) +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -g -Wshadow -W -pedantic ${WARN_CFLAGS}") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_WITHDEBINFO} -O2") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WARN_LDFLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${WARN_LDFLAGS}") +endif() + +if((NOT APPLE) OR CMAKE_C_COMPILER_ID MATCHES "GNU") + add_target_properties(lighttpd LINK_FLAGS "-Wl,-export-dynamic") +endif() + +set_target_properties(lighttpd PROPERTIES CMAKE_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) + +if(WIN32) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNVALGRIND") + add_target_properties(lighttpd COMPILE_FLAGS "-DLI_DECLARE_EXPORTS") + target_link_libraries(lighttpd ws2_32) + target_link_libraries(mod_proxy ws2_32) + target_link_libraries(mod_fcgi ws2_32) + target_link_libraries(mod_scgi ws2_32) + target_link_libraries(mod_ssi ws2_32) + + if(MINGW) + target_link_libraries(lighttpd msvcr70) + add_target_properties(lighttpd LINK_FLAGS "-Wl,-subsystem,console") + endif() +endif() + +if(NOT BUILD_STATIC) + if(HAVE_LIBDL) + target_link_libraries(lighttpd dl) + endif() +endif() + +if(NOT ${CRYPTO_LIBRARY} EQUAL "") + if(NOT WITH_WOLFSSL) + target_link_libraries(lighttpd ssl) + endif() + target_link_libraries(lighttpd ${CRYPTO_LIBRARY}) + add_and_install_library(mod_openssl "mod_openssl.c") + if(NOT WITH_WOLFSSL) + set(L_MOD_OPENSSL ${L_MOD_OPENSSL} ssl) + endif() + set(L_MOD_OPENSSL ${L_MOD_OPENSSL} ${CRYPTO_LIBRARY}) + target_link_libraries(mod_openssl ${L_MOD_OPENSSL}) + set(L_MOD_AUTHN_FILE ${L_MOD_AUTHN_FILE} ${CRYPTO_LIBRARY}) + target_link_libraries(mod_authn_file ${L_MOD_AUTHN_FILE}) + target_link_libraries(mod_secdownload ${CRYPTO_LIBRARY}) + target_link_libraries(mod_wstunnel ${CRYPTO_LIBRARY}) +endif() + +if(WITH_LIBEV) + target_link_libraries(lighttpd ${LIBEV_LDFLAGS}) + add_target_properties(lighttpd COMPILE_FLAGS ${LIBEV_CFLAGS}) +endif() + +if(WITH_LIBUNWIND) + target_link_libraries(lighttpd ${LIBUNWIND_LDFLAGS}) + add_target_properties(lighttpd COMPILE_FLAGS ${LIBUNWIND_CFLAGS}) + + target_link_libraries(test_array ${LIBUNWIND_LDFLAGS}) + add_target_properties(test_array COMPILE_FLAGS ${LIBUNWIND_CFLAGS}) + target_link_libraries(test_buffer ${LIBUNWIND_LDFLAGS}) + add_target_properties(test_buffer COMPILE_FLAGS ${LIBUNWIND_CFLAGS}) + target_link_libraries(test_burl ${LIBUNWIND_LDFLAGS}) + add_target_properties(test_burl COMPILE_FLAGS ${LIBUNWIND_CFLAGS}) + target_link_libraries(test_base64 ${LIBUNWIND_LDFLAGS}) + add_target_properties(test_base64 COMPILE_FLAGS ${LIBUNWIND_CFLAGS}) + target_link_libraries(test_configfile ${PCRE_LDFLAGS} ${LIBUNWIND_LDFLAGS}) + add_target_properties(test_configfile COMPILE_FLAGS ${PCRE_CFLAGS} ${LIBUNWIND_CFLAGS}) + target_link_libraries(test_keyvalue ${PCRE_LDFLAGS} ${LIBUNWIND_LDFLAGS}) + add_target_properties(test_keyvalue COMPILE_FLAGS ${PCRE_CFLAGS} ${LIBUNWIND_CFLAGS}) + target_link_libraries(test_mod_access ${PCRE_LDFLAGS} ${LIBUNWIND_LDFLAGS}) + add_target_properties(test_mod_access COMPILE_FLAGS ${PCRE_CFLAGS} ${LIBUNWIND_CFLAGS}) + target_link_libraries(test_mod_evhost ${PCRE_LDFLAGS} ${LIBUNWIND_LDFLAGS}) + add_target_properties(test_mod_evhost COMPILE_FLAGS ${PCRE_CFLAGS} ${LIBUNWIND_CFLAGS}) + target_link_libraries(test_mod_simple_vhost ${PCRE_LDFLAGS} ${LIBUNWIND_LDFLAGS}) + add_target_properties(test_mod_simple_vhost COMPILE_FLAGS ${PCRE_CFLAGS} ${LIBUNWIND_CFLAGS}) + target_link_libraries(test_request ${LIBUNWIND_LDFLAGS}) + add_target_properties(test_request COMPILE_FLAGS ${LIBUNWIND_CFLAGS}) +endif() + +if(NOT WIN32) +install(TARGETS ${L_INSTALL_TARGETS} + RUNTIME DESTINATION ${SBINDIR} + LIBRARY DESTINATION ${LIGHTTPD_MODULES_DIR} + ARCHIVE DESTINATION ${LIGHTTPD_MODULES_DIR}/static) +else() +## HACK to make win32 to install our libraries in desired directory.. +install(TARGETS lighttpd + RUNTIME DESTINATION ${SBINDIR} + ARCHIVE DESTINATION lib/static) +list(REMOVE_ITEM L_INSTALL_TARGETS lighttpd) +install(TARGETS ${L_INSTALL_TARGETS} + RUNTIME DESTINATION ${SBINDIR}/lib + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static) +endif() diff --git a/data/lighttpd/lighttpd-1.4.53/src/Makefile.am b/data/lighttpd/lighttpd-1.4.53/src/Makefile.am new file mode 100644 index 000000000..80feb0af5 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/Makefile.am @@ -0,0 +1,586 @@ +AM_CFLAGS = $(FAM_CFLAGS) $(LIBUNWIND_CFLAGS) + +noinst_PROGRAMS=\ + t/test_array \ + t/test_buffer \ + t/test_burl \ + t/test_base64 \ + t/test_configfile \ + t/test_keyvalue \ + t/test_mod_access \ + t/test_mod_evhost \ + t/test_mod_simple_vhost \ + t/test_request + +sbin_PROGRAMS=lighttpd lighttpd-angel +LEMON=$(top_builddir)/src/lemon$(BUILD_EXEEXT) + +TESTS=\ + t/test_array$(EXEEXT) \ + t/test_buffer$(EXEEXT) \ + t/test_burl$(EXEEXT) \ + t/test_base64$(EXEEXT) \ + t/test_configfile$(EXEEXT) \ + t/test_keyvalue$(EXEEXT) \ + t/test_mod_access$(EXEEXT) \ + t/test_mod_evhost$(EXEEXT) \ + t/test_mod_simple_vhost$(EXEEXT) \ + t/test_request$(EXEEXT) + +lemon$(BUILD_EXEEXT): lemon.c + $(AM_V_CC)$(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) -o $@ $(srcdir)/lemon.c + +lighttpd_angel_SOURCES=lighttpd-angel.c + +.PHONY: versionstamp parsers + +versionstamp: + @test -f versionstamp.h || touch versionstamp.h; \ + REVISION=""; \ + if test -d "$(top_srcdir)/.svn" -a -x "`which svnversion`"; then \ + REVISION="$$(LANG= LC_ALL=C svnversion "$(top_srcdir)" 2>/dev/null || echo exported)"; \ + if test "$$REVISION" = "exported"; then \ + REVISION=""; \ + fi; \ + fi; \ + if test -z "$$REVISION" -a -d "$(top_srcdir)/.git" -a -x "`which git`"; then \ + REVISION="$$(cd "$(top_srcdir)"; LANG= LC_ALL=C git describe --always 2>/dev/null || echo)"; \ + fi; \ + if test -n "$$REVISION"; then \ + echo "#define REPO_VERSION \"-devel-$$REVISION\"" > versionstamp.h.tmp; \ + else \ + echo "#define REPO_VERSION \"\"" > versionstamp.h.tmp; \ + fi; \ + if ! diff versionstamp.h.tmp versionstamp.h >/dev/null 2>/dev/null; then \ + mv versionstamp.h.tmp versionstamp.h; \ + else \ + rm versionstamp.h.tmp; \ + fi + +configparser.h: configparser.c +configparser.c: $(srcdir)/configparser.y $(srcdir)/lempar.c lemon$(BUILD_EXEEXT) + rm -f configparser.h + $(LEMON) -q $(srcdir)/configparser.y $(srcdir)/lempar.c + +mod_ssi_exprparser.h: mod_ssi_exprparser.c +mod_ssi_exprparser.c: $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c lemon$(BUILD_EXEEXT) + rm -f mod_ssi_exprparser.h + $(LEMON) -q $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c + +parsers: configparser.c mod_ssi_exprparser.c + +BUILT_SOURCES = parsers versionstamp +MAINTAINERCLEANFILES = configparser.c configparser.h mod_ssi_exprparser.c mod_ssi_exprparser.h +CLEANFILES = versionstamp.h versionstamp.h.tmp lemon$(BUILD_EXEEXT) + +common_src=base64.c buffer.c burl.c log.c \ + http_header.c http_kv.c keyvalue.c chunk.c \ + http_chunk.c stream.c fdevent.c gw_backend.c \ + stat_cache.c plugin.c joblist.c etag.c array.c \ + data_string.c data_array.c \ + data_integer.c algo_sha1.c md5.c \ + vector.c \ + fdevent_select.c fdevent_libev.c \ + fdevent_poll.c fdevent_linux_sysepoll.c \ + fdevent_solaris_devpoll.c fdevent_solaris_port.c \ + fdevent_freebsd_kqueue.c \ + data_config.c \ + crc32.c \ + connections-glue.c \ + configfile-glue.c \ + http-header-glue.c \ + http_auth.c \ + http_vhostdb.c \ + rand.c \ + request.c \ + sock_addr.c \ + splaytree.c \ + safe_memclear.c + +src = server.c response.c connections.c \ + inet_ntop_cache.c \ + network.c \ + network_write.c \ + configfile.c configparser.c + +lib_LTLIBRARIES = + +if NO_RDYNAMIC +# if the linker doesn't allow referencing symbols of the binary +# we have to put everything into a shared-lib and link it into +# everything +common_ldflags = -avoid-version -no-undefined +lib_LTLIBRARIES += liblightcomp.la +liblightcomp_la_SOURCES=$(common_src) +liblightcomp_la_CFLAGS=$(AM_CFLAGS) $(LIBEV_CFLAGS) +liblightcomp_la_LDFLAGS = $(common_ldflags) +liblightcomp_la_LIBADD = $(PCRE_LIB) $(CRYPTO_LIB) $(FAM_LIBS) $(LIBEV_LIBS) $(ATTR_LIB) +common_libadd = liblightcomp.la +else +src += $(common_src) +common_ldflags = -avoid-version +common_libadd = +endif +common_module_ldflags = -module -export-dynamic $(common_ldflags) + +lib_LTLIBRARIES += mod_flv_streaming.la +mod_flv_streaming_la_SOURCES = mod_flv_streaming.c +mod_flv_streaming_la_LDFLAGS = $(common_module_ldflags) +mod_flv_streaming_la_LIBADD = $(common_libadd) + +if BUILD_WITH_GEOIP +lib_LTLIBRARIES += mod_geoip.la +mod_geoip_la_SOURCES = mod_geoip.c +mod_geoip_la_LDFLAGS = $(common_module_ldflags) +mod_geoip_la_LIBADD = $(common_libadd) $(GEOIP_LIB) +endif + +lib_LTLIBRARIES += mod_evasive.la +mod_evasive_la_SOURCES = mod_evasive.c +mod_evasive_la_LDFLAGS = $(common_module_ldflags) +mod_evasive_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_webdav.la +mod_webdav_la_SOURCES = mod_webdav.c +mod_webdav_la_CFLAGS = $(AM_CFLAGS) $(XML_CFLAGS) $(SQLITE_CFLAGS) +mod_webdav_la_LDFLAGS = $(common_module_ldflags) +mod_webdav_la_LIBADD = $(common_libadd) $(XML_LIBS) $(SQLITE_LIBS) $(UUID_LIBS) + +if BUILD_WITH_LUA +lib_LTLIBRARIES += mod_magnet.la +mod_magnet_la_SOURCES = mod_magnet.c mod_magnet_cache.c +mod_magnet_la_CFLAGS = $(AM_CFLAGS) $(LUA_CFLAGS) +mod_magnet_la_LDFLAGS = $(common_module_ldflags) +mod_magnet_la_LIBADD = $(common_libadd) $(LUA_LIBS) -lm +endif + +if BUILD_WITH_LUA +lib_LTLIBRARIES += mod_cml.la +mod_cml_la_SOURCES = mod_cml.c mod_cml_lua.c mod_cml_funcs.c +mod_cml_la_CFLAGS = $(AM_CFLAGS) $(LUA_CFLAGS) +mod_cml_la_LDFLAGS = $(common_module_ldflags) +mod_cml_la_LIBADD = $(MEMCACHED_LIB) $(common_libadd) $(LUA_LIBS) -lm +endif + +if BUILD_MOD_TRIGGER_B4_DL +lib_LTLIBRARIES += mod_trigger_b4_dl.la +mod_trigger_b4_dl_la_SOURCES = mod_trigger_b4_dl.c +mod_trigger_b4_dl_la_LDFLAGS = $(common_module_ldflags) +mod_trigger_b4_dl_la_LIBADD = $(GDBM_LIB) $(MEMCACHED_LIB) $(PCRE_LIB) $(common_libadd) +endif + +lib_LTLIBRARIES += mod_vhostdb.la +mod_vhostdb_la_SOURCES = mod_vhostdb.c +mod_vhostdb_la_LDFLAGS = $(common_module_ldflags) +mod_vhostdb_la_LIBADD = $(common_libadd) + +if BUILD_WITH_LDAP +lib_LTLIBRARIES += mod_vhostdb_ldap.la +mod_vhostdb_ldap_la_SOURCES = mod_vhostdb_ldap.c +mod_vhostdb_ldap_la_LDFLAGS = $(common_module_ldflags) +mod_vhostdb_ldap_la_LIBADD = $(LDAP_LIB) $(LBER_LIB) $(common_libadd) +endif + +if BUILD_WITH_MYSQL +lib_LTLIBRARIES += mod_mysql_vhost.la +mod_mysql_vhost_la_SOURCES = mod_mysql_vhost.c +mod_mysql_vhost_la_LDFLAGS = $(common_module_ldflags) +mod_mysql_vhost_la_LIBADD = $(MYSQL_LIBS) $(common_libadd) +mod_mysql_vhost_la_CPPFLAGS = $(MYSQL_CFLAGS) +endif + +if BUILD_WITH_MYSQL +lib_LTLIBRARIES += mod_vhostdb_mysql.la +mod_vhostdb_mysql_la_SOURCES = mod_vhostdb_mysql.c +mod_vhostdb_mysql_la_LDFLAGS = $(common_module_ldflags) +mod_vhostdb_mysql_la_LIBADD = $(MYSQL_LIBS) $(common_libadd) +mod_vhostdb_mysql_la_CPPFLAGS = $(MYSQL_CFLAGS) +endif + +if BUILD_WITH_PGSQL +lib_LTLIBRARIES += mod_vhostdb_pgsql.la +mod_vhostdb_pgsql_la_SOURCES = mod_vhostdb_pgsql.c +mod_vhostdb_pgsql_la_LDFLAGS = $(common_module_ldflags) +mod_vhostdb_pgsql_la_LIBADD = $(PGSQL_LIBS) $(common_libadd) +mod_vhostdb_pgsql_la_CPPFLAGS = $(PGSQL_INCLUDE) +endif + +if BUILD_WITH_DBI +lib_LTLIBRARIES += mod_vhostdb_dbi.la +mod_vhostdb_dbi_la_SOURCES = mod_vhostdb_dbi.c +mod_vhostdb_dbi_la_LDFLAGS = $(common_module_ldflags) +mod_vhostdb_dbi_la_LIBADD = $(DBI_LIBS) $(common_libadd) +mod_vhostdb_dbi_la_CPPFLAGS = $(DBI_CFLAGS) +endif + +lib_LTLIBRARIES += mod_cgi.la +mod_cgi_la_SOURCES = mod_cgi.c +mod_cgi_la_LDFLAGS = $(common_module_ldflags) +mod_cgi_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_scgi.la +mod_scgi_la_SOURCES = mod_scgi.c +mod_scgi_la_LDFLAGS = $(common_module_ldflags) +mod_scgi_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_staticfile.la +mod_staticfile_la_SOURCES = mod_staticfile.c +mod_staticfile_la_LDFLAGS = $(common_module_ldflags) +mod_staticfile_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_dirlisting.la +mod_dirlisting_la_SOURCES = mod_dirlisting.c +mod_dirlisting_la_LDFLAGS = $(common_module_ldflags) +mod_dirlisting_la_LIBADD = $(common_libadd) $(PCRE_LIB) + +lib_LTLIBRARIES += mod_indexfile.la +mod_indexfile_la_SOURCES = mod_indexfile.c +mod_indexfile_la_LDFLAGS = $(common_module_ldflags) +mod_indexfile_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_setenv.la +mod_setenv_la_SOURCES = mod_setenv.c +mod_setenv_la_LDFLAGS = $(common_module_ldflags) +mod_setenv_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_alias.la +mod_alias_la_SOURCES = mod_alias.c +mod_alias_la_LDFLAGS = $(common_module_ldflags) +mod_alias_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_userdir.la +mod_userdir_la_SOURCES = mod_userdir.c +mod_userdir_la_LDFLAGS = $(common_module_ldflags) +mod_userdir_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_rrdtool.la +mod_rrdtool_la_SOURCES = mod_rrdtool.c +mod_rrdtool_la_LDFLAGS = $(common_module_ldflags) +mod_rrdtool_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_usertrack.la +mod_usertrack_la_SOURCES = mod_usertrack.c +mod_usertrack_la_LDFLAGS = $(common_module_ldflags) +mod_usertrack_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_proxy.la +mod_proxy_la_SOURCES = mod_proxy.c +mod_proxy_la_LDFLAGS = $(common_module_ldflags) +mod_proxy_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_sockproxy.la +mod_sockproxy_la_SOURCES = mod_sockproxy.c +mod_sockproxy_la_LDFLAGS = $(common_module_ldflags) +mod_sockproxy_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_ssi.la +mod_ssi_la_SOURCES = mod_ssi_exprparser.c mod_ssi_expr.c mod_ssi.c +mod_ssi_la_LDFLAGS = $(common_module_ldflags) +mod_ssi_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_secdownload.la +mod_secdownload_la_SOURCES = mod_secdownload.c +mod_secdownload_la_LDFLAGS = $(common_module_ldflags) +mod_secdownload_la_LIBADD = $(common_libadd) $(CRYPTO_LIB) + +#lib_LTLIBRARIES += mod_httptls.la +#mod_httptls_la_SOURCES = mod_httptls.c +#mod_httptls_la_LDFLAGS = $(common_module_ldflags) +#mod_httptls_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_expire.la +mod_expire_la_SOURCES = mod_expire.c +mod_expire_la_LDFLAGS = $(common_module_ldflags) +mod_expire_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_evhost.la +mod_evhost_la_SOURCES = mod_evhost.c +mod_evhost_la_LDFLAGS = $(common_module_ldflags) +mod_evhost_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_simple_vhost.la +mod_simple_vhost_la_SOURCES = mod_simple_vhost.c +mod_simple_vhost_la_LDFLAGS = $(common_module_ldflags) +mod_simple_vhost_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_fastcgi.la +mod_fastcgi_la_SOURCES = mod_fastcgi.c +mod_fastcgi_la_LDFLAGS = $(common_module_ldflags) +mod_fastcgi_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_extforward.la +mod_extforward_la_SOURCES = mod_extforward.c +mod_extforward_la_LDFLAGS = $(common_module_ldflags) +mod_extforward_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_access.la +mod_access_la_SOURCES = mod_access.c +mod_access_la_LDFLAGS = $(common_module_ldflags) +mod_access_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_compress.la +mod_compress_la_SOURCES = mod_compress.c +mod_compress_la_LDFLAGS = $(common_module_ldflags) +mod_compress_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd) + +lib_LTLIBRARIES += mod_deflate.la +mod_deflate_la_SOURCES = mod_deflate.c +mod_deflate_la_LDFLAGS = $(common_module_ldflags) +mod_deflate_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd) + +lib_LTLIBRARIES += mod_auth.la +mod_auth_la_SOURCES = mod_auth.c +mod_auth_la_LDFLAGS = $(common_module_ldflags) +mod_auth_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_authn_file.la +mod_authn_file_la_SOURCES = mod_authn_file.c +mod_authn_file_la_LDFLAGS = $(common_module_ldflags) +mod_authn_file_la_LIBADD = $(CRYPT_LIB) $(CRYPTO_LIB) $(common_libadd) + +if BUILD_WITH_KRB5 +lib_LTLIBRARIES += mod_authn_gssapi.la +mod_authn_gssapi_la_SOURCES = mod_authn_gssapi.c +mod_authn_gssapi_la_LDFLAGS = $(common_module_ldflags) +mod_authn_gssapi_la_LIBADD = $(KRB5_LIB) $(common_libadd) +endif + +if BUILD_WITH_LDAP +lib_LTLIBRARIES += mod_authn_ldap.la +mod_authn_ldap_la_SOURCES = mod_authn_ldap.c +mod_authn_ldap_la_LDFLAGS = $(common_module_ldflags) +mod_authn_ldap_la_LIBADD = $(LDAP_LIB) $(LBER_LIB) $(common_libadd) +endif + +if BUILD_WITH_PAM +lib_LTLIBRARIES += mod_authn_pam.la +mod_authn_pam_la_SOURCES = mod_authn_pam.c +mod_authn_pam_la_LDFLAGS = $(common_module_ldflags) +mod_authn_pam_la_LIBADD = $(PAM_LIB) $(common_libadd) +endif + +if BUILD_WITH_MYSQL +lib_LTLIBRARIES += mod_authn_mysql.la +mod_authn_mysql_la_SOURCES = mod_authn_mysql.c +mod_authn_mysql_la_LDFLAGS = $(common_module_ldflags) +mod_authn_mysql_la_LIBADD = $(CRYPT_LIB) $(MYSQL_LIBS) $(common_libadd) +mod_authn_mysql_la_CPPFLAGS = $(MYSQL_CFLAGS) +endif + +if BUILD_WITH_SASL +lib_LTLIBRARIES += mod_authn_sasl.la +mod_authn_sasl_la_SOURCES = mod_authn_sasl.c +mod_authn_sasl_la_LDFLAGS = $(common_module_ldflags) +mod_authn_sasl_la_LIBADD = $(SASL_LIBS) $(common_libadd) +mod_authn_sasl_la_CPPFLAGS = $(SASL_CFLAGS) +endif + +if BUILD_WITH_OPENSSL +lib_LTLIBRARIES += mod_openssl.la +mod_openssl_la_SOURCES = mod_openssl.c +mod_openssl_la_LDFLAGS = $(common_module_ldflags) +mod_openssl_la_LIBADD = $(SSL_LIB) $(common_libadd) +endif + +lib_LTLIBRARIES += mod_rewrite.la +mod_rewrite_la_SOURCES = mod_rewrite.c +mod_rewrite_la_LDFLAGS = $(common_module_ldflags) +mod_rewrite_la_LIBADD = $(PCRE_LIB) $(common_libadd) + +lib_LTLIBRARIES += mod_redirect.la +mod_redirect_la_SOURCES = mod_redirect.c +mod_redirect_la_LDFLAGS = $(common_module_ldflags) +mod_redirect_la_LIBADD = $(PCRE_LIB) $(common_libadd) + +lib_LTLIBRARIES += mod_status.la +mod_status_la_SOURCES = mod_status.c +mod_status_la_LDFLAGS = $(common_module_ldflags) +mod_status_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_accesslog.la +mod_accesslog_la_SOURCES = mod_accesslog.c +mod_accesslog_la_LDFLAGS = $(common_module_ldflags) +mod_accesslog_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_uploadprogress.la +mod_uploadprogress_la_SOURCES = mod_uploadprogress.c +mod_uploadprogress_la_LDFLAGS = $(common_module_ldflags) +mod_uploadprogress_la_LIBADD = $(common_libadd) + +lib_LTLIBRARIES += mod_wstunnel.la +mod_wstunnel_la_SOURCES = mod_wstunnel.c +mod_wstunnel_la_LDFLAGS = $(common_module_ldflags) +mod_wstunnel_la_LIBADD = $(common_libadd) $(CRYPTO_LIB) + + +hdr = server.h base64.h buffer.h burl.h network.h log.h http_kv.h keyvalue.h \ + response.h request.h fastcgi.h chunk.h \ + first.h settings.h http_chunk.h \ + algo_sha1.h md5.h http_auth.h http_header.h http_vhostdb.h stream.h \ + fdevent.h gw_backend.h connections.h base.h base_decls.h stat_cache.h \ + plugin.h \ + etag.h joblist.h array.h vector.h crc32.h \ + fdevent_impl.h network_write.h configfile.h \ + mod_ssi.h mod_ssi_expr.h inet_ntop_cache.h \ + configparser.h mod_ssi_exprparser.h \ + rand.h \ + sys-crypto.h sys-endian.h sys-mmap.h sys-socket.h sys-strings.h \ + mod_cml.h mod_cml_funcs.h \ + safe_memclear.h sock_addr.h splaytree.h status_counter.h \ + mod_magnet_cache.h + + +DEFS= @DEFS@ -DHAVE_VERSIONSTAMP_H -DLIBRARY_DIR="\"$(libdir)\"" -DSBIN_DIR="\"$(sbindir)\"" + + +if LIGHTTPD_STATIC + +## static lighttpd server (used in conjunction with -DLIGHTTPD_STATIC) +## (order is not important) +lighttpd_SOURCES = \ + $(src) \ + mod_access.c \ + mod_accesslog.c \ + mod_alias.c \ + mod_auth.c \ + mod_authn_file.c \ + mod_cgi.c \ + mod_compress.c \ + mod_deflate.c \ + mod_dirlisting.c \ + mod_evasive.c \ + mod_expire.c \ + mod_extforward.c \ + mod_fastcgi.c \ + mod_flv_streaming.c \ + mod_indexfile.c \ + mod_proxy.c \ + mod_redirect.c \ + mod_rewrite.c \ + mod_rrdtool.c \ + mod_scgi.c \ + mod_secdownload.c \ + mod_setenv.c \ + mod_simple_vhost.c \ + mod_ssi_exprparser.c mod_ssi_expr.c mod_ssi.c \ + mod_staticfile.c \ + mod_status.c \ + mod_uploadprogress.c \ + mod_userdir.c \ + mod_usertrack.c \ + mod_vhostdb.c \ + mod_webdav.c +lighttpd_CPPFLAGS = \ + -DLIGHTTPD_STATIC \ + $(XML_CFLAGS) $(SQLITE_CFLAGS) \ + $(FAM_CFLAGS) $(LIBEV_CFLAGS) $(LIBUNWIND_CFLAGS) +lighttpd_LDADD = \ + $(common_libadd) \ + $(CRYPT_LIB) $(CRYPTO_LIB) \ + $(XML_LIBS) $(SQLITE_LIBS) $(UUID_LIBS) \ + $(PCRE_LIB) $(Z_LIB) $(BZ_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) \ + $(FAM_LIBS) $(LIBEV_LIBS) $(LIBUNWIND_LIBS) +lighttpd_LDFLAGS = -export-dynamic + +if BUILD_WITH_GEOIP +lighttpd_SOURCES += mod_geoip.c +lighttpd_LDADD += $(GEOIP_LIB) +endif +if BUILD_WITH_LUA +lighttpd_SOURCES += mod_cml.c mod_cml_lua.c mod_cml_funcs.c \ + mod_magnet.c mod_magnet_cache.c +lighttpd_CPPFLAGS += $(LUA_CFLAGS) +lighttpd_LDADD += $(LUA_LIBS) -lm +endif +if BUILD_WITH_KRB5 +lighttpd_SOURCES += mod_authn_gssapi.c +lighttpd_LDADD += $(KRB5_LIB) +endif +if BUILD_WITH_LDAP +lighttpd_SOURCES += mod_authn_ldap.c mod_vhostdb_ldap.c +lighttpd_LDADD += $(LDAP_LIB) $(LBER_LIB) +endif +if BUILD_WITH_PAM +lighttpd_SOURCES += mod_authn_pam.c +lighttpd_LDADD += $(PAM_LIB) +endif +if BUILD_WITH_MYSQL +lighttpd_SOURCES += mod_authn_mysql.c mod_mysql_vhost.c mod_vhostdb_mysql.c +lighttpd_CPPFLAGS += $(MYSQL_CFLAGS) +lighttpd_LDADD += $(MYSQL_LIBS) +endif +if BUILD_WITH_PGSQL +lighttpd_SOURCES += mod_vhostdb_pgsql.c +lighttpd_CPPFLAGS += $(PGSQL_INCLUDE) +lighttpd_LDADD += $(PGSQL_LIBS) +endif +if BUILD_WITH_DBI +lighttpd_SOURCES += mod_vhostdb_dbi.c +lighttpd_CPPFLAGS += $(DBI_CFLAGS) +lighttpd_LDADD += $(DBI_LIBS) +endif +if BUILD_WITH_OPENSSL +lighttpd_SOURCES += mod_openssl.c +lighttpd_LDADD += $(SSL_LIB) +endif +if BUILD_WITH_MEMCACHED +lighttpd_CPPFLAGS += $(MEMCACHED_CFLAGS) +lighttpd_LDADD += $(MEMCACHED_LIB) +endif +if BUILD_WITH_GDBM +lighttpd_LDADD += $(GDBM_LIB) +endif +if BUILD_MOD_TRIGGER_B4_DL +lighttpd_SOURCES += mod_trigger_b4_dl.c +endif + +else + +## default lighttpd server +lighttpd_SOURCES = $(src) +lighttpd_CPPFLAGS = $(FAM_CFLAGS) $(LIBEV_CFLAGS) +lighttpd_LDADD = $(PCRE_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) $(common_libadd) $(CRYPTO_LIB) $(FAM_LIBS) $(LIBEV_LIBS) $(LIBUNWIND_LIBS) +lighttpd_LDFLAGS = -export-dynamic + +endif + +t_test_array_SOURCES = t/test_array.c array.c data_array.c data_integer.c data_string.c buffer.c +t_test_array_LDADD = $(LIBUNWIND_LIBS) + +t_test_buffer_SOURCES = t/test_buffer.c buffer.c +t_test_buffer_LDADD = $(LIBUNWIND_LIBS) + +t_test_base64_SOURCES = t/test_base64.c base64.c buffer.c +t_test_base64_LDADD = $(LIBUNWIND_LIBS) + +t_test_burl_SOURCES = t/test_burl.c burl.c buffer.c base64.c +t_test_burl_LDADD = $(LIBUNWIND_LIBS) + +t_test_configfile_SOURCES = t/test_configfile.c buffer.c array.c data_config.c data_integer.c data_string.c http_header.c http_kv.c vector.c log.c sock_addr.c +t_test_configfile_LDADD = $(PCRE_LIB) $(LIBUNWIND_LIBS) + +t_test_keyvalue_SOURCES = t/test_keyvalue.c burl.c buffer.c base64.c array.c data_integer.c data_string.c log.c +t_test_keyvalue_LDADD = $(PCRE_LIB) $(LIBUNWIND_LIBS) + +t_test_mod_access_SOURCES = t/test_mod_access.c configfile-glue.c buffer.c array.c data_config.c data_integer.c data_string.c http_header.c http_kv.c vector.c log.c sock_addr.c +t_test_mod_access_LDADD = $(PCRE_LIB) $(LIBUNWIND_LIBS) + +t_test_mod_evhost_SOURCES = t/test_mod_evhost.c configfile-glue.c buffer.c array.c data_config.c data_integer.c data_string.c http_header.c http_kv.c vector.c log.c sock_addr.c +t_test_mod_evhost_LDADD = $(PCRE_LIB) $(LIBUNWIND_LIBS) + +t_test_mod_simple_vhost_SOURCES = t/test_mod_simple_vhost.c configfile-glue.c buffer.c array.c data_config.c data_integer.c data_string.c http_header.c http_kv.c vector.c log.c sock_addr.c +t_test_mod_simple_vhost_LDADD = $(PCRE_LIB) $(LIBUNWIND_LIBS) + +t_test_request_SOURCES = t/test_request.c request.c buffer.c array.c data_integer.c data_string.c http_header.c http_kv.c log.c sock_addr.c +t_test_request_LDADD = $(LIBUNWIND_LIBS) + +noinst_HEADERS = $(hdr) +EXTRA_DIST = \ + mod_skeleton.c \ + configparser.y \ + mod_ssi_exprparser.y \ + lemon.c \ + lempar.c \ + SConscript \ + CMakeLists.txt config.h.cmake \ + meson.build diff --git a/data/lighttpd/lighttpd-1.4.53/src/Makefile.in b/data/lighttpd/lighttpd-1.4.53/src/Makefile.in new file mode 100644 index 000000000..9e36531e6 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/Makefile.in @@ -0,0 +1,4913 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +noinst_PROGRAMS = t/test_array$(EXEEXT) t/test_buffer$(EXEEXT) \ + t/test_burl$(EXEEXT) t/test_base64$(EXEEXT) \ + t/test_configfile$(EXEEXT) t/test_keyvalue$(EXEEXT) \ + t/test_mod_access$(EXEEXT) t/test_mod_evhost$(EXEEXT) \ + t/test_mod_simple_vhost$(EXEEXT) t/test_request$(EXEEXT) +sbin_PROGRAMS = lighttpd$(EXEEXT) lighttpd-angel$(EXEEXT) +@NO_RDYNAMIC_TRUE@am__append_1 = liblightcomp.la +@NO_RDYNAMIC_FALSE@am__append_2 = $(common_src) +@BUILD_WITH_GEOIP_TRUE@am__append_3 = mod_geoip.la +@BUILD_WITH_LUA_TRUE@am__append_4 = mod_magnet.la mod_cml.la +@BUILD_MOD_TRIGGER_B4_DL_TRUE@am__append_5 = mod_trigger_b4_dl.la +@BUILD_WITH_LDAP_TRUE@am__append_6 = mod_vhostdb_ldap.la +@BUILD_WITH_MYSQL_TRUE@am__append_7 = mod_mysql_vhost.la \ +@BUILD_WITH_MYSQL_TRUE@ mod_vhostdb_mysql.la +@BUILD_WITH_PGSQL_TRUE@am__append_8 = mod_vhostdb_pgsql.la +@BUILD_WITH_DBI_TRUE@am__append_9 = mod_vhostdb_dbi.la +@BUILD_WITH_KRB5_TRUE@am__append_10 = mod_authn_gssapi.la +@BUILD_WITH_LDAP_TRUE@am__append_11 = mod_authn_ldap.la +@BUILD_WITH_PAM_TRUE@am__append_12 = mod_authn_pam.la +@BUILD_WITH_MYSQL_TRUE@am__append_13 = mod_authn_mysql.la +@BUILD_WITH_SASL_TRUE@am__append_14 = mod_authn_sasl.la +@BUILD_WITH_OPENSSL_TRUE@am__append_15 = mod_openssl.la +@BUILD_WITH_GEOIP_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_16 = mod_geoip.c +@BUILD_WITH_GEOIP_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_17 = $(GEOIP_LIB) +@BUILD_WITH_LUA_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_18 = mod_cml.c mod_cml_lua.c mod_cml_funcs.c \ +@BUILD_WITH_LUA_TRUE@@LIGHTTPD_STATIC_TRUE@ mod_magnet.c mod_magnet_cache.c + +@BUILD_WITH_LUA_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_19 = $(LUA_CFLAGS) +@BUILD_WITH_LUA_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_20 = $(LUA_LIBS) -lm +@BUILD_WITH_KRB5_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_21 = mod_authn_gssapi.c +@BUILD_WITH_KRB5_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_22 = $(KRB5_LIB) +@BUILD_WITH_LDAP_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_23 = mod_authn_ldap.c mod_vhostdb_ldap.c +@BUILD_WITH_LDAP_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_24 = $(LDAP_LIB) $(LBER_LIB) +@BUILD_WITH_PAM_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_25 = mod_authn_pam.c +@BUILD_WITH_PAM_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_26 = $(PAM_LIB) +@BUILD_WITH_MYSQL_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_27 = mod_authn_mysql.c mod_mysql_vhost.c mod_vhostdb_mysql.c +@BUILD_WITH_MYSQL_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_28 = $(MYSQL_CFLAGS) +@BUILD_WITH_MYSQL_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_29 = $(MYSQL_LIBS) +@BUILD_WITH_PGSQL_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_30 = mod_vhostdb_pgsql.c +@BUILD_WITH_PGSQL_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_31 = $(PGSQL_INCLUDE) +@BUILD_WITH_PGSQL_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_32 = $(PGSQL_LIBS) +@BUILD_WITH_DBI_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_33 = mod_vhostdb_dbi.c +@BUILD_WITH_DBI_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_34 = $(DBI_CFLAGS) +@BUILD_WITH_DBI_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_35 = $(DBI_LIBS) +@BUILD_WITH_OPENSSL_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_36 = mod_openssl.c +@BUILD_WITH_OPENSSL_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_37 = $(SSL_LIB) +@BUILD_WITH_MEMCACHED_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_38 = $(MEMCACHED_CFLAGS) +@BUILD_WITH_MEMCACHED_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_39 = $(MEMCACHED_LIB) +@BUILD_WITH_GDBM_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_40 = $(GDBM_LIB) +@BUILD_MOD_TRIGGER_B4_DL_TRUE@@LIGHTTPD_STATIC_TRUE@am__append_41 = mod_trigger_b4_dl.c +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = \ + $(top_srcdir)/scripts/m4/ax_prog_cc_for_build.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(libdir)" +PROGRAMS = $(noinst_PROGRAMS) $(sbin_PROGRAMS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +@NO_RDYNAMIC_TRUE@liblightcomp_la_DEPENDENCIES = \ +@NO_RDYNAMIC_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ +@NO_RDYNAMIC_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ +@NO_RDYNAMIC_TRUE@ $(am__DEPENDENCIES_1) +am__liblightcomp_la_SOURCES_DIST = base64.c buffer.c burl.c log.c \ + http_header.c http_kv.c keyvalue.c chunk.c http_chunk.c \ + stream.c fdevent.c gw_backend.c stat_cache.c plugin.c \ + joblist.c etag.c array.c data_string.c data_array.c \ + data_integer.c algo_sha1.c md5.c vector.c fdevent_select.c \ + fdevent_libev.c fdevent_poll.c fdevent_linux_sysepoll.c \ + fdevent_solaris_devpoll.c fdevent_solaris_port.c \ + fdevent_freebsd_kqueue.c data_config.c crc32.c \ + connections-glue.c configfile-glue.c http-header-glue.c \ + http_auth.c http_vhostdb.c rand.c request.c sock_addr.c \ + splaytree.c safe_memclear.c +am__objects_1 = liblightcomp_la-base64.lo liblightcomp_la-buffer.lo \ + liblightcomp_la-burl.lo liblightcomp_la-log.lo \ + liblightcomp_la-http_header.lo liblightcomp_la-http_kv.lo \ + liblightcomp_la-keyvalue.lo liblightcomp_la-chunk.lo \ + liblightcomp_la-http_chunk.lo liblightcomp_la-stream.lo \ + liblightcomp_la-fdevent.lo liblightcomp_la-gw_backend.lo \ + liblightcomp_la-stat_cache.lo liblightcomp_la-plugin.lo \ + liblightcomp_la-joblist.lo liblightcomp_la-etag.lo \ + liblightcomp_la-array.lo liblightcomp_la-data_string.lo \ + liblightcomp_la-data_array.lo liblightcomp_la-data_integer.lo \ + liblightcomp_la-algo_sha1.lo liblightcomp_la-md5.lo \ + liblightcomp_la-vector.lo liblightcomp_la-fdevent_select.lo \ + liblightcomp_la-fdevent_libev.lo \ + liblightcomp_la-fdevent_poll.lo \ + liblightcomp_la-fdevent_linux_sysepoll.lo \ + liblightcomp_la-fdevent_solaris_devpoll.lo \ + liblightcomp_la-fdevent_solaris_port.lo \ + liblightcomp_la-fdevent_freebsd_kqueue.lo \ + liblightcomp_la-data_config.lo liblightcomp_la-crc32.lo \ + liblightcomp_la-connections-glue.lo \ + liblightcomp_la-configfile-glue.lo \ + liblightcomp_la-http-header-glue.lo \ + liblightcomp_la-http_auth.lo liblightcomp_la-http_vhostdb.lo \ + liblightcomp_la-rand.lo liblightcomp_la-request.lo \ + liblightcomp_la-sock_addr.lo liblightcomp_la-splaytree.lo \ + liblightcomp_la-safe_memclear.lo +@NO_RDYNAMIC_TRUE@am_liblightcomp_la_OBJECTS = $(am__objects_1) +liblightcomp_la_OBJECTS = $(am_liblightcomp_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +liblightcomp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(liblightcomp_la_CFLAGS) $(CFLAGS) $(liblightcomp_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@NO_RDYNAMIC_TRUE@am_liblightcomp_la_rpath = -rpath $(libdir) +@NO_RDYNAMIC_TRUE@am__DEPENDENCIES_2 = liblightcomp.la +mod_access_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_access_la_OBJECTS = mod_access.lo +mod_access_la_OBJECTS = $(am_mod_access_la_OBJECTS) +mod_access_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_access_la_LDFLAGS) $(LDFLAGS) -o $@ +mod_accesslog_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_accesslog_la_OBJECTS = mod_accesslog.lo +mod_accesslog_la_OBJECTS = $(am_mod_accesslog_la_OBJECTS) +mod_accesslog_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_accesslog_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +mod_alias_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_alias_la_OBJECTS = mod_alias.lo +mod_alias_la_OBJECTS = $(am_mod_alias_la_OBJECTS) +mod_alias_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_alias_la_LDFLAGS) $(LDFLAGS) -o $@ +mod_auth_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_auth_la_OBJECTS = mod_auth.lo +mod_auth_la_OBJECTS = $(am_mod_auth_la_OBJECTS) +mod_auth_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_auth_la_LDFLAGS) $(LDFLAGS) -o $@ +mod_authn_file_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) +am_mod_authn_file_la_OBJECTS = mod_authn_file.lo +mod_authn_file_la_OBJECTS = $(am_mod_authn_file_la_OBJECTS) +mod_authn_file_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_authn_file_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +@BUILD_WITH_KRB5_TRUE@mod_authn_gssapi_la_DEPENDENCIES = \ +@BUILD_WITH_KRB5_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_KRB5_TRUE@ $(am__DEPENDENCIES_2) +am__mod_authn_gssapi_la_SOURCES_DIST = mod_authn_gssapi.c +@BUILD_WITH_KRB5_TRUE@am_mod_authn_gssapi_la_OBJECTS = \ +@BUILD_WITH_KRB5_TRUE@ mod_authn_gssapi.lo +mod_authn_gssapi_la_OBJECTS = $(am_mod_authn_gssapi_la_OBJECTS) +mod_authn_gssapi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_authn_gssapi_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@BUILD_WITH_KRB5_TRUE@am_mod_authn_gssapi_la_rpath = -rpath $(libdir) +@BUILD_WITH_LDAP_TRUE@mod_authn_ldap_la_DEPENDENCIES = \ +@BUILD_WITH_LDAP_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_LDAP_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_LDAP_TRUE@ $(am__DEPENDENCIES_2) +am__mod_authn_ldap_la_SOURCES_DIST = mod_authn_ldap.c +@BUILD_WITH_LDAP_TRUE@am_mod_authn_ldap_la_OBJECTS = \ +@BUILD_WITH_LDAP_TRUE@ mod_authn_ldap.lo +mod_authn_ldap_la_OBJECTS = $(am_mod_authn_ldap_la_OBJECTS) +mod_authn_ldap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_authn_ldap_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +@BUILD_WITH_LDAP_TRUE@am_mod_authn_ldap_la_rpath = -rpath $(libdir) +@BUILD_WITH_MYSQL_TRUE@mod_authn_mysql_la_DEPENDENCIES = \ +@BUILD_WITH_MYSQL_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_MYSQL_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_MYSQL_TRUE@ $(am__DEPENDENCIES_2) +am__mod_authn_mysql_la_SOURCES_DIST = mod_authn_mysql.c +@BUILD_WITH_MYSQL_TRUE@am_mod_authn_mysql_la_OBJECTS = \ +@BUILD_WITH_MYSQL_TRUE@ mod_authn_mysql_la-mod_authn_mysql.lo +mod_authn_mysql_la_OBJECTS = $(am_mod_authn_mysql_la_OBJECTS) +mod_authn_mysql_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_authn_mysql_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@BUILD_WITH_MYSQL_TRUE@am_mod_authn_mysql_la_rpath = -rpath $(libdir) +@BUILD_WITH_PAM_TRUE@mod_authn_pam_la_DEPENDENCIES = \ +@BUILD_WITH_PAM_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_PAM_TRUE@ $(am__DEPENDENCIES_2) +am__mod_authn_pam_la_SOURCES_DIST = mod_authn_pam.c +@BUILD_WITH_PAM_TRUE@am_mod_authn_pam_la_OBJECTS = mod_authn_pam.lo +mod_authn_pam_la_OBJECTS = $(am_mod_authn_pam_la_OBJECTS) +mod_authn_pam_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_authn_pam_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +@BUILD_WITH_PAM_TRUE@am_mod_authn_pam_la_rpath = -rpath $(libdir) +@BUILD_WITH_SASL_TRUE@mod_authn_sasl_la_DEPENDENCIES = \ +@BUILD_WITH_SASL_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_SASL_TRUE@ $(am__DEPENDENCIES_2) +am__mod_authn_sasl_la_SOURCES_DIST = mod_authn_sasl.c +@BUILD_WITH_SASL_TRUE@am_mod_authn_sasl_la_OBJECTS = \ +@BUILD_WITH_SASL_TRUE@ mod_authn_sasl_la-mod_authn_sasl.lo +mod_authn_sasl_la_OBJECTS = $(am_mod_authn_sasl_la_OBJECTS) +mod_authn_sasl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_authn_sasl_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +@BUILD_WITH_SASL_TRUE@am_mod_authn_sasl_la_rpath = -rpath $(libdir) +mod_cgi_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_cgi_la_OBJECTS = mod_cgi.lo +mod_cgi_la_OBJECTS = $(am_mod_cgi_la_OBJECTS) +mod_cgi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_cgi_la_LDFLAGS) $(LDFLAGS) -o $@ +@BUILD_WITH_LUA_TRUE@mod_cml_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ +@BUILD_WITH_LUA_TRUE@ $(am__DEPENDENCIES_2) \ +@BUILD_WITH_LUA_TRUE@ $(am__DEPENDENCIES_1) +am__mod_cml_la_SOURCES_DIST = mod_cml.c mod_cml_lua.c mod_cml_funcs.c +@BUILD_WITH_LUA_TRUE@am_mod_cml_la_OBJECTS = mod_cml_la-mod_cml.lo \ +@BUILD_WITH_LUA_TRUE@ mod_cml_la-mod_cml_lua.lo \ +@BUILD_WITH_LUA_TRUE@ mod_cml_la-mod_cml_funcs.lo +mod_cml_la_OBJECTS = $(am_mod_cml_la_OBJECTS) +mod_cml_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(mod_cml_la_CFLAGS) \ + $(CFLAGS) $(mod_cml_la_LDFLAGS) $(LDFLAGS) -o $@ +@BUILD_WITH_LUA_TRUE@am_mod_cml_la_rpath = -rpath $(libdir) +mod_compress_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) +am_mod_compress_la_OBJECTS = mod_compress.lo +mod_compress_la_OBJECTS = $(am_mod_compress_la_OBJECTS) +mod_compress_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_compress_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +mod_deflate_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) +am_mod_deflate_la_OBJECTS = mod_deflate.lo +mod_deflate_la_OBJECTS = $(am_mod_deflate_la_OBJECTS) +mod_deflate_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_deflate_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +mod_dirlisting_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_1) +am_mod_dirlisting_la_OBJECTS = mod_dirlisting.lo +mod_dirlisting_la_OBJECTS = $(am_mod_dirlisting_la_OBJECTS) +mod_dirlisting_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_dirlisting_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +mod_evasive_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_evasive_la_OBJECTS = mod_evasive.lo +mod_evasive_la_OBJECTS = $(am_mod_evasive_la_OBJECTS) +mod_evasive_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_evasive_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +mod_evhost_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_evhost_la_OBJECTS = mod_evhost.lo +mod_evhost_la_OBJECTS = $(am_mod_evhost_la_OBJECTS) +mod_evhost_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_evhost_la_LDFLAGS) $(LDFLAGS) -o $@ +mod_expire_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_expire_la_OBJECTS = mod_expire.lo +mod_expire_la_OBJECTS = $(am_mod_expire_la_OBJECTS) +mod_expire_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_expire_la_LDFLAGS) $(LDFLAGS) -o $@ +mod_extforward_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_extforward_la_OBJECTS = mod_extforward.lo +mod_extforward_la_OBJECTS = $(am_mod_extforward_la_OBJECTS) +mod_extforward_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_extforward_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +mod_fastcgi_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_fastcgi_la_OBJECTS = mod_fastcgi.lo +mod_fastcgi_la_OBJECTS = $(am_mod_fastcgi_la_OBJECTS) +mod_fastcgi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_fastcgi_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +mod_flv_streaming_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_flv_streaming_la_OBJECTS = mod_flv_streaming.lo +mod_flv_streaming_la_OBJECTS = $(am_mod_flv_streaming_la_OBJECTS) +mod_flv_streaming_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_flv_streaming_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@BUILD_WITH_GEOIP_TRUE@mod_geoip_la_DEPENDENCIES = \ +@BUILD_WITH_GEOIP_TRUE@ $(am__DEPENDENCIES_2) \ +@BUILD_WITH_GEOIP_TRUE@ $(am__DEPENDENCIES_1) +am__mod_geoip_la_SOURCES_DIST = mod_geoip.c +@BUILD_WITH_GEOIP_TRUE@am_mod_geoip_la_OBJECTS = mod_geoip.lo +mod_geoip_la_OBJECTS = $(am_mod_geoip_la_OBJECTS) +mod_geoip_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_geoip_la_LDFLAGS) $(LDFLAGS) -o $@ +@BUILD_WITH_GEOIP_TRUE@am_mod_geoip_la_rpath = -rpath $(libdir) +mod_indexfile_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_indexfile_la_OBJECTS = mod_indexfile.lo +mod_indexfile_la_OBJECTS = $(am_mod_indexfile_la_OBJECTS) +mod_indexfile_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_indexfile_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +@BUILD_WITH_LUA_TRUE@mod_magnet_la_DEPENDENCIES = \ +@BUILD_WITH_LUA_TRUE@ $(am__DEPENDENCIES_2) \ +@BUILD_WITH_LUA_TRUE@ $(am__DEPENDENCIES_1) +am__mod_magnet_la_SOURCES_DIST = mod_magnet.c mod_magnet_cache.c +@BUILD_WITH_LUA_TRUE@am_mod_magnet_la_OBJECTS = \ +@BUILD_WITH_LUA_TRUE@ mod_magnet_la-mod_magnet.lo \ +@BUILD_WITH_LUA_TRUE@ mod_magnet_la-mod_magnet_cache.lo +mod_magnet_la_OBJECTS = $(am_mod_magnet_la_OBJECTS) +mod_magnet_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(mod_magnet_la_CFLAGS) \ + $(CFLAGS) $(mod_magnet_la_LDFLAGS) $(LDFLAGS) -o $@ +@BUILD_WITH_LUA_TRUE@am_mod_magnet_la_rpath = -rpath $(libdir) +@BUILD_WITH_MYSQL_TRUE@mod_mysql_vhost_la_DEPENDENCIES = \ +@BUILD_WITH_MYSQL_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_MYSQL_TRUE@ $(am__DEPENDENCIES_2) +am__mod_mysql_vhost_la_SOURCES_DIST = mod_mysql_vhost.c +@BUILD_WITH_MYSQL_TRUE@am_mod_mysql_vhost_la_OBJECTS = \ +@BUILD_WITH_MYSQL_TRUE@ mod_mysql_vhost_la-mod_mysql_vhost.lo +mod_mysql_vhost_la_OBJECTS = $(am_mod_mysql_vhost_la_OBJECTS) +mod_mysql_vhost_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_mysql_vhost_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@BUILD_WITH_MYSQL_TRUE@am_mod_mysql_vhost_la_rpath = -rpath $(libdir) +@BUILD_WITH_OPENSSL_TRUE@mod_openssl_la_DEPENDENCIES = \ +@BUILD_WITH_OPENSSL_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_OPENSSL_TRUE@ $(am__DEPENDENCIES_2) +am__mod_openssl_la_SOURCES_DIST = mod_openssl.c +@BUILD_WITH_OPENSSL_TRUE@am_mod_openssl_la_OBJECTS = mod_openssl.lo +mod_openssl_la_OBJECTS = $(am_mod_openssl_la_OBJECTS) +mod_openssl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_openssl_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +@BUILD_WITH_OPENSSL_TRUE@am_mod_openssl_la_rpath = -rpath $(libdir) +mod_proxy_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_proxy_la_OBJECTS = mod_proxy.lo +mod_proxy_la_OBJECTS = $(am_mod_proxy_la_OBJECTS) +mod_proxy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_proxy_la_LDFLAGS) $(LDFLAGS) -o $@ +mod_redirect_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) +am_mod_redirect_la_OBJECTS = mod_redirect.lo +mod_redirect_la_OBJECTS = $(am_mod_redirect_la_OBJECTS) +mod_redirect_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_redirect_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +mod_rewrite_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) +am_mod_rewrite_la_OBJECTS = mod_rewrite.lo +mod_rewrite_la_OBJECTS = $(am_mod_rewrite_la_OBJECTS) +mod_rewrite_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_rewrite_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +mod_rrdtool_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_rrdtool_la_OBJECTS = mod_rrdtool.lo +mod_rrdtool_la_OBJECTS = $(am_mod_rrdtool_la_OBJECTS) +mod_rrdtool_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_rrdtool_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +mod_scgi_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_scgi_la_OBJECTS = mod_scgi.lo +mod_scgi_la_OBJECTS = $(am_mod_scgi_la_OBJECTS) +mod_scgi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_scgi_la_LDFLAGS) $(LDFLAGS) -o $@ +mod_secdownload_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_1) +am_mod_secdownload_la_OBJECTS = mod_secdownload.lo +mod_secdownload_la_OBJECTS = $(am_mod_secdownload_la_OBJECTS) +mod_secdownload_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_secdownload_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +mod_setenv_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_setenv_la_OBJECTS = mod_setenv.lo +mod_setenv_la_OBJECTS = $(am_mod_setenv_la_OBJECTS) +mod_setenv_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_setenv_la_LDFLAGS) $(LDFLAGS) -o $@ +mod_simple_vhost_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_simple_vhost_la_OBJECTS = mod_simple_vhost.lo +mod_simple_vhost_la_OBJECTS = $(am_mod_simple_vhost_la_OBJECTS) +mod_simple_vhost_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_simple_vhost_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +mod_sockproxy_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_sockproxy_la_OBJECTS = mod_sockproxy.lo +mod_sockproxy_la_OBJECTS = $(am_mod_sockproxy_la_OBJECTS) +mod_sockproxy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_sockproxy_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +mod_ssi_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_ssi_la_OBJECTS = mod_ssi_exprparser.lo mod_ssi_expr.lo \ + mod_ssi.lo +mod_ssi_la_OBJECTS = $(am_mod_ssi_la_OBJECTS) +mod_ssi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_ssi_la_LDFLAGS) $(LDFLAGS) -o $@ +mod_staticfile_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_staticfile_la_OBJECTS = mod_staticfile.lo +mod_staticfile_la_OBJECTS = $(am_mod_staticfile_la_OBJECTS) +mod_staticfile_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_staticfile_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +mod_status_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_status_la_OBJECTS = mod_status.lo +mod_status_la_OBJECTS = $(am_mod_status_la_OBJECTS) +mod_status_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(mod_status_la_LDFLAGS) $(LDFLAGS) -o $@ +@BUILD_MOD_TRIGGER_B4_DL_TRUE@mod_trigger_b4_dl_la_DEPENDENCIES = \ +@BUILD_MOD_TRIGGER_B4_DL_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_MOD_TRIGGER_B4_DL_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_MOD_TRIGGER_B4_DL_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_MOD_TRIGGER_B4_DL_TRUE@ $(am__DEPENDENCIES_2) +am__mod_trigger_b4_dl_la_SOURCES_DIST = mod_trigger_b4_dl.c +@BUILD_MOD_TRIGGER_B4_DL_TRUE@am_mod_trigger_b4_dl_la_OBJECTS = \ +@BUILD_MOD_TRIGGER_B4_DL_TRUE@ mod_trigger_b4_dl.lo +mod_trigger_b4_dl_la_OBJECTS = $(am_mod_trigger_b4_dl_la_OBJECTS) +mod_trigger_b4_dl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_trigger_b4_dl_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@BUILD_MOD_TRIGGER_B4_DL_TRUE@am_mod_trigger_b4_dl_la_rpath = -rpath \ +@BUILD_MOD_TRIGGER_B4_DL_TRUE@ $(libdir) +mod_uploadprogress_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_uploadprogress_la_OBJECTS = mod_uploadprogress.lo +mod_uploadprogress_la_OBJECTS = $(am_mod_uploadprogress_la_OBJECTS) +mod_uploadprogress_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_uploadprogress_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +mod_userdir_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_userdir_la_OBJECTS = mod_userdir.lo +mod_userdir_la_OBJECTS = $(am_mod_userdir_la_OBJECTS) +mod_userdir_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_userdir_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +mod_usertrack_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_usertrack_la_OBJECTS = mod_usertrack.lo +mod_usertrack_la_OBJECTS = $(am_mod_usertrack_la_OBJECTS) +mod_usertrack_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_usertrack_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +mod_vhostdb_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_vhostdb_la_OBJECTS = mod_vhostdb.lo +mod_vhostdb_la_OBJECTS = $(am_mod_vhostdb_la_OBJECTS) +mod_vhostdb_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_vhostdb_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +@BUILD_WITH_DBI_TRUE@mod_vhostdb_dbi_la_DEPENDENCIES = \ +@BUILD_WITH_DBI_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_DBI_TRUE@ $(am__DEPENDENCIES_2) +am__mod_vhostdb_dbi_la_SOURCES_DIST = mod_vhostdb_dbi.c +@BUILD_WITH_DBI_TRUE@am_mod_vhostdb_dbi_la_OBJECTS = \ +@BUILD_WITH_DBI_TRUE@ mod_vhostdb_dbi_la-mod_vhostdb_dbi.lo +mod_vhostdb_dbi_la_OBJECTS = $(am_mod_vhostdb_dbi_la_OBJECTS) +mod_vhostdb_dbi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_vhostdb_dbi_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@BUILD_WITH_DBI_TRUE@am_mod_vhostdb_dbi_la_rpath = -rpath $(libdir) +@BUILD_WITH_LDAP_TRUE@mod_vhostdb_ldap_la_DEPENDENCIES = \ +@BUILD_WITH_LDAP_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_LDAP_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_LDAP_TRUE@ $(am__DEPENDENCIES_2) +am__mod_vhostdb_ldap_la_SOURCES_DIST = mod_vhostdb_ldap.c +@BUILD_WITH_LDAP_TRUE@am_mod_vhostdb_ldap_la_OBJECTS = \ +@BUILD_WITH_LDAP_TRUE@ mod_vhostdb_ldap.lo +mod_vhostdb_ldap_la_OBJECTS = $(am_mod_vhostdb_ldap_la_OBJECTS) +mod_vhostdb_ldap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_vhostdb_ldap_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@BUILD_WITH_LDAP_TRUE@am_mod_vhostdb_ldap_la_rpath = -rpath $(libdir) +@BUILD_WITH_MYSQL_TRUE@mod_vhostdb_mysql_la_DEPENDENCIES = \ +@BUILD_WITH_MYSQL_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_MYSQL_TRUE@ $(am__DEPENDENCIES_2) +am__mod_vhostdb_mysql_la_SOURCES_DIST = mod_vhostdb_mysql.c +@BUILD_WITH_MYSQL_TRUE@am_mod_vhostdb_mysql_la_OBJECTS = mod_vhostdb_mysql_la-mod_vhostdb_mysql.lo +mod_vhostdb_mysql_la_OBJECTS = $(am_mod_vhostdb_mysql_la_OBJECTS) +mod_vhostdb_mysql_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_vhostdb_mysql_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@BUILD_WITH_MYSQL_TRUE@am_mod_vhostdb_mysql_la_rpath = -rpath \ +@BUILD_WITH_MYSQL_TRUE@ $(libdir) +@BUILD_WITH_PGSQL_TRUE@mod_vhostdb_pgsql_la_DEPENDENCIES = \ +@BUILD_WITH_PGSQL_TRUE@ $(am__DEPENDENCIES_1) \ +@BUILD_WITH_PGSQL_TRUE@ $(am__DEPENDENCIES_2) +am__mod_vhostdb_pgsql_la_SOURCES_DIST = mod_vhostdb_pgsql.c +@BUILD_WITH_PGSQL_TRUE@am_mod_vhostdb_pgsql_la_OBJECTS = mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.lo +mod_vhostdb_pgsql_la_OBJECTS = $(am_mod_vhostdb_pgsql_la_OBJECTS) +mod_vhostdb_pgsql_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_vhostdb_pgsql_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@BUILD_WITH_PGSQL_TRUE@am_mod_vhostdb_pgsql_la_rpath = -rpath \ +@BUILD_WITH_PGSQL_TRUE@ $(libdir) +mod_webdav_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_mod_webdav_la_OBJECTS = mod_webdav_la-mod_webdav.lo +mod_webdav_la_OBJECTS = $(am_mod_webdav_la_OBJECTS) +mod_webdav_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(mod_webdav_la_CFLAGS) \ + $(CFLAGS) $(mod_webdav_la_LDFLAGS) $(LDFLAGS) -o $@ +mod_wstunnel_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_1) +am_mod_wstunnel_la_OBJECTS = mod_wstunnel.lo +mod_wstunnel_la_OBJECTS = $(am_mod_wstunnel_la_OBJECTS) +mod_wstunnel_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_wstunnel_la_LDFLAGS) $(LDFLAGS) \ + -o $@ +am__lighttpd_SOURCES_DIST = server.c response.c connections.c \ + inet_ntop_cache.c network.c network_write.c configfile.c \ + configparser.c base64.c buffer.c burl.c log.c http_header.c \ + http_kv.c keyvalue.c chunk.c http_chunk.c stream.c fdevent.c \ + gw_backend.c stat_cache.c plugin.c joblist.c etag.c array.c \ + data_string.c data_array.c data_integer.c algo_sha1.c md5.c \ + vector.c fdevent_select.c fdevent_libev.c fdevent_poll.c \ + fdevent_linux_sysepoll.c fdevent_solaris_devpoll.c \ + fdevent_solaris_port.c fdevent_freebsd_kqueue.c data_config.c \ + crc32.c connections-glue.c configfile-glue.c \ + http-header-glue.c http_auth.c http_vhostdb.c rand.c request.c \ + sock_addr.c splaytree.c safe_memclear.c mod_access.c \ + mod_accesslog.c mod_alias.c mod_auth.c mod_authn_file.c \ + mod_cgi.c mod_compress.c mod_deflate.c mod_dirlisting.c \ + mod_evasive.c mod_expire.c mod_extforward.c mod_fastcgi.c \ + mod_flv_streaming.c mod_indexfile.c mod_proxy.c mod_redirect.c \ + mod_rewrite.c mod_rrdtool.c mod_scgi.c mod_secdownload.c \ + mod_setenv.c mod_simple_vhost.c mod_ssi_exprparser.c \ + mod_ssi_expr.c mod_ssi.c mod_staticfile.c mod_status.c \ + mod_uploadprogress.c mod_userdir.c mod_usertrack.c \ + mod_vhostdb.c mod_webdav.c mod_geoip.c mod_cml.c mod_cml_lua.c \ + mod_cml_funcs.c mod_magnet.c mod_magnet_cache.c \ + mod_authn_gssapi.c mod_authn_ldap.c mod_vhostdb_ldap.c \ + mod_authn_pam.c mod_authn_mysql.c mod_mysql_vhost.c \ + mod_vhostdb_mysql.c mod_vhostdb_pgsql.c mod_vhostdb_dbi.c \ + mod_openssl.c mod_trigger_b4_dl.c +am__objects_2 = lighttpd-base64.$(OBJEXT) lighttpd-buffer.$(OBJEXT) \ + lighttpd-burl.$(OBJEXT) lighttpd-log.$(OBJEXT) \ + lighttpd-http_header.$(OBJEXT) lighttpd-http_kv.$(OBJEXT) \ + lighttpd-keyvalue.$(OBJEXT) lighttpd-chunk.$(OBJEXT) \ + lighttpd-http_chunk.$(OBJEXT) lighttpd-stream.$(OBJEXT) \ + lighttpd-fdevent.$(OBJEXT) lighttpd-gw_backend.$(OBJEXT) \ + lighttpd-stat_cache.$(OBJEXT) lighttpd-plugin.$(OBJEXT) \ + lighttpd-joblist.$(OBJEXT) lighttpd-etag.$(OBJEXT) \ + lighttpd-array.$(OBJEXT) lighttpd-data_string.$(OBJEXT) \ + lighttpd-data_array.$(OBJEXT) lighttpd-data_integer.$(OBJEXT) \ + lighttpd-algo_sha1.$(OBJEXT) lighttpd-md5.$(OBJEXT) \ + lighttpd-vector.$(OBJEXT) lighttpd-fdevent_select.$(OBJEXT) \ + lighttpd-fdevent_libev.$(OBJEXT) \ + lighttpd-fdevent_poll.$(OBJEXT) \ + lighttpd-fdevent_linux_sysepoll.$(OBJEXT) \ + lighttpd-fdevent_solaris_devpoll.$(OBJEXT) \ + lighttpd-fdevent_solaris_port.$(OBJEXT) \ + lighttpd-fdevent_freebsd_kqueue.$(OBJEXT) \ + lighttpd-data_config.$(OBJEXT) lighttpd-crc32.$(OBJEXT) \ + lighttpd-connections-glue.$(OBJEXT) \ + lighttpd-configfile-glue.$(OBJEXT) \ + lighttpd-http-header-glue.$(OBJEXT) \ + lighttpd-http_auth.$(OBJEXT) lighttpd-http_vhostdb.$(OBJEXT) \ + lighttpd-rand.$(OBJEXT) lighttpd-request.$(OBJEXT) \ + lighttpd-sock_addr.$(OBJEXT) lighttpd-splaytree.$(OBJEXT) \ + lighttpd-safe_memclear.$(OBJEXT) +@NO_RDYNAMIC_FALSE@am__objects_3 = $(am__objects_2) +am__objects_4 = lighttpd-server.$(OBJEXT) lighttpd-response.$(OBJEXT) \ + lighttpd-connections.$(OBJEXT) \ + lighttpd-inet_ntop_cache.$(OBJEXT) lighttpd-network.$(OBJEXT) \ + lighttpd-network_write.$(OBJEXT) lighttpd-configfile.$(OBJEXT) \ + lighttpd-configparser.$(OBJEXT) $(am__objects_3) +@BUILD_WITH_GEOIP_TRUE@@LIGHTTPD_STATIC_TRUE@am__objects_5 = lighttpd-mod_geoip.$(OBJEXT) +@BUILD_WITH_LUA_TRUE@@LIGHTTPD_STATIC_TRUE@am__objects_6 = lighttpd-mod_cml.$(OBJEXT) \ +@BUILD_WITH_LUA_TRUE@@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_cml_lua.$(OBJEXT) \ +@BUILD_WITH_LUA_TRUE@@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_cml_funcs.$(OBJEXT) \ +@BUILD_WITH_LUA_TRUE@@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_magnet.$(OBJEXT) \ +@BUILD_WITH_LUA_TRUE@@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_magnet_cache.$(OBJEXT) +@BUILD_WITH_KRB5_TRUE@@LIGHTTPD_STATIC_TRUE@am__objects_7 = lighttpd-mod_authn_gssapi.$(OBJEXT) +@BUILD_WITH_LDAP_TRUE@@LIGHTTPD_STATIC_TRUE@am__objects_8 = lighttpd-mod_authn_ldap.$(OBJEXT) \ +@BUILD_WITH_LDAP_TRUE@@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_vhostdb_ldap.$(OBJEXT) +@BUILD_WITH_PAM_TRUE@@LIGHTTPD_STATIC_TRUE@am__objects_9 = lighttpd-mod_authn_pam.$(OBJEXT) +@BUILD_WITH_MYSQL_TRUE@@LIGHTTPD_STATIC_TRUE@am__objects_10 = lighttpd-mod_authn_mysql.$(OBJEXT) \ +@BUILD_WITH_MYSQL_TRUE@@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_mysql_vhost.$(OBJEXT) \ +@BUILD_WITH_MYSQL_TRUE@@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_vhostdb_mysql.$(OBJEXT) +@BUILD_WITH_PGSQL_TRUE@@LIGHTTPD_STATIC_TRUE@am__objects_11 = lighttpd-mod_vhostdb_pgsql.$(OBJEXT) +@BUILD_WITH_DBI_TRUE@@LIGHTTPD_STATIC_TRUE@am__objects_12 = lighttpd-mod_vhostdb_dbi.$(OBJEXT) +@BUILD_WITH_OPENSSL_TRUE@@LIGHTTPD_STATIC_TRUE@am__objects_13 = lighttpd-mod_openssl.$(OBJEXT) +@BUILD_MOD_TRIGGER_B4_DL_TRUE@@LIGHTTPD_STATIC_TRUE@am__objects_14 = lighttpd-mod_trigger_b4_dl.$(OBJEXT) +@LIGHTTPD_STATIC_FALSE@am_lighttpd_OBJECTS = $(am__objects_4) +@LIGHTTPD_STATIC_TRUE@am_lighttpd_OBJECTS = $(am__objects_4) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_access.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_accesslog.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_alias.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_auth.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_authn_file.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_cgi.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_compress.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_deflate.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_dirlisting.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_evasive.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_expire.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_extforward.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_fastcgi.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_flv_streaming.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_indexfile.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_proxy.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_redirect.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_rewrite.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_rrdtool.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_scgi.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_secdownload.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_setenv.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_simple_vhost.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_ssi_exprparser.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_ssi_expr.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_ssi.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_staticfile.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_status.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_uploadprogress.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_userdir.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_usertrack.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_vhostdb.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ lighttpd-mod_webdav.$(OBJEXT) \ +@LIGHTTPD_STATIC_TRUE@ $(am__objects_5) $(am__objects_6) \ +@LIGHTTPD_STATIC_TRUE@ $(am__objects_7) $(am__objects_8) \ +@LIGHTTPD_STATIC_TRUE@ $(am__objects_9) $(am__objects_10) \ +@LIGHTTPD_STATIC_TRUE@ $(am__objects_11) $(am__objects_12) \ +@LIGHTTPD_STATIC_TRUE@ $(am__objects_13) $(am__objects_14) +lighttpd_OBJECTS = $(am_lighttpd_OBJECTS) +@BUILD_WITH_GEOIP_TRUE@@LIGHTTPD_STATIC_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) +@BUILD_WITH_LUA_TRUE@@LIGHTTPD_STATIC_TRUE@am__DEPENDENCIES_4 = $(am__DEPENDENCIES_1) +@BUILD_WITH_KRB5_TRUE@@LIGHTTPD_STATIC_TRUE@am__DEPENDENCIES_5 = $(am__DEPENDENCIES_1) +@BUILD_WITH_LDAP_TRUE@@LIGHTTPD_STATIC_TRUE@am__DEPENDENCIES_6 = $(am__DEPENDENCIES_1) \ +@BUILD_WITH_LDAP_TRUE@@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) +@BUILD_WITH_PAM_TRUE@@LIGHTTPD_STATIC_TRUE@am__DEPENDENCIES_7 = $(am__DEPENDENCIES_1) +@BUILD_WITH_MYSQL_TRUE@@LIGHTTPD_STATIC_TRUE@am__DEPENDENCIES_8 = $(am__DEPENDENCIES_1) +@BUILD_WITH_PGSQL_TRUE@@LIGHTTPD_STATIC_TRUE@am__DEPENDENCIES_9 = $(am__DEPENDENCIES_1) +@BUILD_WITH_DBI_TRUE@@LIGHTTPD_STATIC_TRUE@am__DEPENDENCIES_10 = $(am__DEPENDENCIES_1) +@BUILD_WITH_OPENSSL_TRUE@@LIGHTTPD_STATIC_TRUE@am__DEPENDENCIES_11 = $(am__DEPENDENCIES_1) +@BUILD_WITH_MEMCACHED_TRUE@@LIGHTTPD_STATIC_TRUE@am__DEPENDENCIES_12 = $(am__DEPENDENCIES_1) +@BUILD_WITH_GDBM_TRUE@@LIGHTTPD_STATIC_TRUE@am__DEPENDENCIES_13 = $(am__DEPENDENCIES_1) +@LIGHTTPD_STATIC_FALSE@lighttpd_DEPENDENCIES = $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_FALSE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_FALSE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_FALSE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_FALSE@ $(am__DEPENDENCIES_2) \ +@LIGHTTPD_STATIC_FALSE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_FALSE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_FALSE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_FALSE@ $(am__DEPENDENCIES_1) +@LIGHTTPD_STATIC_TRUE@lighttpd_DEPENDENCIES = $(am__DEPENDENCIES_2) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_1) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_3) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_4) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_5) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_6) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_7) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_8) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_9) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_10) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_11) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_12) \ +@LIGHTTPD_STATIC_TRUE@ $(am__DEPENDENCIES_13) +lighttpd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(lighttpd_LDFLAGS) $(LDFLAGS) -o $@ +am_lighttpd_angel_OBJECTS = lighttpd-angel.$(OBJEXT) +lighttpd_angel_OBJECTS = $(am_lighttpd_angel_OBJECTS) +lighttpd_angel_LDADD = $(LDADD) +am__dirstamp = $(am__leading_dot)dirstamp +am_t_test_array_OBJECTS = t/test_array.$(OBJEXT) array.$(OBJEXT) \ + data_array.$(OBJEXT) data_integer.$(OBJEXT) \ + data_string.$(OBJEXT) buffer.$(OBJEXT) +t_test_array_OBJECTS = $(am_t_test_array_OBJECTS) +t_test_array_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_t_test_base64_OBJECTS = t/test_base64.$(OBJEXT) base64.$(OBJEXT) \ + buffer.$(OBJEXT) +t_test_base64_OBJECTS = $(am_t_test_base64_OBJECTS) +t_test_base64_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_t_test_buffer_OBJECTS = t/test_buffer.$(OBJEXT) buffer.$(OBJEXT) +t_test_buffer_OBJECTS = $(am_t_test_buffer_OBJECTS) +t_test_buffer_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_t_test_burl_OBJECTS = t/test_burl.$(OBJEXT) burl.$(OBJEXT) \ + buffer.$(OBJEXT) base64.$(OBJEXT) +t_test_burl_OBJECTS = $(am_t_test_burl_OBJECTS) +t_test_burl_DEPENDENCIES = $(am__DEPENDENCIES_1) +am_t_test_configfile_OBJECTS = t/test_configfile.$(OBJEXT) \ + buffer.$(OBJEXT) array.$(OBJEXT) data_config.$(OBJEXT) \ + data_integer.$(OBJEXT) data_string.$(OBJEXT) \ + http_header.$(OBJEXT) http_kv.$(OBJEXT) vector.$(OBJEXT) \ + log.$(OBJEXT) sock_addr.$(OBJEXT) +t_test_configfile_OBJECTS = $(am_t_test_configfile_OBJECTS) +t_test_configfile_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_t_test_keyvalue_OBJECTS = t/test_keyvalue.$(OBJEXT) burl.$(OBJEXT) \ + buffer.$(OBJEXT) base64.$(OBJEXT) array.$(OBJEXT) \ + data_integer.$(OBJEXT) data_string.$(OBJEXT) log.$(OBJEXT) +t_test_keyvalue_OBJECTS = $(am_t_test_keyvalue_OBJECTS) +t_test_keyvalue_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_t_test_mod_access_OBJECTS = t/test_mod_access.$(OBJEXT) \ + configfile-glue.$(OBJEXT) buffer.$(OBJEXT) array.$(OBJEXT) \ + data_config.$(OBJEXT) data_integer.$(OBJEXT) \ + data_string.$(OBJEXT) http_header.$(OBJEXT) http_kv.$(OBJEXT) \ + vector.$(OBJEXT) log.$(OBJEXT) sock_addr.$(OBJEXT) +t_test_mod_access_OBJECTS = $(am_t_test_mod_access_OBJECTS) +t_test_mod_access_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_t_test_mod_evhost_OBJECTS = t/test_mod_evhost.$(OBJEXT) \ + configfile-glue.$(OBJEXT) buffer.$(OBJEXT) array.$(OBJEXT) \ + data_config.$(OBJEXT) data_integer.$(OBJEXT) \ + data_string.$(OBJEXT) http_header.$(OBJEXT) http_kv.$(OBJEXT) \ + vector.$(OBJEXT) log.$(OBJEXT) sock_addr.$(OBJEXT) +t_test_mod_evhost_OBJECTS = $(am_t_test_mod_evhost_OBJECTS) +t_test_mod_evhost_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_t_test_mod_simple_vhost_OBJECTS = \ + t/test_mod_simple_vhost.$(OBJEXT) configfile-glue.$(OBJEXT) \ + buffer.$(OBJEXT) array.$(OBJEXT) data_config.$(OBJEXT) \ + data_integer.$(OBJEXT) data_string.$(OBJEXT) \ + http_header.$(OBJEXT) http_kv.$(OBJEXT) vector.$(OBJEXT) \ + log.$(OBJEXT) sock_addr.$(OBJEXT) +t_test_mod_simple_vhost_OBJECTS = \ + $(am_t_test_mod_simple_vhost_OBJECTS) +t_test_mod_simple_vhost_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_t_test_request_OBJECTS = t/test_request.$(OBJEXT) request.$(OBJEXT) \ + buffer.$(OBJEXT) array.$(OBJEXT) data_integer.$(OBJEXT) \ + data_string.$(OBJEXT) http_header.$(OBJEXT) http_kv.$(OBJEXT) \ + log.$(OBJEXT) sock_addr.$(OBJEXT) +t_test_request_OBJECTS = $(am_t_test_request_OBJECTS) +t_test_request_DEPENDENCIES = $(am__DEPENDENCIES_1) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/array.Po ./$(DEPDIR)/base64.Po \ + ./$(DEPDIR)/buffer.Po ./$(DEPDIR)/burl.Po \ + ./$(DEPDIR)/configfile-glue.Po ./$(DEPDIR)/data_array.Po \ + ./$(DEPDIR)/data_config.Po ./$(DEPDIR)/data_integer.Po \ + ./$(DEPDIR)/data_string.Po ./$(DEPDIR)/http_header.Po \ + ./$(DEPDIR)/http_kv.Po \ + ./$(DEPDIR)/liblightcomp_la-algo_sha1.Plo \ + ./$(DEPDIR)/liblightcomp_la-array.Plo \ + ./$(DEPDIR)/liblightcomp_la-base64.Plo \ + ./$(DEPDIR)/liblightcomp_la-buffer.Plo \ + ./$(DEPDIR)/liblightcomp_la-burl.Plo \ + ./$(DEPDIR)/liblightcomp_la-chunk.Plo \ + ./$(DEPDIR)/liblightcomp_la-configfile-glue.Plo \ + ./$(DEPDIR)/liblightcomp_la-connections-glue.Plo \ + ./$(DEPDIR)/liblightcomp_la-crc32.Plo \ + ./$(DEPDIR)/liblightcomp_la-data_array.Plo \ + ./$(DEPDIR)/liblightcomp_la-data_config.Plo \ + ./$(DEPDIR)/liblightcomp_la-data_integer.Plo \ + ./$(DEPDIR)/liblightcomp_la-data_string.Plo \ + ./$(DEPDIR)/liblightcomp_la-etag.Plo \ + ./$(DEPDIR)/liblightcomp_la-fdevent.Plo \ + ./$(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Plo \ + ./$(DEPDIR)/liblightcomp_la-fdevent_libev.Plo \ + ./$(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Plo \ + ./$(DEPDIR)/liblightcomp_la-fdevent_poll.Plo \ + ./$(DEPDIR)/liblightcomp_la-fdevent_select.Plo \ + ./$(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Plo \ + ./$(DEPDIR)/liblightcomp_la-fdevent_solaris_port.Plo \ + ./$(DEPDIR)/liblightcomp_la-gw_backend.Plo \ + ./$(DEPDIR)/liblightcomp_la-http-header-glue.Plo \ + ./$(DEPDIR)/liblightcomp_la-http_auth.Plo \ + ./$(DEPDIR)/liblightcomp_la-http_chunk.Plo \ + ./$(DEPDIR)/liblightcomp_la-http_header.Plo \ + ./$(DEPDIR)/liblightcomp_la-http_kv.Plo \ + ./$(DEPDIR)/liblightcomp_la-http_vhostdb.Plo \ + ./$(DEPDIR)/liblightcomp_la-joblist.Plo \ + ./$(DEPDIR)/liblightcomp_la-keyvalue.Plo \ + ./$(DEPDIR)/liblightcomp_la-log.Plo \ + ./$(DEPDIR)/liblightcomp_la-md5.Plo \ + ./$(DEPDIR)/liblightcomp_la-plugin.Plo \ + ./$(DEPDIR)/liblightcomp_la-rand.Plo \ + ./$(DEPDIR)/liblightcomp_la-request.Plo \ + ./$(DEPDIR)/liblightcomp_la-safe_memclear.Plo \ + ./$(DEPDIR)/liblightcomp_la-sock_addr.Plo \ + ./$(DEPDIR)/liblightcomp_la-splaytree.Plo \ + ./$(DEPDIR)/liblightcomp_la-stat_cache.Plo \ + ./$(DEPDIR)/liblightcomp_la-stream.Plo \ + ./$(DEPDIR)/liblightcomp_la-vector.Plo \ + ./$(DEPDIR)/lighttpd-algo_sha1.Po \ + ./$(DEPDIR)/lighttpd-angel.Po ./$(DEPDIR)/lighttpd-array.Po \ + ./$(DEPDIR)/lighttpd-base64.Po ./$(DEPDIR)/lighttpd-buffer.Po \ + ./$(DEPDIR)/lighttpd-burl.Po ./$(DEPDIR)/lighttpd-chunk.Po \ + ./$(DEPDIR)/lighttpd-configfile-glue.Po \ + ./$(DEPDIR)/lighttpd-configfile.Po \ + ./$(DEPDIR)/lighttpd-configparser.Po \ + ./$(DEPDIR)/lighttpd-connections-glue.Po \ + ./$(DEPDIR)/lighttpd-connections.Po \ + ./$(DEPDIR)/lighttpd-crc32.Po \ + ./$(DEPDIR)/lighttpd-data_array.Po \ + ./$(DEPDIR)/lighttpd-data_config.Po \ + ./$(DEPDIR)/lighttpd-data_integer.Po \ + ./$(DEPDIR)/lighttpd-data_string.Po \ + ./$(DEPDIR)/lighttpd-etag.Po ./$(DEPDIR)/lighttpd-fdevent.Po \ + ./$(DEPDIR)/lighttpd-fdevent_freebsd_kqueue.Po \ + ./$(DEPDIR)/lighttpd-fdevent_libev.Po \ + ./$(DEPDIR)/lighttpd-fdevent_linux_sysepoll.Po \ + ./$(DEPDIR)/lighttpd-fdevent_poll.Po \ + ./$(DEPDIR)/lighttpd-fdevent_select.Po \ + ./$(DEPDIR)/lighttpd-fdevent_solaris_devpoll.Po \ + ./$(DEPDIR)/lighttpd-fdevent_solaris_port.Po \ + ./$(DEPDIR)/lighttpd-gw_backend.Po \ + ./$(DEPDIR)/lighttpd-http-header-glue.Po \ + ./$(DEPDIR)/lighttpd-http_auth.Po \ + ./$(DEPDIR)/lighttpd-http_chunk.Po \ + ./$(DEPDIR)/lighttpd-http_header.Po \ + ./$(DEPDIR)/lighttpd-http_kv.Po \ + ./$(DEPDIR)/lighttpd-http_vhostdb.Po \ + ./$(DEPDIR)/lighttpd-inet_ntop_cache.Po \ + ./$(DEPDIR)/lighttpd-joblist.Po \ + ./$(DEPDIR)/lighttpd-keyvalue.Po ./$(DEPDIR)/lighttpd-log.Po \ + ./$(DEPDIR)/lighttpd-md5.Po ./$(DEPDIR)/lighttpd-mod_access.Po \ + ./$(DEPDIR)/lighttpd-mod_accesslog.Po \ + ./$(DEPDIR)/lighttpd-mod_alias.Po \ + ./$(DEPDIR)/lighttpd-mod_auth.Po \ + ./$(DEPDIR)/lighttpd-mod_authn_file.Po \ + ./$(DEPDIR)/lighttpd-mod_authn_gssapi.Po \ + ./$(DEPDIR)/lighttpd-mod_authn_ldap.Po \ + ./$(DEPDIR)/lighttpd-mod_authn_mysql.Po \ + ./$(DEPDIR)/lighttpd-mod_authn_pam.Po \ + ./$(DEPDIR)/lighttpd-mod_cgi.Po \ + ./$(DEPDIR)/lighttpd-mod_cml.Po \ + ./$(DEPDIR)/lighttpd-mod_cml_funcs.Po \ + ./$(DEPDIR)/lighttpd-mod_cml_lua.Po \ + ./$(DEPDIR)/lighttpd-mod_compress.Po \ + ./$(DEPDIR)/lighttpd-mod_deflate.Po \ + ./$(DEPDIR)/lighttpd-mod_dirlisting.Po \ + ./$(DEPDIR)/lighttpd-mod_evasive.Po \ + ./$(DEPDIR)/lighttpd-mod_expire.Po \ + ./$(DEPDIR)/lighttpd-mod_extforward.Po \ + ./$(DEPDIR)/lighttpd-mod_fastcgi.Po \ + ./$(DEPDIR)/lighttpd-mod_flv_streaming.Po \ + ./$(DEPDIR)/lighttpd-mod_geoip.Po \ + ./$(DEPDIR)/lighttpd-mod_indexfile.Po \ + ./$(DEPDIR)/lighttpd-mod_magnet.Po \ + ./$(DEPDIR)/lighttpd-mod_magnet_cache.Po \ + ./$(DEPDIR)/lighttpd-mod_mysql_vhost.Po \ + ./$(DEPDIR)/lighttpd-mod_openssl.Po \ + ./$(DEPDIR)/lighttpd-mod_proxy.Po \ + ./$(DEPDIR)/lighttpd-mod_redirect.Po \ + ./$(DEPDIR)/lighttpd-mod_rewrite.Po \ + ./$(DEPDIR)/lighttpd-mod_rrdtool.Po \ + ./$(DEPDIR)/lighttpd-mod_scgi.Po \ + ./$(DEPDIR)/lighttpd-mod_secdownload.Po \ + ./$(DEPDIR)/lighttpd-mod_setenv.Po \ + ./$(DEPDIR)/lighttpd-mod_simple_vhost.Po \ + ./$(DEPDIR)/lighttpd-mod_ssi.Po \ + ./$(DEPDIR)/lighttpd-mod_ssi_expr.Po \ + ./$(DEPDIR)/lighttpd-mod_ssi_exprparser.Po \ + ./$(DEPDIR)/lighttpd-mod_staticfile.Po \ + ./$(DEPDIR)/lighttpd-mod_status.Po \ + ./$(DEPDIR)/lighttpd-mod_trigger_b4_dl.Po \ + ./$(DEPDIR)/lighttpd-mod_uploadprogress.Po \ + ./$(DEPDIR)/lighttpd-mod_userdir.Po \ + ./$(DEPDIR)/lighttpd-mod_usertrack.Po \ + ./$(DEPDIR)/lighttpd-mod_vhostdb.Po \ + ./$(DEPDIR)/lighttpd-mod_vhostdb_dbi.Po \ + ./$(DEPDIR)/lighttpd-mod_vhostdb_ldap.Po \ + ./$(DEPDIR)/lighttpd-mod_vhostdb_mysql.Po \ + ./$(DEPDIR)/lighttpd-mod_vhostdb_pgsql.Po \ + ./$(DEPDIR)/lighttpd-mod_webdav.Po \ + ./$(DEPDIR)/lighttpd-network.Po \ + ./$(DEPDIR)/lighttpd-network_write.Po \ + ./$(DEPDIR)/lighttpd-plugin.Po ./$(DEPDIR)/lighttpd-rand.Po \ + ./$(DEPDIR)/lighttpd-request.Po \ + ./$(DEPDIR)/lighttpd-response.Po \ + ./$(DEPDIR)/lighttpd-safe_memclear.Po \ + ./$(DEPDIR)/lighttpd-server.Po \ + ./$(DEPDIR)/lighttpd-sock_addr.Po \ + ./$(DEPDIR)/lighttpd-splaytree.Po \ + ./$(DEPDIR)/lighttpd-stat_cache.Po \ + ./$(DEPDIR)/lighttpd-stream.Po ./$(DEPDIR)/lighttpd-vector.Po \ + ./$(DEPDIR)/log.Po ./$(DEPDIR)/mod_access.Plo \ + ./$(DEPDIR)/mod_accesslog.Plo ./$(DEPDIR)/mod_alias.Plo \ + ./$(DEPDIR)/mod_auth.Plo ./$(DEPDIR)/mod_authn_file.Plo \ + ./$(DEPDIR)/mod_authn_gssapi.Plo \ + ./$(DEPDIR)/mod_authn_ldap.Plo \ + ./$(DEPDIR)/mod_authn_mysql_la-mod_authn_mysql.Plo \ + ./$(DEPDIR)/mod_authn_pam.Plo \ + ./$(DEPDIR)/mod_authn_sasl_la-mod_authn_sasl.Plo \ + ./$(DEPDIR)/mod_cgi.Plo ./$(DEPDIR)/mod_cml_la-mod_cml.Plo \ + ./$(DEPDIR)/mod_cml_la-mod_cml_funcs.Plo \ + ./$(DEPDIR)/mod_cml_la-mod_cml_lua.Plo \ + ./$(DEPDIR)/mod_compress.Plo ./$(DEPDIR)/mod_deflate.Plo \ + ./$(DEPDIR)/mod_dirlisting.Plo ./$(DEPDIR)/mod_evasive.Plo \ + ./$(DEPDIR)/mod_evhost.Plo ./$(DEPDIR)/mod_expire.Plo \ + ./$(DEPDIR)/mod_extforward.Plo ./$(DEPDIR)/mod_fastcgi.Plo \ + ./$(DEPDIR)/mod_flv_streaming.Plo ./$(DEPDIR)/mod_geoip.Plo \ + ./$(DEPDIR)/mod_indexfile.Plo \ + ./$(DEPDIR)/mod_magnet_la-mod_magnet.Plo \ + ./$(DEPDIR)/mod_magnet_la-mod_magnet_cache.Plo \ + ./$(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Plo \ + ./$(DEPDIR)/mod_openssl.Plo ./$(DEPDIR)/mod_proxy.Plo \ + ./$(DEPDIR)/mod_redirect.Plo ./$(DEPDIR)/mod_rewrite.Plo \ + ./$(DEPDIR)/mod_rrdtool.Plo ./$(DEPDIR)/mod_scgi.Plo \ + ./$(DEPDIR)/mod_secdownload.Plo ./$(DEPDIR)/mod_setenv.Plo \ + ./$(DEPDIR)/mod_simple_vhost.Plo ./$(DEPDIR)/mod_sockproxy.Plo \ + ./$(DEPDIR)/mod_ssi.Plo ./$(DEPDIR)/mod_ssi_expr.Plo \ + ./$(DEPDIR)/mod_ssi_exprparser.Plo \ + ./$(DEPDIR)/mod_staticfile.Plo ./$(DEPDIR)/mod_status.Plo \ + ./$(DEPDIR)/mod_trigger_b4_dl.Plo \ + ./$(DEPDIR)/mod_uploadprogress.Plo ./$(DEPDIR)/mod_userdir.Plo \ + ./$(DEPDIR)/mod_usertrack.Plo ./$(DEPDIR)/mod_vhostdb.Plo \ + ./$(DEPDIR)/mod_vhostdb_dbi_la-mod_vhostdb_dbi.Plo \ + ./$(DEPDIR)/mod_vhostdb_ldap.Plo \ + ./$(DEPDIR)/mod_vhostdb_mysql_la-mod_vhostdb_mysql.Plo \ + ./$(DEPDIR)/mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.Plo \ + ./$(DEPDIR)/mod_webdav_la-mod_webdav.Plo \ + ./$(DEPDIR)/mod_wstunnel.Plo ./$(DEPDIR)/request.Po \ + ./$(DEPDIR)/sock_addr.Po ./$(DEPDIR)/vector.Po \ + t/$(DEPDIR)/test_array.Po t/$(DEPDIR)/test_base64.Po \ + t/$(DEPDIR)/test_buffer.Po t/$(DEPDIR)/test_burl.Po \ + t/$(DEPDIR)/test_configfile.Po t/$(DEPDIR)/test_keyvalue.Po \ + t/$(DEPDIR)/test_mod_access.Po t/$(DEPDIR)/test_mod_evhost.Po \ + t/$(DEPDIR)/test_mod_simple_vhost.Po \ + t/$(DEPDIR)/test_request.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(liblightcomp_la_SOURCES) $(mod_access_la_SOURCES) \ + $(mod_accesslog_la_SOURCES) $(mod_alias_la_SOURCES) \ + $(mod_auth_la_SOURCES) $(mod_authn_file_la_SOURCES) \ + $(mod_authn_gssapi_la_SOURCES) $(mod_authn_ldap_la_SOURCES) \ + $(mod_authn_mysql_la_SOURCES) $(mod_authn_pam_la_SOURCES) \ + $(mod_authn_sasl_la_SOURCES) $(mod_cgi_la_SOURCES) \ + $(mod_cml_la_SOURCES) $(mod_compress_la_SOURCES) \ + $(mod_deflate_la_SOURCES) $(mod_dirlisting_la_SOURCES) \ + $(mod_evasive_la_SOURCES) $(mod_evhost_la_SOURCES) \ + $(mod_expire_la_SOURCES) $(mod_extforward_la_SOURCES) \ + $(mod_fastcgi_la_SOURCES) $(mod_flv_streaming_la_SOURCES) \ + $(mod_geoip_la_SOURCES) $(mod_indexfile_la_SOURCES) \ + $(mod_magnet_la_SOURCES) $(mod_mysql_vhost_la_SOURCES) \ + $(mod_openssl_la_SOURCES) $(mod_proxy_la_SOURCES) \ + $(mod_redirect_la_SOURCES) $(mod_rewrite_la_SOURCES) \ + $(mod_rrdtool_la_SOURCES) $(mod_scgi_la_SOURCES) \ + $(mod_secdownload_la_SOURCES) $(mod_setenv_la_SOURCES) \ + $(mod_simple_vhost_la_SOURCES) $(mod_sockproxy_la_SOURCES) \ + $(mod_ssi_la_SOURCES) $(mod_staticfile_la_SOURCES) \ + $(mod_status_la_SOURCES) $(mod_trigger_b4_dl_la_SOURCES) \ + $(mod_uploadprogress_la_SOURCES) $(mod_userdir_la_SOURCES) \ + $(mod_usertrack_la_SOURCES) $(mod_vhostdb_la_SOURCES) \ + $(mod_vhostdb_dbi_la_SOURCES) $(mod_vhostdb_ldap_la_SOURCES) \ + $(mod_vhostdb_mysql_la_SOURCES) \ + $(mod_vhostdb_pgsql_la_SOURCES) $(mod_webdav_la_SOURCES) \ + $(mod_wstunnel_la_SOURCES) $(lighttpd_SOURCES) \ + $(lighttpd_angel_SOURCES) $(t_test_array_SOURCES) \ + $(t_test_base64_SOURCES) $(t_test_buffer_SOURCES) \ + $(t_test_burl_SOURCES) $(t_test_configfile_SOURCES) \ + $(t_test_keyvalue_SOURCES) $(t_test_mod_access_SOURCES) \ + $(t_test_mod_evhost_SOURCES) \ + $(t_test_mod_simple_vhost_SOURCES) $(t_test_request_SOURCES) +DIST_SOURCES = $(am__liblightcomp_la_SOURCES_DIST) \ + $(mod_access_la_SOURCES) $(mod_accesslog_la_SOURCES) \ + $(mod_alias_la_SOURCES) $(mod_auth_la_SOURCES) \ + $(mod_authn_file_la_SOURCES) \ + $(am__mod_authn_gssapi_la_SOURCES_DIST) \ + $(am__mod_authn_ldap_la_SOURCES_DIST) \ + $(am__mod_authn_mysql_la_SOURCES_DIST) \ + $(am__mod_authn_pam_la_SOURCES_DIST) \ + $(am__mod_authn_sasl_la_SOURCES_DIST) $(mod_cgi_la_SOURCES) \ + $(am__mod_cml_la_SOURCES_DIST) $(mod_compress_la_SOURCES) \ + $(mod_deflate_la_SOURCES) $(mod_dirlisting_la_SOURCES) \ + $(mod_evasive_la_SOURCES) $(mod_evhost_la_SOURCES) \ + $(mod_expire_la_SOURCES) $(mod_extforward_la_SOURCES) \ + $(mod_fastcgi_la_SOURCES) $(mod_flv_streaming_la_SOURCES) \ + $(am__mod_geoip_la_SOURCES_DIST) $(mod_indexfile_la_SOURCES) \ + $(am__mod_magnet_la_SOURCES_DIST) \ + $(am__mod_mysql_vhost_la_SOURCES_DIST) \ + $(am__mod_openssl_la_SOURCES_DIST) $(mod_proxy_la_SOURCES) \ + $(mod_redirect_la_SOURCES) $(mod_rewrite_la_SOURCES) \ + $(mod_rrdtool_la_SOURCES) $(mod_scgi_la_SOURCES) \ + $(mod_secdownload_la_SOURCES) $(mod_setenv_la_SOURCES) \ + $(mod_simple_vhost_la_SOURCES) $(mod_sockproxy_la_SOURCES) \ + $(mod_ssi_la_SOURCES) $(mod_staticfile_la_SOURCES) \ + $(mod_status_la_SOURCES) \ + $(am__mod_trigger_b4_dl_la_SOURCES_DIST) \ + $(mod_uploadprogress_la_SOURCES) $(mod_userdir_la_SOURCES) \ + $(mod_usertrack_la_SOURCES) $(mod_vhostdb_la_SOURCES) \ + $(am__mod_vhostdb_dbi_la_SOURCES_DIST) \ + $(am__mod_vhostdb_ldap_la_SOURCES_DIST) \ + $(am__mod_vhostdb_mysql_la_SOURCES_DIST) \ + $(am__mod_vhostdb_pgsql_la_SOURCES_DIST) \ + $(mod_webdav_la_SOURCES) $(mod_wstunnel_la_SOURCES) \ + $(am__lighttpd_SOURCES_DIST) $(lighttpd_angel_SOURCES) \ + $(t_test_array_SOURCES) $(t_test_base64_SOURCES) \ + $(t_test_buffer_SOURCES) $(t_test_burl_SOURCES) \ + $(t_test_configfile_SOURCES) $(t_test_keyvalue_SOURCES) \ + $(t_test_mod_access_SOURCES) $(t_test_mod_evhost_SOURCES) \ + $(t_test_mod_simple_vhost_SOURCES) $(t_test_request_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ATTR_LIB = @ATTR_LIB@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_EXEEXT = @BUILD_EXEEXT@ +BUILD_OBJEXT = @BUILD_OBJEXT@ +BZ_LIB = @BZ_LIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_FOR_BUILD = @CC_FOR_BUILD@ +CFLAGS = @CFLAGS@ +CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@ +CPP_FOR_BUILD = @CPP_FOR_BUILD@ +CRYPTO_LIB = @CRYPTO_LIB@ +CRYPT_LIB = @CRYPT_LIB@ +CYGPATH_W = @CYGPATH_W@ +DBI_CFLAGS = @DBI_CFLAGS@ +DBI_LIBS = @DBI_LIBS@ +DEFS = @DEFS@ -DHAVE_VERSIONSTAMP_H -DLIBRARY_DIR="\"$(libdir)\"" -DSBIN_DIR="\"$(sbindir)\"" +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DL_LIB = @DL_LIB@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FAM_CFLAGS = @FAM_CFLAGS@ +FAM_LIBS = @FAM_LIBS@ +FGREP = @FGREP@ +GDBM_LIB = @GDBM_LIB@ +GEOIP_LIB = @GEOIP_LIB@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KRB5_LIB = @KRB5_LIB@ +LBER_LIB = @LBER_LIB@ +LD = @LD@ +LDAP_LIB = @LDAP_LIB@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@ +LIBEV_CFLAGS = @LIBEV_CFLAGS@ +LIBEV_LIBS = @LIBEV_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@ +LIBUNWIND_LIBS = @LIBUNWIND_LIBS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LUA_CFLAGS = @LUA_CFLAGS@ +LUA_LIBS = @LUA_LIBS@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MEMCACHED_LIB = @MEMCACHED_LIB@ +MKDIR_P = @MKDIR_P@ +MYSQL_CFLAGS = @MYSQL_CFLAGS@ +MYSQL_CONFIG = @MYSQL_CONFIG@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PAM_LIB = @PAM_LIB@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PCRECONFIG = @PCRECONFIG@ +PCRE_LIB = @PCRE_LIB@ +PGSQL_CONFIG = @PGSQL_CONFIG@ +PGSQL_INCLUDE = @PGSQL_INCLUDE@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SASL_CFLAGS = @SASL_CFLAGS@ +SASL_LIBS = @SASL_LIBS@ +SED = @SED@ +SENDFILE_LIB = @SENDFILE_LIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SQLITE_CFLAGS = @SQLITE_CFLAGS@ +SQLITE_LIBS = @SQLITE_LIBS@ +SSL_LIB = @SSL_LIB@ +STRIP = @STRIP@ +UUID_LIBS = @UUID_LIBS@ +VERSION = @VERSION@ +XML_CFLAGS = @XML_CFLAGS@ +XML_LIBS = @XML_LIBS@ +Z_LIB = @Z_LIB@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CFLAGS = $(FAM_CFLAGS) $(LIBUNWIND_CFLAGS) +LEMON = $(top_builddir)/src/lemon$(BUILD_EXEEXT) +TESTS = \ + t/test_array$(EXEEXT) \ + t/test_buffer$(EXEEXT) \ + t/test_burl$(EXEEXT) \ + t/test_base64$(EXEEXT) \ + t/test_configfile$(EXEEXT) \ + t/test_keyvalue$(EXEEXT) \ + t/test_mod_access$(EXEEXT) \ + t/test_mod_evhost$(EXEEXT) \ + t/test_mod_simple_vhost$(EXEEXT) \ + t/test_request$(EXEEXT) + +lighttpd_angel_SOURCES = lighttpd-angel.c +BUILT_SOURCES = parsers versionstamp +MAINTAINERCLEANFILES = configparser.c configparser.h mod_ssi_exprparser.c mod_ssi_exprparser.h +CLEANFILES = versionstamp.h versionstamp.h.tmp lemon$(BUILD_EXEEXT) +common_src = base64.c buffer.c burl.c log.c \ + http_header.c http_kv.c keyvalue.c chunk.c \ + http_chunk.c stream.c fdevent.c gw_backend.c \ + stat_cache.c plugin.c joblist.c etag.c array.c \ + data_string.c data_array.c \ + data_integer.c algo_sha1.c md5.c \ + vector.c \ + fdevent_select.c fdevent_libev.c \ + fdevent_poll.c fdevent_linux_sysepoll.c \ + fdevent_solaris_devpoll.c fdevent_solaris_port.c \ + fdevent_freebsd_kqueue.c \ + data_config.c \ + crc32.c \ + connections-glue.c \ + configfile-glue.c \ + http-header-glue.c \ + http_auth.c \ + http_vhostdb.c \ + rand.c \ + request.c \ + sock_addr.c \ + splaytree.c \ + safe_memclear.c + +src = server.c response.c connections.c inet_ntop_cache.c network.c \ + network_write.c configfile.c configparser.c $(am__append_2) + +#lib_LTLIBRARIES += mod_httptls.la +#mod_httptls_la_SOURCES = mod_httptls.c +#mod_httptls_la_LDFLAGS = $(common_module_ldflags) +#mod_httptls_la_LIBADD = $(common_libadd) +lib_LTLIBRARIES = $(am__append_1) mod_flv_streaming.la $(am__append_3) \ + mod_evasive.la mod_webdav.la $(am__append_4) $(am__append_5) \ + mod_vhostdb.la $(am__append_6) $(am__append_7) $(am__append_8) \ + $(am__append_9) mod_cgi.la mod_scgi.la mod_staticfile.la \ + mod_dirlisting.la mod_indexfile.la mod_setenv.la mod_alias.la \ + mod_userdir.la mod_rrdtool.la mod_usertrack.la mod_proxy.la \ + mod_sockproxy.la mod_ssi.la mod_secdownload.la mod_expire.la \ + mod_evhost.la mod_simple_vhost.la mod_fastcgi.la \ + mod_extforward.la mod_access.la mod_compress.la mod_deflate.la \ + mod_auth.la mod_authn_file.la $(am__append_10) \ + $(am__append_11) $(am__append_12) $(am__append_13) \ + $(am__append_14) $(am__append_15) mod_rewrite.la \ + mod_redirect.la mod_status.la mod_accesslog.la \ + mod_uploadprogress.la mod_wstunnel.la +@NO_RDYNAMIC_FALSE@common_ldflags = -avoid-version + +# if the linker doesn't allow referencing symbols of the binary +# we have to put everything into a shared-lib and link it into +# everything +@NO_RDYNAMIC_TRUE@common_ldflags = -avoid-version -no-undefined +@NO_RDYNAMIC_TRUE@liblightcomp_la_SOURCES = $(common_src) +@NO_RDYNAMIC_TRUE@liblightcomp_la_CFLAGS = $(AM_CFLAGS) $(LIBEV_CFLAGS) +@NO_RDYNAMIC_TRUE@liblightcomp_la_LDFLAGS = $(common_ldflags) +@NO_RDYNAMIC_TRUE@liblightcomp_la_LIBADD = $(PCRE_LIB) $(CRYPTO_LIB) $(FAM_LIBS) $(LIBEV_LIBS) $(ATTR_LIB) +@NO_RDYNAMIC_FALSE@common_libadd = +@NO_RDYNAMIC_TRUE@common_libadd = liblightcomp.la +common_module_ldflags = -module -export-dynamic $(common_ldflags) +mod_flv_streaming_la_SOURCES = mod_flv_streaming.c +mod_flv_streaming_la_LDFLAGS = $(common_module_ldflags) +mod_flv_streaming_la_LIBADD = $(common_libadd) +@BUILD_WITH_GEOIP_TRUE@mod_geoip_la_SOURCES = mod_geoip.c +@BUILD_WITH_GEOIP_TRUE@mod_geoip_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_GEOIP_TRUE@mod_geoip_la_LIBADD = $(common_libadd) $(GEOIP_LIB) +mod_evasive_la_SOURCES = mod_evasive.c +mod_evasive_la_LDFLAGS = $(common_module_ldflags) +mod_evasive_la_LIBADD = $(common_libadd) +mod_webdav_la_SOURCES = mod_webdav.c +mod_webdav_la_CFLAGS = $(AM_CFLAGS) $(XML_CFLAGS) $(SQLITE_CFLAGS) +mod_webdav_la_LDFLAGS = $(common_module_ldflags) +mod_webdav_la_LIBADD = $(common_libadd) $(XML_LIBS) $(SQLITE_LIBS) $(UUID_LIBS) +@BUILD_WITH_LUA_TRUE@mod_magnet_la_SOURCES = mod_magnet.c mod_magnet_cache.c +@BUILD_WITH_LUA_TRUE@mod_magnet_la_CFLAGS = $(AM_CFLAGS) $(LUA_CFLAGS) +@BUILD_WITH_LUA_TRUE@mod_magnet_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_LUA_TRUE@mod_magnet_la_LIBADD = $(common_libadd) $(LUA_LIBS) -lm +@BUILD_WITH_LUA_TRUE@mod_cml_la_SOURCES = mod_cml.c mod_cml_lua.c mod_cml_funcs.c +@BUILD_WITH_LUA_TRUE@mod_cml_la_CFLAGS = $(AM_CFLAGS) $(LUA_CFLAGS) +@BUILD_WITH_LUA_TRUE@mod_cml_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_LUA_TRUE@mod_cml_la_LIBADD = $(MEMCACHED_LIB) $(common_libadd) $(LUA_LIBS) -lm +@BUILD_MOD_TRIGGER_B4_DL_TRUE@mod_trigger_b4_dl_la_SOURCES = mod_trigger_b4_dl.c +@BUILD_MOD_TRIGGER_B4_DL_TRUE@mod_trigger_b4_dl_la_LDFLAGS = $(common_module_ldflags) +@BUILD_MOD_TRIGGER_B4_DL_TRUE@mod_trigger_b4_dl_la_LIBADD = $(GDBM_LIB) $(MEMCACHED_LIB) $(PCRE_LIB) $(common_libadd) +mod_vhostdb_la_SOURCES = mod_vhostdb.c +mod_vhostdb_la_LDFLAGS = $(common_module_ldflags) +mod_vhostdb_la_LIBADD = $(common_libadd) +@BUILD_WITH_LDAP_TRUE@mod_vhostdb_ldap_la_SOURCES = mod_vhostdb_ldap.c +@BUILD_WITH_LDAP_TRUE@mod_vhostdb_ldap_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_LDAP_TRUE@mod_vhostdb_ldap_la_LIBADD = $(LDAP_LIB) $(LBER_LIB) $(common_libadd) +@BUILD_WITH_MYSQL_TRUE@mod_mysql_vhost_la_SOURCES = mod_mysql_vhost.c +@BUILD_WITH_MYSQL_TRUE@mod_mysql_vhost_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_MYSQL_TRUE@mod_mysql_vhost_la_LIBADD = $(MYSQL_LIBS) $(common_libadd) +@BUILD_WITH_MYSQL_TRUE@mod_mysql_vhost_la_CPPFLAGS = $(MYSQL_CFLAGS) +@BUILD_WITH_MYSQL_TRUE@mod_vhostdb_mysql_la_SOURCES = mod_vhostdb_mysql.c +@BUILD_WITH_MYSQL_TRUE@mod_vhostdb_mysql_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_MYSQL_TRUE@mod_vhostdb_mysql_la_LIBADD = $(MYSQL_LIBS) $(common_libadd) +@BUILD_WITH_MYSQL_TRUE@mod_vhostdb_mysql_la_CPPFLAGS = $(MYSQL_CFLAGS) +@BUILD_WITH_PGSQL_TRUE@mod_vhostdb_pgsql_la_SOURCES = mod_vhostdb_pgsql.c +@BUILD_WITH_PGSQL_TRUE@mod_vhostdb_pgsql_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_PGSQL_TRUE@mod_vhostdb_pgsql_la_LIBADD = $(PGSQL_LIBS) $(common_libadd) +@BUILD_WITH_PGSQL_TRUE@mod_vhostdb_pgsql_la_CPPFLAGS = $(PGSQL_INCLUDE) +@BUILD_WITH_DBI_TRUE@mod_vhostdb_dbi_la_SOURCES = mod_vhostdb_dbi.c +@BUILD_WITH_DBI_TRUE@mod_vhostdb_dbi_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_DBI_TRUE@mod_vhostdb_dbi_la_LIBADD = $(DBI_LIBS) $(common_libadd) +@BUILD_WITH_DBI_TRUE@mod_vhostdb_dbi_la_CPPFLAGS = $(DBI_CFLAGS) +mod_cgi_la_SOURCES = mod_cgi.c +mod_cgi_la_LDFLAGS = $(common_module_ldflags) +mod_cgi_la_LIBADD = $(common_libadd) +mod_scgi_la_SOURCES = mod_scgi.c +mod_scgi_la_LDFLAGS = $(common_module_ldflags) +mod_scgi_la_LIBADD = $(common_libadd) +mod_staticfile_la_SOURCES = mod_staticfile.c +mod_staticfile_la_LDFLAGS = $(common_module_ldflags) +mod_staticfile_la_LIBADD = $(common_libadd) +mod_dirlisting_la_SOURCES = mod_dirlisting.c +mod_dirlisting_la_LDFLAGS = $(common_module_ldflags) +mod_dirlisting_la_LIBADD = $(common_libadd) $(PCRE_LIB) +mod_indexfile_la_SOURCES = mod_indexfile.c +mod_indexfile_la_LDFLAGS = $(common_module_ldflags) +mod_indexfile_la_LIBADD = $(common_libadd) +mod_setenv_la_SOURCES = mod_setenv.c +mod_setenv_la_LDFLAGS = $(common_module_ldflags) +mod_setenv_la_LIBADD = $(common_libadd) +mod_alias_la_SOURCES = mod_alias.c +mod_alias_la_LDFLAGS = $(common_module_ldflags) +mod_alias_la_LIBADD = $(common_libadd) +mod_userdir_la_SOURCES = mod_userdir.c +mod_userdir_la_LDFLAGS = $(common_module_ldflags) +mod_userdir_la_LIBADD = $(common_libadd) +mod_rrdtool_la_SOURCES = mod_rrdtool.c +mod_rrdtool_la_LDFLAGS = $(common_module_ldflags) +mod_rrdtool_la_LIBADD = $(common_libadd) +mod_usertrack_la_SOURCES = mod_usertrack.c +mod_usertrack_la_LDFLAGS = $(common_module_ldflags) +mod_usertrack_la_LIBADD = $(common_libadd) +mod_proxy_la_SOURCES = mod_proxy.c +mod_proxy_la_LDFLAGS = $(common_module_ldflags) +mod_proxy_la_LIBADD = $(common_libadd) +mod_sockproxy_la_SOURCES = mod_sockproxy.c +mod_sockproxy_la_LDFLAGS = $(common_module_ldflags) +mod_sockproxy_la_LIBADD = $(common_libadd) +mod_ssi_la_SOURCES = mod_ssi_exprparser.c mod_ssi_expr.c mod_ssi.c +mod_ssi_la_LDFLAGS = $(common_module_ldflags) +mod_ssi_la_LIBADD = $(common_libadd) +mod_secdownload_la_SOURCES = mod_secdownload.c +mod_secdownload_la_LDFLAGS = $(common_module_ldflags) +mod_secdownload_la_LIBADD = $(common_libadd) $(CRYPTO_LIB) +mod_expire_la_SOURCES = mod_expire.c +mod_expire_la_LDFLAGS = $(common_module_ldflags) +mod_expire_la_LIBADD = $(common_libadd) +mod_evhost_la_SOURCES = mod_evhost.c +mod_evhost_la_LDFLAGS = $(common_module_ldflags) +mod_evhost_la_LIBADD = $(common_libadd) +mod_simple_vhost_la_SOURCES = mod_simple_vhost.c +mod_simple_vhost_la_LDFLAGS = $(common_module_ldflags) +mod_simple_vhost_la_LIBADD = $(common_libadd) +mod_fastcgi_la_SOURCES = mod_fastcgi.c +mod_fastcgi_la_LDFLAGS = $(common_module_ldflags) +mod_fastcgi_la_LIBADD = $(common_libadd) +mod_extforward_la_SOURCES = mod_extforward.c +mod_extforward_la_LDFLAGS = $(common_module_ldflags) +mod_extforward_la_LIBADD = $(common_libadd) +mod_access_la_SOURCES = mod_access.c +mod_access_la_LDFLAGS = $(common_module_ldflags) +mod_access_la_LIBADD = $(common_libadd) +mod_compress_la_SOURCES = mod_compress.c +mod_compress_la_LDFLAGS = $(common_module_ldflags) +mod_compress_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd) +mod_deflate_la_SOURCES = mod_deflate.c +mod_deflate_la_LDFLAGS = $(common_module_ldflags) +mod_deflate_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd) +mod_auth_la_SOURCES = mod_auth.c +mod_auth_la_LDFLAGS = $(common_module_ldflags) +mod_auth_la_LIBADD = $(common_libadd) +mod_authn_file_la_SOURCES = mod_authn_file.c +mod_authn_file_la_LDFLAGS = $(common_module_ldflags) +mod_authn_file_la_LIBADD = $(CRYPT_LIB) $(CRYPTO_LIB) $(common_libadd) +@BUILD_WITH_KRB5_TRUE@mod_authn_gssapi_la_SOURCES = mod_authn_gssapi.c +@BUILD_WITH_KRB5_TRUE@mod_authn_gssapi_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_KRB5_TRUE@mod_authn_gssapi_la_LIBADD = $(KRB5_LIB) $(common_libadd) +@BUILD_WITH_LDAP_TRUE@mod_authn_ldap_la_SOURCES = mod_authn_ldap.c +@BUILD_WITH_LDAP_TRUE@mod_authn_ldap_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_LDAP_TRUE@mod_authn_ldap_la_LIBADD = $(LDAP_LIB) $(LBER_LIB) $(common_libadd) +@BUILD_WITH_PAM_TRUE@mod_authn_pam_la_SOURCES = mod_authn_pam.c +@BUILD_WITH_PAM_TRUE@mod_authn_pam_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_PAM_TRUE@mod_authn_pam_la_LIBADD = $(PAM_LIB) $(common_libadd) +@BUILD_WITH_MYSQL_TRUE@mod_authn_mysql_la_SOURCES = mod_authn_mysql.c +@BUILD_WITH_MYSQL_TRUE@mod_authn_mysql_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_MYSQL_TRUE@mod_authn_mysql_la_LIBADD = $(CRYPT_LIB) $(MYSQL_LIBS) $(common_libadd) +@BUILD_WITH_MYSQL_TRUE@mod_authn_mysql_la_CPPFLAGS = $(MYSQL_CFLAGS) +@BUILD_WITH_SASL_TRUE@mod_authn_sasl_la_SOURCES = mod_authn_sasl.c +@BUILD_WITH_SASL_TRUE@mod_authn_sasl_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_SASL_TRUE@mod_authn_sasl_la_LIBADD = $(SASL_LIBS) $(common_libadd) +@BUILD_WITH_SASL_TRUE@mod_authn_sasl_la_CPPFLAGS = $(SASL_CFLAGS) +@BUILD_WITH_OPENSSL_TRUE@mod_openssl_la_SOURCES = mod_openssl.c +@BUILD_WITH_OPENSSL_TRUE@mod_openssl_la_LDFLAGS = $(common_module_ldflags) +@BUILD_WITH_OPENSSL_TRUE@mod_openssl_la_LIBADD = $(SSL_LIB) $(common_libadd) +mod_rewrite_la_SOURCES = mod_rewrite.c +mod_rewrite_la_LDFLAGS = $(common_module_ldflags) +mod_rewrite_la_LIBADD = $(PCRE_LIB) $(common_libadd) +mod_redirect_la_SOURCES = mod_redirect.c +mod_redirect_la_LDFLAGS = $(common_module_ldflags) +mod_redirect_la_LIBADD = $(PCRE_LIB) $(common_libadd) +mod_status_la_SOURCES = mod_status.c +mod_status_la_LDFLAGS = $(common_module_ldflags) +mod_status_la_LIBADD = $(common_libadd) +mod_accesslog_la_SOURCES = mod_accesslog.c +mod_accesslog_la_LDFLAGS = $(common_module_ldflags) +mod_accesslog_la_LIBADD = $(common_libadd) +mod_uploadprogress_la_SOURCES = mod_uploadprogress.c +mod_uploadprogress_la_LDFLAGS = $(common_module_ldflags) +mod_uploadprogress_la_LIBADD = $(common_libadd) +mod_wstunnel_la_SOURCES = mod_wstunnel.c +mod_wstunnel_la_LDFLAGS = $(common_module_ldflags) +mod_wstunnel_la_LIBADD = $(common_libadd) $(CRYPTO_LIB) +hdr = server.h base64.h buffer.h burl.h network.h log.h http_kv.h keyvalue.h \ + response.h request.h fastcgi.h chunk.h \ + first.h settings.h http_chunk.h \ + algo_sha1.h md5.h http_auth.h http_header.h http_vhostdb.h stream.h \ + fdevent.h gw_backend.h connections.h base.h base_decls.h stat_cache.h \ + plugin.h \ + etag.h joblist.h array.h vector.h crc32.h \ + fdevent_impl.h network_write.h configfile.h \ + mod_ssi.h mod_ssi_expr.h inet_ntop_cache.h \ + configparser.h mod_ssi_exprparser.h \ + rand.h \ + sys-crypto.h sys-endian.h sys-mmap.h sys-socket.h sys-strings.h \ + mod_cml.h mod_cml_funcs.h \ + safe_memclear.h sock_addr.h splaytree.h status_counter.h \ + mod_magnet_cache.h + +@LIGHTTPD_STATIC_FALSE@lighttpd_SOURCES = $(src) +@LIGHTTPD_STATIC_TRUE@lighttpd_SOURCES = $(src) mod_access.c \ +@LIGHTTPD_STATIC_TRUE@ mod_accesslog.c mod_alias.c mod_auth.c \ +@LIGHTTPD_STATIC_TRUE@ mod_authn_file.c mod_cgi.c \ +@LIGHTTPD_STATIC_TRUE@ mod_compress.c mod_deflate.c \ +@LIGHTTPD_STATIC_TRUE@ mod_dirlisting.c mod_evasive.c \ +@LIGHTTPD_STATIC_TRUE@ mod_expire.c mod_extforward.c \ +@LIGHTTPD_STATIC_TRUE@ mod_fastcgi.c mod_flv_streaming.c \ +@LIGHTTPD_STATIC_TRUE@ mod_indexfile.c mod_proxy.c \ +@LIGHTTPD_STATIC_TRUE@ mod_redirect.c mod_rewrite.c \ +@LIGHTTPD_STATIC_TRUE@ mod_rrdtool.c mod_scgi.c \ +@LIGHTTPD_STATIC_TRUE@ mod_secdownload.c mod_setenv.c \ +@LIGHTTPD_STATIC_TRUE@ mod_simple_vhost.c mod_ssi_exprparser.c \ +@LIGHTTPD_STATIC_TRUE@ mod_ssi_expr.c mod_ssi.c \ +@LIGHTTPD_STATIC_TRUE@ mod_staticfile.c mod_status.c \ +@LIGHTTPD_STATIC_TRUE@ mod_uploadprogress.c mod_userdir.c \ +@LIGHTTPD_STATIC_TRUE@ mod_usertrack.c mod_vhostdb.c \ +@LIGHTTPD_STATIC_TRUE@ mod_webdav.c $(am__append_16) \ +@LIGHTTPD_STATIC_TRUE@ $(am__append_18) $(am__append_21) \ +@LIGHTTPD_STATIC_TRUE@ $(am__append_23) $(am__append_25) \ +@LIGHTTPD_STATIC_TRUE@ $(am__append_27) $(am__append_30) \ +@LIGHTTPD_STATIC_TRUE@ $(am__append_33) $(am__append_36) \ +@LIGHTTPD_STATIC_TRUE@ $(am__append_41) +@LIGHTTPD_STATIC_FALSE@lighttpd_CPPFLAGS = $(FAM_CFLAGS) $(LIBEV_CFLAGS) +@LIGHTTPD_STATIC_TRUE@lighttpd_CPPFLAGS = -DLIGHTTPD_STATIC \ +@LIGHTTPD_STATIC_TRUE@ $(XML_CFLAGS) $(SQLITE_CFLAGS) \ +@LIGHTTPD_STATIC_TRUE@ $(FAM_CFLAGS) $(LIBEV_CFLAGS) \ +@LIGHTTPD_STATIC_TRUE@ $(LIBUNWIND_CFLAGS) $(am__append_19) \ +@LIGHTTPD_STATIC_TRUE@ $(am__append_28) $(am__append_31) \ +@LIGHTTPD_STATIC_TRUE@ $(am__append_34) $(am__append_38) +@LIGHTTPD_STATIC_FALSE@lighttpd_LDADD = $(PCRE_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) $(common_libadd) $(CRYPTO_LIB) $(FAM_LIBS) $(LIBEV_LIBS) $(LIBUNWIND_LIBS) +@LIGHTTPD_STATIC_TRUE@lighttpd_LDADD = $(common_libadd) $(CRYPT_LIB) \ +@LIGHTTPD_STATIC_TRUE@ $(CRYPTO_LIB) $(XML_LIBS) $(SQLITE_LIBS) \ +@LIGHTTPD_STATIC_TRUE@ $(UUID_LIBS) $(PCRE_LIB) $(Z_LIB) \ +@LIGHTTPD_STATIC_TRUE@ $(BZ_LIB) $(DL_LIB) $(SENDFILE_LIB) \ +@LIGHTTPD_STATIC_TRUE@ $(ATTR_LIB) $(FAM_LIBS) $(LIBEV_LIBS) \ +@LIGHTTPD_STATIC_TRUE@ $(LIBUNWIND_LIBS) $(am__append_17) \ +@LIGHTTPD_STATIC_TRUE@ $(am__append_20) $(am__append_22) \ +@LIGHTTPD_STATIC_TRUE@ $(am__append_24) $(am__append_26) \ +@LIGHTTPD_STATIC_TRUE@ $(am__append_29) $(am__append_32) \ +@LIGHTTPD_STATIC_TRUE@ $(am__append_35) $(am__append_37) \ +@LIGHTTPD_STATIC_TRUE@ $(am__append_39) $(am__append_40) +@LIGHTTPD_STATIC_FALSE@lighttpd_LDFLAGS = -export-dynamic +@LIGHTTPD_STATIC_TRUE@lighttpd_LDFLAGS = -export-dynamic +t_test_array_SOURCES = t/test_array.c array.c data_array.c data_integer.c data_string.c buffer.c +t_test_array_LDADD = $(LIBUNWIND_LIBS) +t_test_buffer_SOURCES = t/test_buffer.c buffer.c +t_test_buffer_LDADD = $(LIBUNWIND_LIBS) +t_test_base64_SOURCES = t/test_base64.c base64.c buffer.c +t_test_base64_LDADD = $(LIBUNWIND_LIBS) +t_test_burl_SOURCES = t/test_burl.c burl.c buffer.c base64.c +t_test_burl_LDADD = $(LIBUNWIND_LIBS) +t_test_configfile_SOURCES = t/test_configfile.c buffer.c array.c data_config.c data_integer.c data_string.c http_header.c http_kv.c vector.c log.c sock_addr.c +t_test_configfile_LDADD = $(PCRE_LIB) $(LIBUNWIND_LIBS) +t_test_keyvalue_SOURCES = t/test_keyvalue.c burl.c buffer.c base64.c array.c data_integer.c data_string.c log.c +t_test_keyvalue_LDADD = $(PCRE_LIB) $(LIBUNWIND_LIBS) +t_test_mod_access_SOURCES = t/test_mod_access.c configfile-glue.c buffer.c array.c data_config.c data_integer.c data_string.c http_header.c http_kv.c vector.c log.c sock_addr.c +t_test_mod_access_LDADD = $(PCRE_LIB) $(LIBUNWIND_LIBS) +t_test_mod_evhost_SOURCES = t/test_mod_evhost.c configfile-glue.c buffer.c array.c data_config.c data_integer.c data_string.c http_header.c http_kv.c vector.c log.c sock_addr.c +t_test_mod_evhost_LDADD = $(PCRE_LIB) $(LIBUNWIND_LIBS) +t_test_mod_simple_vhost_SOURCES = t/test_mod_simple_vhost.c configfile-glue.c buffer.c array.c data_config.c data_integer.c data_string.c http_header.c http_kv.c vector.c log.c sock_addr.c +t_test_mod_simple_vhost_LDADD = $(PCRE_LIB) $(LIBUNWIND_LIBS) +t_test_request_SOURCES = t/test_request.c request.c buffer.c array.c data_integer.c data_string.c http_header.c http_kv.c log.c sock_addr.c +t_test_request_LDADD = $(LIBUNWIND_LIBS) +noinst_HEADERS = $(hdr) +EXTRA_DIST = \ + mod_skeleton.c \ + configparser.y \ + mod_ssi_exprparser.y \ + lemon.c \ + lempar.c \ + SConscript \ + CMakeLists.txt config.h.cmake \ + meson.build + +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +liblightcomp.la: $(liblightcomp_la_OBJECTS) $(liblightcomp_la_DEPENDENCIES) $(EXTRA_liblightcomp_la_DEPENDENCIES) + $(AM_V_CCLD)$(liblightcomp_la_LINK) $(am_liblightcomp_la_rpath) $(liblightcomp_la_OBJECTS) $(liblightcomp_la_LIBADD) $(LIBS) + +mod_access.la: $(mod_access_la_OBJECTS) $(mod_access_la_DEPENDENCIES) $(EXTRA_mod_access_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_access_la_LINK) -rpath $(libdir) $(mod_access_la_OBJECTS) $(mod_access_la_LIBADD) $(LIBS) + +mod_accesslog.la: $(mod_accesslog_la_OBJECTS) $(mod_accesslog_la_DEPENDENCIES) $(EXTRA_mod_accesslog_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_accesslog_la_LINK) -rpath $(libdir) $(mod_accesslog_la_OBJECTS) $(mod_accesslog_la_LIBADD) $(LIBS) + +mod_alias.la: $(mod_alias_la_OBJECTS) $(mod_alias_la_DEPENDENCIES) $(EXTRA_mod_alias_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_alias_la_LINK) -rpath $(libdir) $(mod_alias_la_OBJECTS) $(mod_alias_la_LIBADD) $(LIBS) + +mod_auth.la: $(mod_auth_la_OBJECTS) $(mod_auth_la_DEPENDENCIES) $(EXTRA_mod_auth_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_auth_la_LINK) -rpath $(libdir) $(mod_auth_la_OBJECTS) $(mod_auth_la_LIBADD) $(LIBS) + +mod_authn_file.la: $(mod_authn_file_la_OBJECTS) $(mod_authn_file_la_DEPENDENCIES) $(EXTRA_mod_authn_file_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_authn_file_la_LINK) -rpath $(libdir) $(mod_authn_file_la_OBJECTS) $(mod_authn_file_la_LIBADD) $(LIBS) + +mod_authn_gssapi.la: $(mod_authn_gssapi_la_OBJECTS) $(mod_authn_gssapi_la_DEPENDENCIES) $(EXTRA_mod_authn_gssapi_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_authn_gssapi_la_LINK) $(am_mod_authn_gssapi_la_rpath) $(mod_authn_gssapi_la_OBJECTS) $(mod_authn_gssapi_la_LIBADD) $(LIBS) + +mod_authn_ldap.la: $(mod_authn_ldap_la_OBJECTS) $(mod_authn_ldap_la_DEPENDENCIES) $(EXTRA_mod_authn_ldap_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_authn_ldap_la_LINK) $(am_mod_authn_ldap_la_rpath) $(mod_authn_ldap_la_OBJECTS) $(mod_authn_ldap_la_LIBADD) $(LIBS) + +mod_authn_mysql.la: $(mod_authn_mysql_la_OBJECTS) $(mod_authn_mysql_la_DEPENDENCIES) $(EXTRA_mod_authn_mysql_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_authn_mysql_la_LINK) $(am_mod_authn_mysql_la_rpath) $(mod_authn_mysql_la_OBJECTS) $(mod_authn_mysql_la_LIBADD) $(LIBS) + +mod_authn_pam.la: $(mod_authn_pam_la_OBJECTS) $(mod_authn_pam_la_DEPENDENCIES) $(EXTRA_mod_authn_pam_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_authn_pam_la_LINK) $(am_mod_authn_pam_la_rpath) $(mod_authn_pam_la_OBJECTS) $(mod_authn_pam_la_LIBADD) $(LIBS) + +mod_authn_sasl.la: $(mod_authn_sasl_la_OBJECTS) $(mod_authn_sasl_la_DEPENDENCIES) $(EXTRA_mod_authn_sasl_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_authn_sasl_la_LINK) $(am_mod_authn_sasl_la_rpath) $(mod_authn_sasl_la_OBJECTS) $(mod_authn_sasl_la_LIBADD) $(LIBS) + +mod_cgi.la: $(mod_cgi_la_OBJECTS) $(mod_cgi_la_DEPENDENCIES) $(EXTRA_mod_cgi_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_cgi_la_LINK) -rpath $(libdir) $(mod_cgi_la_OBJECTS) $(mod_cgi_la_LIBADD) $(LIBS) + +mod_cml.la: $(mod_cml_la_OBJECTS) $(mod_cml_la_DEPENDENCIES) $(EXTRA_mod_cml_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_cml_la_LINK) $(am_mod_cml_la_rpath) $(mod_cml_la_OBJECTS) $(mod_cml_la_LIBADD) $(LIBS) + +mod_compress.la: $(mod_compress_la_OBJECTS) $(mod_compress_la_DEPENDENCIES) $(EXTRA_mod_compress_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_compress_la_LINK) -rpath $(libdir) $(mod_compress_la_OBJECTS) $(mod_compress_la_LIBADD) $(LIBS) + +mod_deflate.la: $(mod_deflate_la_OBJECTS) $(mod_deflate_la_DEPENDENCIES) $(EXTRA_mod_deflate_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_deflate_la_LINK) -rpath $(libdir) $(mod_deflate_la_OBJECTS) $(mod_deflate_la_LIBADD) $(LIBS) + +mod_dirlisting.la: $(mod_dirlisting_la_OBJECTS) $(mod_dirlisting_la_DEPENDENCIES) $(EXTRA_mod_dirlisting_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_dirlisting_la_LINK) -rpath $(libdir) $(mod_dirlisting_la_OBJECTS) $(mod_dirlisting_la_LIBADD) $(LIBS) + +mod_evasive.la: $(mod_evasive_la_OBJECTS) $(mod_evasive_la_DEPENDENCIES) $(EXTRA_mod_evasive_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_evasive_la_LINK) -rpath $(libdir) $(mod_evasive_la_OBJECTS) $(mod_evasive_la_LIBADD) $(LIBS) + +mod_evhost.la: $(mod_evhost_la_OBJECTS) $(mod_evhost_la_DEPENDENCIES) $(EXTRA_mod_evhost_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_evhost_la_LINK) -rpath $(libdir) $(mod_evhost_la_OBJECTS) $(mod_evhost_la_LIBADD) $(LIBS) + +mod_expire.la: $(mod_expire_la_OBJECTS) $(mod_expire_la_DEPENDENCIES) $(EXTRA_mod_expire_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_expire_la_LINK) -rpath $(libdir) $(mod_expire_la_OBJECTS) $(mod_expire_la_LIBADD) $(LIBS) + +mod_extforward.la: $(mod_extforward_la_OBJECTS) $(mod_extforward_la_DEPENDENCIES) $(EXTRA_mod_extforward_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_extforward_la_LINK) -rpath $(libdir) $(mod_extforward_la_OBJECTS) $(mod_extforward_la_LIBADD) $(LIBS) + +mod_fastcgi.la: $(mod_fastcgi_la_OBJECTS) $(mod_fastcgi_la_DEPENDENCIES) $(EXTRA_mod_fastcgi_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_fastcgi_la_LINK) -rpath $(libdir) $(mod_fastcgi_la_OBJECTS) $(mod_fastcgi_la_LIBADD) $(LIBS) + +mod_flv_streaming.la: $(mod_flv_streaming_la_OBJECTS) $(mod_flv_streaming_la_DEPENDENCIES) $(EXTRA_mod_flv_streaming_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_flv_streaming_la_LINK) -rpath $(libdir) $(mod_flv_streaming_la_OBJECTS) $(mod_flv_streaming_la_LIBADD) $(LIBS) + +mod_geoip.la: $(mod_geoip_la_OBJECTS) $(mod_geoip_la_DEPENDENCIES) $(EXTRA_mod_geoip_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_geoip_la_LINK) $(am_mod_geoip_la_rpath) $(mod_geoip_la_OBJECTS) $(mod_geoip_la_LIBADD) $(LIBS) + +mod_indexfile.la: $(mod_indexfile_la_OBJECTS) $(mod_indexfile_la_DEPENDENCIES) $(EXTRA_mod_indexfile_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_indexfile_la_LINK) -rpath $(libdir) $(mod_indexfile_la_OBJECTS) $(mod_indexfile_la_LIBADD) $(LIBS) + +mod_magnet.la: $(mod_magnet_la_OBJECTS) $(mod_magnet_la_DEPENDENCIES) $(EXTRA_mod_magnet_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_magnet_la_LINK) $(am_mod_magnet_la_rpath) $(mod_magnet_la_OBJECTS) $(mod_magnet_la_LIBADD) $(LIBS) + +mod_mysql_vhost.la: $(mod_mysql_vhost_la_OBJECTS) $(mod_mysql_vhost_la_DEPENDENCIES) $(EXTRA_mod_mysql_vhost_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_mysql_vhost_la_LINK) $(am_mod_mysql_vhost_la_rpath) $(mod_mysql_vhost_la_OBJECTS) $(mod_mysql_vhost_la_LIBADD) $(LIBS) + +mod_openssl.la: $(mod_openssl_la_OBJECTS) $(mod_openssl_la_DEPENDENCIES) $(EXTRA_mod_openssl_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_openssl_la_LINK) $(am_mod_openssl_la_rpath) $(mod_openssl_la_OBJECTS) $(mod_openssl_la_LIBADD) $(LIBS) + +mod_proxy.la: $(mod_proxy_la_OBJECTS) $(mod_proxy_la_DEPENDENCIES) $(EXTRA_mod_proxy_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_proxy_la_LINK) -rpath $(libdir) $(mod_proxy_la_OBJECTS) $(mod_proxy_la_LIBADD) $(LIBS) + +mod_redirect.la: $(mod_redirect_la_OBJECTS) $(mod_redirect_la_DEPENDENCIES) $(EXTRA_mod_redirect_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_redirect_la_LINK) -rpath $(libdir) $(mod_redirect_la_OBJECTS) $(mod_redirect_la_LIBADD) $(LIBS) + +mod_rewrite.la: $(mod_rewrite_la_OBJECTS) $(mod_rewrite_la_DEPENDENCIES) $(EXTRA_mod_rewrite_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_rewrite_la_LINK) -rpath $(libdir) $(mod_rewrite_la_OBJECTS) $(mod_rewrite_la_LIBADD) $(LIBS) + +mod_rrdtool.la: $(mod_rrdtool_la_OBJECTS) $(mod_rrdtool_la_DEPENDENCIES) $(EXTRA_mod_rrdtool_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_rrdtool_la_LINK) -rpath $(libdir) $(mod_rrdtool_la_OBJECTS) $(mod_rrdtool_la_LIBADD) $(LIBS) + +mod_scgi.la: $(mod_scgi_la_OBJECTS) $(mod_scgi_la_DEPENDENCIES) $(EXTRA_mod_scgi_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_scgi_la_LINK) -rpath $(libdir) $(mod_scgi_la_OBJECTS) $(mod_scgi_la_LIBADD) $(LIBS) + +mod_secdownload.la: $(mod_secdownload_la_OBJECTS) $(mod_secdownload_la_DEPENDENCIES) $(EXTRA_mod_secdownload_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_secdownload_la_LINK) -rpath $(libdir) $(mod_secdownload_la_OBJECTS) $(mod_secdownload_la_LIBADD) $(LIBS) + +mod_setenv.la: $(mod_setenv_la_OBJECTS) $(mod_setenv_la_DEPENDENCIES) $(EXTRA_mod_setenv_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_setenv_la_LINK) -rpath $(libdir) $(mod_setenv_la_OBJECTS) $(mod_setenv_la_LIBADD) $(LIBS) + +mod_simple_vhost.la: $(mod_simple_vhost_la_OBJECTS) $(mod_simple_vhost_la_DEPENDENCIES) $(EXTRA_mod_simple_vhost_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_simple_vhost_la_LINK) -rpath $(libdir) $(mod_simple_vhost_la_OBJECTS) $(mod_simple_vhost_la_LIBADD) $(LIBS) + +mod_sockproxy.la: $(mod_sockproxy_la_OBJECTS) $(mod_sockproxy_la_DEPENDENCIES) $(EXTRA_mod_sockproxy_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_sockproxy_la_LINK) -rpath $(libdir) $(mod_sockproxy_la_OBJECTS) $(mod_sockproxy_la_LIBADD) $(LIBS) + +mod_ssi.la: $(mod_ssi_la_OBJECTS) $(mod_ssi_la_DEPENDENCIES) $(EXTRA_mod_ssi_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_ssi_la_LINK) -rpath $(libdir) $(mod_ssi_la_OBJECTS) $(mod_ssi_la_LIBADD) $(LIBS) + +mod_staticfile.la: $(mod_staticfile_la_OBJECTS) $(mod_staticfile_la_DEPENDENCIES) $(EXTRA_mod_staticfile_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_staticfile_la_LINK) -rpath $(libdir) $(mod_staticfile_la_OBJECTS) $(mod_staticfile_la_LIBADD) $(LIBS) + +mod_status.la: $(mod_status_la_OBJECTS) $(mod_status_la_DEPENDENCIES) $(EXTRA_mod_status_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_status_la_LINK) -rpath $(libdir) $(mod_status_la_OBJECTS) $(mod_status_la_LIBADD) $(LIBS) + +mod_trigger_b4_dl.la: $(mod_trigger_b4_dl_la_OBJECTS) $(mod_trigger_b4_dl_la_DEPENDENCIES) $(EXTRA_mod_trigger_b4_dl_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_trigger_b4_dl_la_LINK) $(am_mod_trigger_b4_dl_la_rpath) $(mod_trigger_b4_dl_la_OBJECTS) $(mod_trigger_b4_dl_la_LIBADD) $(LIBS) + +mod_uploadprogress.la: $(mod_uploadprogress_la_OBJECTS) $(mod_uploadprogress_la_DEPENDENCIES) $(EXTRA_mod_uploadprogress_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_uploadprogress_la_LINK) -rpath $(libdir) $(mod_uploadprogress_la_OBJECTS) $(mod_uploadprogress_la_LIBADD) $(LIBS) + +mod_userdir.la: $(mod_userdir_la_OBJECTS) $(mod_userdir_la_DEPENDENCIES) $(EXTRA_mod_userdir_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_userdir_la_LINK) -rpath $(libdir) $(mod_userdir_la_OBJECTS) $(mod_userdir_la_LIBADD) $(LIBS) + +mod_usertrack.la: $(mod_usertrack_la_OBJECTS) $(mod_usertrack_la_DEPENDENCIES) $(EXTRA_mod_usertrack_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_usertrack_la_LINK) -rpath $(libdir) $(mod_usertrack_la_OBJECTS) $(mod_usertrack_la_LIBADD) $(LIBS) + +mod_vhostdb.la: $(mod_vhostdb_la_OBJECTS) $(mod_vhostdb_la_DEPENDENCIES) $(EXTRA_mod_vhostdb_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_vhostdb_la_LINK) -rpath $(libdir) $(mod_vhostdb_la_OBJECTS) $(mod_vhostdb_la_LIBADD) $(LIBS) + +mod_vhostdb_dbi.la: $(mod_vhostdb_dbi_la_OBJECTS) $(mod_vhostdb_dbi_la_DEPENDENCIES) $(EXTRA_mod_vhostdb_dbi_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_vhostdb_dbi_la_LINK) $(am_mod_vhostdb_dbi_la_rpath) $(mod_vhostdb_dbi_la_OBJECTS) $(mod_vhostdb_dbi_la_LIBADD) $(LIBS) + +mod_vhostdb_ldap.la: $(mod_vhostdb_ldap_la_OBJECTS) $(mod_vhostdb_ldap_la_DEPENDENCIES) $(EXTRA_mod_vhostdb_ldap_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_vhostdb_ldap_la_LINK) $(am_mod_vhostdb_ldap_la_rpath) $(mod_vhostdb_ldap_la_OBJECTS) $(mod_vhostdb_ldap_la_LIBADD) $(LIBS) + +mod_vhostdb_mysql.la: $(mod_vhostdb_mysql_la_OBJECTS) $(mod_vhostdb_mysql_la_DEPENDENCIES) $(EXTRA_mod_vhostdb_mysql_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_vhostdb_mysql_la_LINK) $(am_mod_vhostdb_mysql_la_rpath) $(mod_vhostdb_mysql_la_OBJECTS) $(mod_vhostdb_mysql_la_LIBADD) $(LIBS) + +mod_vhostdb_pgsql.la: $(mod_vhostdb_pgsql_la_OBJECTS) $(mod_vhostdb_pgsql_la_DEPENDENCIES) $(EXTRA_mod_vhostdb_pgsql_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_vhostdb_pgsql_la_LINK) $(am_mod_vhostdb_pgsql_la_rpath) $(mod_vhostdb_pgsql_la_OBJECTS) $(mod_vhostdb_pgsql_la_LIBADD) $(LIBS) + +mod_webdav.la: $(mod_webdav_la_OBJECTS) $(mod_webdav_la_DEPENDENCIES) $(EXTRA_mod_webdav_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_webdav_la_LINK) -rpath $(libdir) $(mod_webdav_la_OBJECTS) $(mod_webdav_la_LIBADD) $(LIBS) + +mod_wstunnel.la: $(mod_wstunnel_la_OBJECTS) $(mod_wstunnel_la_DEPENDENCIES) $(EXTRA_mod_wstunnel_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_wstunnel_la_LINK) -rpath $(libdir) $(mod_wstunnel_la_OBJECTS) $(mod_wstunnel_la_LIBADD) $(LIBS) + +lighttpd$(EXEEXT): $(lighttpd_OBJECTS) $(lighttpd_DEPENDENCIES) $(EXTRA_lighttpd_DEPENDENCIES) + @rm -f lighttpd$(EXEEXT) + $(AM_V_CCLD)$(lighttpd_LINK) $(lighttpd_OBJECTS) $(lighttpd_LDADD) $(LIBS) + +lighttpd-angel$(EXEEXT): $(lighttpd_angel_OBJECTS) $(lighttpd_angel_DEPENDENCIES) $(EXTRA_lighttpd_angel_DEPENDENCIES) + @rm -f lighttpd-angel$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(lighttpd_angel_OBJECTS) $(lighttpd_angel_LDADD) $(LIBS) +t/$(am__dirstamp): + @$(MKDIR_P) t + @: > t/$(am__dirstamp) +t/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) t/$(DEPDIR) + @: > t/$(DEPDIR)/$(am__dirstamp) +t/test_array.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/test_array$(EXEEXT): $(t_test_array_OBJECTS) $(t_test_array_DEPENDENCIES) $(EXTRA_t_test_array_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/test_array$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_test_array_OBJECTS) $(t_test_array_LDADD) $(LIBS) +t/test_base64.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/test_base64$(EXEEXT): $(t_test_base64_OBJECTS) $(t_test_base64_DEPENDENCIES) $(EXTRA_t_test_base64_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/test_base64$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_test_base64_OBJECTS) $(t_test_base64_LDADD) $(LIBS) +t/test_buffer.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/test_buffer$(EXEEXT): $(t_test_buffer_OBJECTS) $(t_test_buffer_DEPENDENCIES) $(EXTRA_t_test_buffer_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/test_buffer$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_test_buffer_OBJECTS) $(t_test_buffer_LDADD) $(LIBS) +t/test_burl.$(OBJEXT): t/$(am__dirstamp) t/$(DEPDIR)/$(am__dirstamp) + +t/test_burl$(EXEEXT): $(t_test_burl_OBJECTS) $(t_test_burl_DEPENDENCIES) $(EXTRA_t_test_burl_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/test_burl$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_test_burl_OBJECTS) $(t_test_burl_LDADD) $(LIBS) +t/test_configfile.$(OBJEXT): t/$(am__dirstamp) \ + t/$(DEPDIR)/$(am__dirstamp) + +t/test_configfile$(EXEEXT): $(t_test_configfile_OBJECTS) $(t_test_configfile_DEPENDENCIES) $(EXTRA_t_test_configfile_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/test_configfile$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_test_configfile_OBJECTS) $(t_test_configfile_LDADD) $(LIBS) +t/test_keyvalue.$(OBJEXT): t/$(am__dirstamp) \ + t/$(DEPDIR)/$(am__dirstamp) + +t/test_keyvalue$(EXEEXT): $(t_test_keyvalue_OBJECTS) $(t_test_keyvalue_DEPENDENCIES) $(EXTRA_t_test_keyvalue_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/test_keyvalue$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_test_keyvalue_OBJECTS) $(t_test_keyvalue_LDADD) $(LIBS) +t/test_mod_access.$(OBJEXT): t/$(am__dirstamp) \ + t/$(DEPDIR)/$(am__dirstamp) + +t/test_mod_access$(EXEEXT): $(t_test_mod_access_OBJECTS) $(t_test_mod_access_DEPENDENCIES) $(EXTRA_t_test_mod_access_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/test_mod_access$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_test_mod_access_OBJECTS) $(t_test_mod_access_LDADD) $(LIBS) +t/test_mod_evhost.$(OBJEXT): t/$(am__dirstamp) \ + t/$(DEPDIR)/$(am__dirstamp) + +t/test_mod_evhost$(EXEEXT): $(t_test_mod_evhost_OBJECTS) $(t_test_mod_evhost_DEPENDENCIES) $(EXTRA_t_test_mod_evhost_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/test_mod_evhost$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_test_mod_evhost_OBJECTS) $(t_test_mod_evhost_LDADD) $(LIBS) +t/test_mod_simple_vhost.$(OBJEXT): t/$(am__dirstamp) \ + t/$(DEPDIR)/$(am__dirstamp) + +t/test_mod_simple_vhost$(EXEEXT): $(t_test_mod_simple_vhost_OBJECTS) $(t_test_mod_simple_vhost_DEPENDENCIES) $(EXTRA_t_test_mod_simple_vhost_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/test_mod_simple_vhost$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_test_mod_simple_vhost_OBJECTS) $(t_test_mod_simple_vhost_LDADD) $(LIBS) +t/test_request.$(OBJEXT): t/$(am__dirstamp) \ + t/$(DEPDIR)/$(am__dirstamp) + +t/test_request$(EXEEXT): $(t_test_request_OBJECTS) $(t_test_request_DEPENDENCIES) $(EXTRA_t_test_request_DEPENDENCIES) t/$(am__dirstamp) + @rm -f t/test_request$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_test_request_OBJECTS) $(t_test_request_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f t/*.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/buffer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/burl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/configfile-glue.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data_array.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data_config.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data_integer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data_string.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_header.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_kv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-algo_sha1.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-array.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-base64.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-buffer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-burl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-chunk.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-configfile-glue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-connections-glue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-crc32.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-data_array.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-data_config.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-data_integer.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-data_string.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-etag.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_libev.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_poll.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_select.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-fdevent_solaris_port.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-gw_backend.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-http-header-glue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-http_auth.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-http_chunk.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-http_header.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-http_kv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-http_vhostdb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-joblist.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-keyvalue.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-log.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-md5.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-plugin.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-rand.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-request.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-safe_memclear.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-sock_addr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-splaytree.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-stat_cache.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-stream.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblightcomp_la-vector.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-algo_sha1.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-angel.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-array.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-base64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-buffer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-burl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-chunk.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-configfile-glue.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-configfile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-configparser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-connections-glue.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-connections.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-crc32.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-data_array.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-data_config.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-data_integer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-data_string.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-etag.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-fdevent.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-fdevent_freebsd_kqueue.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-fdevent_libev.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-fdevent_linux_sysepoll.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-fdevent_poll.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-fdevent_select.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-fdevent_solaris_devpoll.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-fdevent_solaris_port.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-gw_backend.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-http-header-glue.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-http_auth.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-http_chunk.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-http_header.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-http_kv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-http_vhostdb.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-inet_ntop_cache.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-joblist.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-keyvalue.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-log.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-md5.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_access.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_accesslog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_alias.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_auth.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_authn_file.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_authn_gssapi.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_authn_ldap.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_authn_mysql.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_authn_pam.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_cgi.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_cml.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_cml_funcs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_cml_lua.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_compress.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_deflate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_dirlisting.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_evasive.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_expire.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_extforward.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_fastcgi.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_flv_streaming.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_geoip.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_indexfile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_magnet.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_magnet_cache.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_mysql_vhost.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_openssl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_proxy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_redirect.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_rewrite.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_rrdtool.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_scgi.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_secdownload.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_setenv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_simple_vhost.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_ssi.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_ssi_expr.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_ssi_exprparser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_staticfile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_status.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_trigger_b4_dl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_uploadprogress.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_userdir.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_usertrack.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_vhostdb.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_vhostdb_dbi.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_vhostdb_ldap.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_vhostdb_mysql.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_vhostdb_pgsql.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-mod_webdav.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-network.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-network_write.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-plugin.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-rand.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-request.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-response.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-safe_memclear.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-sock_addr.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-splaytree.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-stat_cache.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-stream.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lighttpd-vector.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_access.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_accesslog.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_alias.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_auth.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_authn_file.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_authn_gssapi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_authn_ldap.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_authn_mysql_la-mod_authn_mysql.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_authn_pam.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_authn_sasl_la-mod_authn_sasl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_cgi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_cml_la-mod_cml.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_cml_la-mod_cml_funcs.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_cml_la-mod_cml_lua.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_compress.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_deflate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_dirlisting.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_evasive.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_evhost.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_expire.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_extforward.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_fastcgi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_flv_streaming.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_geoip.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_indexfile.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_magnet_la-mod_magnet.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_magnet_la-mod_magnet_cache.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_openssl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_proxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_redirect.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_rewrite.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_rrdtool.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_scgi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_secdownload.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_setenv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_simple_vhost.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_sockproxy.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_ssi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_ssi_expr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_ssi_exprparser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_staticfile.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_status.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_trigger_b4_dl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_uploadprogress.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_userdir.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_usertrack.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_vhostdb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_vhostdb_dbi_la-mod_vhostdb_dbi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_vhostdb_ldap.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_vhostdb_mysql_la-mod_vhostdb_mysql.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_webdav_la-mod_webdav.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_wstunnel.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/request.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sock_addr.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/vector.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/test_array.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/test_base64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/test_buffer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/test_burl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/test_configfile.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/test_keyvalue.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/test_mod_access.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/test_mod_evhost.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/test_mod_simple_vhost.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@t/$(DEPDIR)/test_request.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +liblightcomp_la-base64.lo: base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-base64.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-base64.Tpo -c -o liblightcomp_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-base64.Tpo $(DEPDIR)/liblightcomp_la-base64.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='liblightcomp_la-base64.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c + +liblightcomp_la-buffer.lo: buffer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-buffer.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-buffer.Tpo -c -o liblightcomp_la-buffer.lo `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-buffer.Tpo $(DEPDIR)/liblightcomp_la-buffer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='buffer.c' object='liblightcomp_la-buffer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-buffer.lo `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c + +liblightcomp_la-burl.lo: burl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-burl.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-burl.Tpo -c -o liblightcomp_la-burl.lo `test -f 'burl.c' || echo '$(srcdir)/'`burl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-burl.Tpo $(DEPDIR)/liblightcomp_la-burl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='burl.c' object='liblightcomp_la-burl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-burl.lo `test -f 'burl.c' || echo '$(srcdir)/'`burl.c + +liblightcomp_la-log.lo: log.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-log.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-log.Tpo -c -o liblightcomp_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-log.Tpo $(DEPDIR)/liblightcomp_la-log.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='liblightcomp_la-log.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c + +liblightcomp_la-http_header.lo: http_header.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-http_header.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-http_header.Tpo -c -o liblightcomp_la-http_header.lo `test -f 'http_header.c' || echo '$(srcdir)/'`http_header.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-http_header.Tpo $(DEPDIR)/liblightcomp_la-http_header.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_header.c' object='liblightcomp_la-http_header.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-http_header.lo `test -f 'http_header.c' || echo '$(srcdir)/'`http_header.c + +liblightcomp_la-http_kv.lo: http_kv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-http_kv.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-http_kv.Tpo -c -o liblightcomp_la-http_kv.lo `test -f 'http_kv.c' || echo '$(srcdir)/'`http_kv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-http_kv.Tpo $(DEPDIR)/liblightcomp_la-http_kv.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_kv.c' object='liblightcomp_la-http_kv.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-http_kv.lo `test -f 'http_kv.c' || echo '$(srcdir)/'`http_kv.c + +liblightcomp_la-keyvalue.lo: keyvalue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-keyvalue.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-keyvalue.Tpo -c -o liblightcomp_la-keyvalue.lo `test -f 'keyvalue.c' || echo '$(srcdir)/'`keyvalue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-keyvalue.Tpo $(DEPDIR)/liblightcomp_la-keyvalue.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='keyvalue.c' object='liblightcomp_la-keyvalue.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-keyvalue.lo `test -f 'keyvalue.c' || echo '$(srcdir)/'`keyvalue.c + +liblightcomp_la-chunk.lo: chunk.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-chunk.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-chunk.Tpo -c -o liblightcomp_la-chunk.lo `test -f 'chunk.c' || echo '$(srcdir)/'`chunk.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-chunk.Tpo $(DEPDIR)/liblightcomp_la-chunk.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='chunk.c' object='liblightcomp_la-chunk.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-chunk.lo `test -f 'chunk.c' || echo '$(srcdir)/'`chunk.c + +liblightcomp_la-http_chunk.lo: http_chunk.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-http_chunk.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-http_chunk.Tpo -c -o liblightcomp_la-http_chunk.lo `test -f 'http_chunk.c' || echo '$(srcdir)/'`http_chunk.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-http_chunk.Tpo $(DEPDIR)/liblightcomp_la-http_chunk.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_chunk.c' object='liblightcomp_la-http_chunk.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-http_chunk.lo `test -f 'http_chunk.c' || echo '$(srcdir)/'`http_chunk.c + +liblightcomp_la-stream.lo: stream.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-stream.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-stream.Tpo -c -o liblightcomp_la-stream.lo `test -f 'stream.c' || echo '$(srcdir)/'`stream.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-stream.Tpo $(DEPDIR)/liblightcomp_la-stream.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stream.c' object='liblightcomp_la-stream.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-stream.lo `test -f 'stream.c' || echo '$(srcdir)/'`stream.c + +liblightcomp_la-fdevent.lo: fdevent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent.Tpo -c -o liblightcomp_la-fdevent.lo `test -f 'fdevent.c' || echo '$(srcdir)/'`fdevent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent.Tpo $(DEPDIR)/liblightcomp_la-fdevent.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent.c' object='liblightcomp_la-fdevent.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent.lo `test -f 'fdevent.c' || echo '$(srcdir)/'`fdevent.c + +liblightcomp_la-gw_backend.lo: gw_backend.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-gw_backend.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-gw_backend.Tpo -c -o liblightcomp_la-gw_backend.lo `test -f 'gw_backend.c' || echo '$(srcdir)/'`gw_backend.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-gw_backend.Tpo $(DEPDIR)/liblightcomp_la-gw_backend.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gw_backend.c' object='liblightcomp_la-gw_backend.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-gw_backend.lo `test -f 'gw_backend.c' || echo '$(srcdir)/'`gw_backend.c + +liblightcomp_la-stat_cache.lo: stat_cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-stat_cache.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-stat_cache.Tpo -c -o liblightcomp_la-stat_cache.lo `test -f 'stat_cache.c' || echo '$(srcdir)/'`stat_cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-stat_cache.Tpo $(DEPDIR)/liblightcomp_la-stat_cache.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stat_cache.c' object='liblightcomp_la-stat_cache.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-stat_cache.lo `test -f 'stat_cache.c' || echo '$(srcdir)/'`stat_cache.c + +liblightcomp_la-plugin.lo: plugin.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-plugin.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-plugin.Tpo -c -o liblightcomp_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-plugin.Tpo $(DEPDIR)/liblightcomp_la-plugin.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugin.c' object='liblightcomp_la-plugin.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c + +liblightcomp_la-joblist.lo: joblist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-joblist.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-joblist.Tpo -c -o liblightcomp_la-joblist.lo `test -f 'joblist.c' || echo '$(srcdir)/'`joblist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-joblist.Tpo $(DEPDIR)/liblightcomp_la-joblist.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='joblist.c' object='liblightcomp_la-joblist.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-joblist.lo `test -f 'joblist.c' || echo '$(srcdir)/'`joblist.c + +liblightcomp_la-etag.lo: etag.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-etag.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-etag.Tpo -c -o liblightcomp_la-etag.lo `test -f 'etag.c' || echo '$(srcdir)/'`etag.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-etag.Tpo $(DEPDIR)/liblightcomp_la-etag.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='etag.c' object='liblightcomp_la-etag.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-etag.lo `test -f 'etag.c' || echo '$(srcdir)/'`etag.c + +liblightcomp_la-array.lo: array.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-array.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-array.Tpo -c -o liblightcomp_la-array.lo `test -f 'array.c' || echo '$(srcdir)/'`array.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-array.Tpo $(DEPDIR)/liblightcomp_la-array.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='array.c' object='liblightcomp_la-array.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-array.lo `test -f 'array.c' || echo '$(srcdir)/'`array.c + +liblightcomp_la-data_string.lo: data_string.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_string.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-data_string.Tpo -c -o liblightcomp_la-data_string.lo `test -f 'data_string.c' || echo '$(srcdir)/'`data_string.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-data_string.Tpo $(DEPDIR)/liblightcomp_la-data_string.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_string.c' object='liblightcomp_la-data_string.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_string.lo `test -f 'data_string.c' || echo '$(srcdir)/'`data_string.c + +liblightcomp_la-data_array.lo: data_array.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_array.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-data_array.Tpo -c -o liblightcomp_la-data_array.lo `test -f 'data_array.c' || echo '$(srcdir)/'`data_array.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-data_array.Tpo $(DEPDIR)/liblightcomp_la-data_array.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_array.c' object='liblightcomp_la-data_array.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_array.lo `test -f 'data_array.c' || echo '$(srcdir)/'`data_array.c + +liblightcomp_la-data_integer.lo: data_integer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_integer.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-data_integer.Tpo -c -o liblightcomp_la-data_integer.lo `test -f 'data_integer.c' || echo '$(srcdir)/'`data_integer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-data_integer.Tpo $(DEPDIR)/liblightcomp_la-data_integer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_integer.c' object='liblightcomp_la-data_integer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_integer.lo `test -f 'data_integer.c' || echo '$(srcdir)/'`data_integer.c + +liblightcomp_la-algo_sha1.lo: algo_sha1.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-algo_sha1.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-algo_sha1.Tpo -c -o liblightcomp_la-algo_sha1.lo `test -f 'algo_sha1.c' || echo '$(srcdir)/'`algo_sha1.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-algo_sha1.Tpo $(DEPDIR)/liblightcomp_la-algo_sha1.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='algo_sha1.c' object='liblightcomp_la-algo_sha1.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-algo_sha1.lo `test -f 'algo_sha1.c' || echo '$(srcdir)/'`algo_sha1.c + +liblightcomp_la-md5.lo: md5.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-md5.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-md5.Tpo -c -o liblightcomp_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-md5.Tpo $(DEPDIR)/liblightcomp_la-md5.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md5.c' object='liblightcomp_la-md5.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c + +liblightcomp_la-vector.lo: vector.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-vector.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-vector.Tpo -c -o liblightcomp_la-vector.lo `test -f 'vector.c' || echo '$(srcdir)/'`vector.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-vector.Tpo $(DEPDIR)/liblightcomp_la-vector.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vector.c' object='liblightcomp_la-vector.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-vector.lo `test -f 'vector.c' || echo '$(srcdir)/'`vector.c + +liblightcomp_la-fdevent_select.lo: fdevent_select.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_select.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_select.Tpo -c -o liblightcomp_la-fdevent_select.lo `test -f 'fdevent_select.c' || echo '$(srcdir)/'`fdevent_select.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_select.Tpo $(DEPDIR)/liblightcomp_la-fdevent_select.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_select.c' object='liblightcomp_la-fdevent_select.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_select.lo `test -f 'fdevent_select.c' || echo '$(srcdir)/'`fdevent_select.c + +liblightcomp_la-fdevent_libev.lo: fdevent_libev.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_libev.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_libev.Tpo -c -o liblightcomp_la-fdevent_libev.lo `test -f 'fdevent_libev.c' || echo '$(srcdir)/'`fdevent_libev.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_libev.Tpo $(DEPDIR)/liblightcomp_la-fdevent_libev.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_libev.c' object='liblightcomp_la-fdevent_libev.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_libev.lo `test -f 'fdevent_libev.c' || echo '$(srcdir)/'`fdevent_libev.c + +liblightcomp_la-fdevent_poll.lo: fdevent_poll.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_poll.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_poll.Tpo -c -o liblightcomp_la-fdevent_poll.lo `test -f 'fdevent_poll.c' || echo '$(srcdir)/'`fdevent_poll.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_poll.Tpo $(DEPDIR)/liblightcomp_la-fdevent_poll.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_poll.c' object='liblightcomp_la-fdevent_poll.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_poll.lo `test -f 'fdevent_poll.c' || echo '$(srcdir)/'`fdevent_poll.c + +liblightcomp_la-fdevent_linux_sysepoll.lo: fdevent_linux_sysepoll.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_linux_sysepoll.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Tpo -c -o liblightcomp_la-fdevent_linux_sysepoll.lo `test -f 'fdevent_linux_sysepoll.c' || echo '$(srcdir)/'`fdevent_linux_sysepoll.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Tpo $(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_linux_sysepoll.c' object='liblightcomp_la-fdevent_linux_sysepoll.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_linux_sysepoll.lo `test -f 'fdevent_linux_sysepoll.c' || echo '$(srcdir)/'`fdevent_linux_sysepoll.c + +liblightcomp_la-fdevent_solaris_devpoll.lo: fdevent_solaris_devpoll.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_solaris_devpoll.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Tpo -c -o liblightcomp_la-fdevent_solaris_devpoll.lo `test -f 'fdevent_solaris_devpoll.c' || echo '$(srcdir)/'`fdevent_solaris_devpoll.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Tpo $(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_solaris_devpoll.c' object='liblightcomp_la-fdevent_solaris_devpoll.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_solaris_devpoll.lo `test -f 'fdevent_solaris_devpoll.c' || echo '$(srcdir)/'`fdevent_solaris_devpoll.c + +liblightcomp_la-fdevent_solaris_port.lo: fdevent_solaris_port.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_solaris_port.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_solaris_port.Tpo -c -o liblightcomp_la-fdevent_solaris_port.lo `test -f 'fdevent_solaris_port.c' || echo '$(srcdir)/'`fdevent_solaris_port.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_solaris_port.Tpo $(DEPDIR)/liblightcomp_la-fdevent_solaris_port.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_solaris_port.c' object='liblightcomp_la-fdevent_solaris_port.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_solaris_port.lo `test -f 'fdevent_solaris_port.c' || echo '$(srcdir)/'`fdevent_solaris_port.c + +liblightcomp_la-fdevent_freebsd_kqueue.lo: fdevent_freebsd_kqueue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-fdevent_freebsd_kqueue.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Tpo -c -o liblightcomp_la-fdevent_freebsd_kqueue.lo `test -f 'fdevent_freebsd_kqueue.c' || echo '$(srcdir)/'`fdevent_freebsd_kqueue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Tpo $(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_freebsd_kqueue.c' object='liblightcomp_la-fdevent_freebsd_kqueue.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-fdevent_freebsd_kqueue.lo `test -f 'fdevent_freebsd_kqueue.c' || echo '$(srcdir)/'`fdevent_freebsd_kqueue.c + +liblightcomp_la-data_config.lo: data_config.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-data_config.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-data_config.Tpo -c -o liblightcomp_la-data_config.lo `test -f 'data_config.c' || echo '$(srcdir)/'`data_config.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-data_config.Tpo $(DEPDIR)/liblightcomp_la-data_config.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_config.c' object='liblightcomp_la-data_config.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-data_config.lo `test -f 'data_config.c' || echo '$(srcdir)/'`data_config.c + +liblightcomp_la-crc32.lo: crc32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-crc32.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-crc32.Tpo -c -o liblightcomp_la-crc32.lo `test -f 'crc32.c' || echo '$(srcdir)/'`crc32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-crc32.Tpo $(DEPDIR)/liblightcomp_la-crc32.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crc32.c' object='liblightcomp_la-crc32.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-crc32.lo `test -f 'crc32.c' || echo '$(srcdir)/'`crc32.c + +liblightcomp_la-connections-glue.lo: connections-glue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-connections-glue.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-connections-glue.Tpo -c -o liblightcomp_la-connections-glue.lo `test -f 'connections-glue.c' || echo '$(srcdir)/'`connections-glue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-connections-glue.Tpo $(DEPDIR)/liblightcomp_la-connections-glue.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connections-glue.c' object='liblightcomp_la-connections-glue.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-connections-glue.lo `test -f 'connections-glue.c' || echo '$(srcdir)/'`connections-glue.c + +liblightcomp_la-configfile-glue.lo: configfile-glue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-configfile-glue.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-configfile-glue.Tpo -c -o liblightcomp_la-configfile-glue.lo `test -f 'configfile-glue.c' || echo '$(srcdir)/'`configfile-glue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-configfile-glue.Tpo $(DEPDIR)/liblightcomp_la-configfile-glue.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='configfile-glue.c' object='liblightcomp_la-configfile-glue.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-configfile-glue.lo `test -f 'configfile-glue.c' || echo '$(srcdir)/'`configfile-glue.c + +liblightcomp_la-http-header-glue.lo: http-header-glue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-http-header-glue.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-http-header-glue.Tpo -c -o liblightcomp_la-http-header-glue.lo `test -f 'http-header-glue.c' || echo '$(srcdir)/'`http-header-glue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-http-header-glue.Tpo $(DEPDIR)/liblightcomp_la-http-header-glue.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http-header-glue.c' object='liblightcomp_la-http-header-glue.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-http-header-glue.lo `test -f 'http-header-glue.c' || echo '$(srcdir)/'`http-header-glue.c + +liblightcomp_la-http_auth.lo: http_auth.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-http_auth.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-http_auth.Tpo -c -o liblightcomp_la-http_auth.lo `test -f 'http_auth.c' || echo '$(srcdir)/'`http_auth.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-http_auth.Tpo $(DEPDIR)/liblightcomp_la-http_auth.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_auth.c' object='liblightcomp_la-http_auth.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-http_auth.lo `test -f 'http_auth.c' || echo '$(srcdir)/'`http_auth.c + +liblightcomp_la-http_vhostdb.lo: http_vhostdb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-http_vhostdb.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-http_vhostdb.Tpo -c -o liblightcomp_la-http_vhostdb.lo `test -f 'http_vhostdb.c' || echo '$(srcdir)/'`http_vhostdb.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-http_vhostdb.Tpo $(DEPDIR)/liblightcomp_la-http_vhostdb.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_vhostdb.c' object='liblightcomp_la-http_vhostdb.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-http_vhostdb.lo `test -f 'http_vhostdb.c' || echo '$(srcdir)/'`http_vhostdb.c + +liblightcomp_la-rand.lo: rand.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-rand.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-rand.Tpo -c -o liblightcomp_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-rand.Tpo $(DEPDIR)/liblightcomp_la-rand.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand.c' object='liblightcomp_la-rand.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c + +liblightcomp_la-request.lo: request.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-request.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-request.Tpo -c -o liblightcomp_la-request.lo `test -f 'request.c' || echo '$(srcdir)/'`request.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-request.Tpo $(DEPDIR)/liblightcomp_la-request.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='request.c' object='liblightcomp_la-request.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-request.lo `test -f 'request.c' || echo '$(srcdir)/'`request.c + +liblightcomp_la-sock_addr.lo: sock_addr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-sock_addr.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-sock_addr.Tpo -c -o liblightcomp_la-sock_addr.lo `test -f 'sock_addr.c' || echo '$(srcdir)/'`sock_addr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-sock_addr.Tpo $(DEPDIR)/liblightcomp_la-sock_addr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sock_addr.c' object='liblightcomp_la-sock_addr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-sock_addr.lo `test -f 'sock_addr.c' || echo '$(srcdir)/'`sock_addr.c + +liblightcomp_la-splaytree.lo: splaytree.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-splaytree.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-splaytree.Tpo -c -o liblightcomp_la-splaytree.lo `test -f 'splaytree.c' || echo '$(srcdir)/'`splaytree.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-splaytree.Tpo $(DEPDIR)/liblightcomp_la-splaytree.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='splaytree.c' object='liblightcomp_la-splaytree.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-splaytree.lo `test -f 'splaytree.c' || echo '$(srcdir)/'`splaytree.c + +liblightcomp_la-safe_memclear.lo: safe_memclear.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -MT liblightcomp_la-safe_memclear.lo -MD -MP -MF $(DEPDIR)/liblightcomp_la-safe_memclear.Tpo -c -o liblightcomp_la-safe_memclear.lo `test -f 'safe_memclear.c' || echo '$(srcdir)/'`safe_memclear.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblightcomp_la-safe_memclear.Tpo $(DEPDIR)/liblightcomp_la-safe_memclear.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='safe_memclear.c' object='liblightcomp_la-safe_memclear.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(liblightcomp_la_CFLAGS) $(CFLAGS) -c -o liblightcomp_la-safe_memclear.lo `test -f 'safe_memclear.c' || echo '$(srcdir)/'`safe_memclear.c + +mod_authn_mysql_la-mod_authn_mysql.lo: mod_authn_mysql.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_authn_mysql_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mod_authn_mysql_la-mod_authn_mysql.lo -MD -MP -MF $(DEPDIR)/mod_authn_mysql_la-mod_authn_mysql.Tpo -c -o mod_authn_mysql_la-mod_authn_mysql.lo `test -f 'mod_authn_mysql.c' || echo '$(srcdir)/'`mod_authn_mysql.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_authn_mysql_la-mod_authn_mysql.Tpo $(DEPDIR)/mod_authn_mysql_la-mod_authn_mysql.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_authn_mysql.c' object='mod_authn_mysql_la-mod_authn_mysql.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_authn_mysql_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mod_authn_mysql_la-mod_authn_mysql.lo `test -f 'mod_authn_mysql.c' || echo '$(srcdir)/'`mod_authn_mysql.c + +mod_authn_sasl_la-mod_authn_sasl.lo: mod_authn_sasl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_authn_sasl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mod_authn_sasl_la-mod_authn_sasl.lo -MD -MP -MF $(DEPDIR)/mod_authn_sasl_la-mod_authn_sasl.Tpo -c -o mod_authn_sasl_la-mod_authn_sasl.lo `test -f 'mod_authn_sasl.c' || echo '$(srcdir)/'`mod_authn_sasl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_authn_sasl_la-mod_authn_sasl.Tpo $(DEPDIR)/mod_authn_sasl_la-mod_authn_sasl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_authn_sasl.c' object='mod_authn_sasl_la-mod_authn_sasl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_authn_sasl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mod_authn_sasl_la-mod_authn_sasl.lo `test -f 'mod_authn_sasl.c' || echo '$(srcdir)/'`mod_authn_sasl.c + +mod_cml_la-mod_cml.lo: mod_cml.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -MT mod_cml_la-mod_cml.lo -MD -MP -MF $(DEPDIR)/mod_cml_la-mod_cml.Tpo -c -o mod_cml_la-mod_cml.lo `test -f 'mod_cml.c' || echo '$(srcdir)/'`mod_cml.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_cml_la-mod_cml.Tpo $(DEPDIR)/mod_cml_la-mod_cml.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cml.c' object='mod_cml_la-mod_cml.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -c -o mod_cml_la-mod_cml.lo `test -f 'mod_cml.c' || echo '$(srcdir)/'`mod_cml.c + +mod_cml_la-mod_cml_lua.lo: mod_cml_lua.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -MT mod_cml_la-mod_cml_lua.lo -MD -MP -MF $(DEPDIR)/mod_cml_la-mod_cml_lua.Tpo -c -o mod_cml_la-mod_cml_lua.lo `test -f 'mod_cml_lua.c' || echo '$(srcdir)/'`mod_cml_lua.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_cml_la-mod_cml_lua.Tpo $(DEPDIR)/mod_cml_la-mod_cml_lua.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cml_lua.c' object='mod_cml_la-mod_cml_lua.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -c -o mod_cml_la-mod_cml_lua.lo `test -f 'mod_cml_lua.c' || echo '$(srcdir)/'`mod_cml_lua.c + +mod_cml_la-mod_cml_funcs.lo: mod_cml_funcs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -MT mod_cml_la-mod_cml_funcs.lo -MD -MP -MF $(DEPDIR)/mod_cml_la-mod_cml_funcs.Tpo -c -o mod_cml_la-mod_cml_funcs.lo `test -f 'mod_cml_funcs.c' || echo '$(srcdir)/'`mod_cml_funcs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_cml_la-mod_cml_funcs.Tpo $(DEPDIR)/mod_cml_la-mod_cml_funcs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cml_funcs.c' object='mod_cml_la-mod_cml_funcs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_cml_la_CFLAGS) $(CFLAGS) -c -o mod_cml_la-mod_cml_funcs.lo `test -f 'mod_cml_funcs.c' || echo '$(srcdir)/'`mod_cml_funcs.c + +mod_magnet_la-mod_magnet.lo: mod_magnet.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_magnet_la_CFLAGS) $(CFLAGS) -MT mod_magnet_la-mod_magnet.lo -MD -MP -MF $(DEPDIR)/mod_magnet_la-mod_magnet.Tpo -c -o mod_magnet_la-mod_magnet.lo `test -f 'mod_magnet.c' || echo '$(srcdir)/'`mod_magnet.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_magnet_la-mod_magnet.Tpo $(DEPDIR)/mod_magnet_la-mod_magnet.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_magnet.c' object='mod_magnet_la-mod_magnet.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_magnet_la_CFLAGS) $(CFLAGS) -c -o mod_magnet_la-mod_magnet.lo `test -f 'mod_magnet.c' || echo '$(srcdir)/'`mod_magnet.c + +mod_magnet_la-mod_magnet_cache.lo: mod_magnet_cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_magnet_la_CFLAGS) $(CFLAGS) -MT mod_magnet_la-mod_magnet_cache.lo -MD -MP -MF $(DEPDIR)/mod_magnet_la-mod_magnet_cache.Tpo -c -o mod_magnet_la-mod_magnet_cache.lo `test -f 'mod_magnet_cache.c' || echo '$(srcdir)/'`mod_magnet_cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_magnet_la-mod_magnet_cache.Tpo $(DEPDIR)/mod_magnet_la-mod_magnet_cache.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_magnet_cache.c' object='mod_magnet_la-mod_magnet_cache.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_magnet_la_CFLAGS) $(CFLAGS) -c -o mod_magnet_la-mod_magnet_cache.lo `test -f 'mod_magnet_cache.c' || echo '$(srcdir)/'`mod_magnet_cache.c + +mod_mysql_vhost_la-mod_mysql_vhost.lo: mod_mysql_vhost.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_mysql_vhost_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mod_mysql_vhost_la-mod_mysql_vhost.lo -MD -MP -MF $(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Tpo -c -o mod_mysql_vhost_la-mod_mysql_vhost.lo `test -f 'mod_mysql_vhost.c' || echo '$(srcdir)/'`mod_mysql_vhost.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Tpo $(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_mysql_vhost.c' object='mod_mysql_vhost_la-mod_mysql_vhost.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_mysql_vhost_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mod_mysql_vhost_la-mod_mysql_vhost.lo `test -f 'mod_mysql_vhost.c' || echo '$(srcdir)/'`mod_mysql_vhost.c + +mod_vhostdb_dbi_la-mod_vhostdb_dbi.lo: mod_vhostdb_dbi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_vhostdb_dbi_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mod_vhostdb_dbi_la-mod_vhostdb_dbi.lo -MD -MP -MF $(DEPDIR)/mod_vhostdb_dbi_la-mod_vhostdb_dbi.Tpo -c -o mod_vhostdb_dbi_la-mod_vhostdb_dbi.lo `test -f 'mod_vhostdb_dbi.c' || echo '$(srcdir)/'`mod_vhostdb_dbi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_vhostdb_dbi_la-mod_vhostdb_dbi.Tpo $(DEPDIR)/mod_vhostdb_dbi_la-mod_vhostdb_dbi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb_dbi.c' object='mod_vhostdb_dbi_la-mod_vhostdb_dbi.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_vhostdb_dbi_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mod_vhostdb_dbi_la-mod_vhostdb_dbi.lo `test -f 'mod_vhostdb_dbi.c' || echo '$(srcdir)/'`mod_vhostdb_dbi.c + +mod_vhostdb_mysql_la-mod_vhostdb_mysql.lo: mod_vhostdb_mysql.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_vhostdb_mysql_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mod_vhostdb_mysql_la-mod_vhostdb_mysql.lo -MD -MP -MF $(DEPDIR)/mod_vhostdb_mysql_la-mod_vhostdb_mysql.Tpo -c -o mod_vhostdb_mysql_la-mod_vhostdb_mysql.lo `test -f 'mod_vhostdb_mysql.c' || echo '$(srcdir)/'`mod_vhostdb_mysql.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_vhostdb_mysql_la-mod_vhostdb_mysql.Tpo $(DEPDIR)/mod_vhostdb_mysql_la-mod_vhostdb_mysql.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb_mysql.c' object='mod_vhostdb_mysql_la-mod_vhostdb_mysql.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_vhostdb_mysql_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mod_vhostdb_mysql_la-mod_vhostdb_mysql.lo `test -f 'mod_vhostdb_mysql.c' || echo '$(srcdir)/'`mod_vhostdb_mysql.c + +mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.lo: mod_vhostdb_pgsql.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_vhostdb_pgsql_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.lo -MD -MP -MF $(DEPDIR)/mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.Tpo -c -o mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.lo `test -f 'mod_vhostdb_pgsql.c' || echo '$(srcdir)/'`mod_vhostdb_pgsql.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.Tpo $(DEPDIR)/mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb_pgsql.c' object='mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_vhostdb_pgsql_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.lo `test -f 'mod_vhostdb_pgsql.c' || echo '$(srcdir)/'`mod_vhostdb_pgsql.c + +mod_webdav_la-mod_webdav.lo: mod_webdav.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_webdav_la_CFLAGS) $(CFLAGS) -MT mod_webdav_la-mod_webdav.lo -MD -MP -MF $(DEPDIR)/mod_webdav_la-mod_webdav.Tpo -c -o mod_webdav_la-mod_webdav.lo `test -f 'mod_webdav.c' || echo '$(srcdir)/'`mod_webdav.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_webdav_la-mod_webdav.Tpo $(DEPDIR)/mod_webdav_la-mod_webdav.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_webdav.c' object='mod_webdav_la-mod_webdav.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mod_webdav_la_CFLAGS) $(CFLAGS) -c -o mod_webdav_la-mod_webdav.lo `test -f 'mod_webdav.c' || echo '$(srcdir)/'`mod_webdav.c + +lighttpd-server.o: server.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-server.o -MD -MP -MF $(DEPDIR)/lighttpd-server.Tpo -c -o lighttpd-server.o `test -f 'server.c' || echo '$(srcdir)/'`server.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-server.Tpo $(DEPDIR)/lighttpd-server.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server.c' object='lighttpd-server.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-server.o `test -f 'server.c' || echo '$(srcdir)/'`server.c + +lighttpd-server.obj: server.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-server.obj -MD -MP -MF $(DEPDIR)/lighttpd-server.Tpo -c -o lighttpd-server.obj `if test -f 'server.c'; then $(CYGPATH_W) 'server.c'; else $(CYGPATH_W) '$(srcdir)/server.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-server.Tpo $(DEPDIR)/lighttpd-server.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server.c' object='lighttpd-server.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-server.obj `if test -f 'server.c'; then $(CYGPATH_W) 'server.c'; else $(CYGPATH_W) '$(srcdir)/server.c'; fi` + +lighttpd-response.o: response.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-response.o -MD -MP -MF $(DEPDIR)/lighttpd-response.Tpo -c -o lighttpd-response.o `test -f 'response.c' || echo '$(srcdir)/'`response.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-response.Tpo $(DEPDIR)/lighttpd-response.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='response.c' object='lighttpd-response.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-response.o `test -f 'response.c' || echo '$(srcdir)/'`response.c + +lighttpd-response.obj: response.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-response.obj -MD -MP -MF $(DEPDIR)/lighttpd-response.Tpo -c -o lighttpd-response.obj `if test -f 'response.c'; then $(CYGPATH_W) 'response.c'; else $(CYGPATH_W) '$(srcdir)/response.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-response.Tpo $(DEPDIR)/lighttpd-response.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='response.c' object='lighttpd-response.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-response.obj `if test -f 'response.c'; then $(CYGPATH_W) 'response.c'; else $(CYGPATH_W) '$(srcdir)/response.c'; fi` + +lighttpd-connections.o: connections.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-connections.o -MD -MP -MF $(DEPDIR)/lighttpd-connections.Tpo -c -o lighttpd-connections.o `test -f 'connections.c' || echo '$(srcdir)/'`connections.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-connections.Tpo $(DEPDIR)/lighttpd-connections.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connections.c' object='lighttpd-connections.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-connections.o `test -f 'connections.c' || echo '$(srcdir)/'`connections.c + +lighttpd-connections.obj: connections.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-connections.obj -MD -MP -MF $(DEPDIR)/lighttpd-connections.Tpo -c -o lighttpd-connections.obj `if test -f 'connections.c'; then $(CYGPATH_W) 'connections.c'; else $(CYGPATH_W) '$(srcdir)/connections.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-connections.Tpo $(DEPDIR)/lighttpd-connections.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connections.c' object='lighttpd-connections.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-connections.obj `if test -f 'connections.c'; then $(CYGPATH_W) 'connections.c'; else $(CYGPATH_W) '$(srcdir)/connections.c'; fi` + +lighttpd-inet_ntop_cache.o: inet_ntop_cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-inet_ntop_cache.o -MD -MP -MF $(DEPDIR)/lighttpd-inet_ntop_cache.Tpo -c -o lighttpd-inet_ntop_cache.o `test -f 'inet_ntop_cache.c' || echo '$(srcdir)/'`inet_ntop_cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-inet_ntop_cache.Tpo $(DEPDIR)/lighttpd-inet_ntop_cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_ntop_cache.c' object='lighttpd-inet_ntop_cache.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-inet_ntop_cache.o `test -f 'inet_ntop_cache.c' || echo '$(srcdir)/'`inet_ntop_cache.c + +lighttpd-inet_ntop_cache.obj: inet_ntop_cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-inet_ntop_cache.obj -MD -MP -MF $(DEPDIR)/lighttpd-inet_ntop_cache.Tpo -c -o lighttpd-inet_ntop_cache.obj `if test -f 'inet_ntop_cache.c'; then $(CYGPATH_W) 'inet_ntop_cache.c'; else $(CYGPATH_W) '$(srcdir)/inet_ntop_cache.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-inet_ntop_cache.Tpo $(DEPDIR)/lighttpd-inet_ntop_cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_ntop_cache.c' object='lighttpd-inet_ntop_cache.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-inet_ntop_cache.obj `if test -f 'inet_ntop_cache.c'; then $(CYGPATH_W) 'inet_ntop_cache.c'; else $(CYGPATH_W) '$(srcdir)/inet_ntop_cache.c'; fi` + +lighttpd-network.o: network.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-network.o -MD -MP -MF $(DEPDIR)/lighttpd-network.Tpo -c -o lighttpd-network.o `test -f 'network.c' || echo '$(srcdir)/'`network.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-network.Tpo $(DEPDIR)/lighttpd-network.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network.c' object='lighttpd-network.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-network.o `test -f 'network.c' || echo '$(srcdir)/'`network.c + +lighttpd-network.obj: network.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-network.obj -MD -MP -MF $(DEPDIR)/lighttpd-network.Tpo -c -o lighttpd-network.obj `if test -f 'network.c'; then $(CYGPATH_W) 'network.c'; else $(CYGPATH_W) '$(srcdir)/network.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-network.Tpo $(DEPDIR)/lighttpd-network.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network.c' object='lighttpd-network.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-network.obj `if test -f 'network.c'; then $(CYGPATH_W) 'network.c'; else $(CYGPATH_W) '$(srcdir)/network.c'; fi` + +lighttpd-network_write.o: network_write.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-network_write.o -MD -MP -MF $(DEPDIR)/lighttpd-network_write.Tpo -c -o lighttpd-network_write.o `test -f 'network_write.c' || echo '$(srcdir)/'`network_write.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-network_write.Tpo $(DEPDIR)/lighttpd-network_write.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network_write.c' object='lighttpd-network_write.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-network_write.o `test -f 'network_write.c' || echo '$(srcdir)/'`network_write.c + +lighttpd-network_write.obj: network_write.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-network_write.obj -MD -MP -MF $(DEPDIR)/lighttpd-network_write.Tpo -c -o lighttpd-network_write.obj `if test -f 'network_write.c'; then $(CYGPATH_W) 'network_write.c'; else $(CYGPATH_W) '$(srcdir)/network_write.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-network_write.Tpo $(DEPDIR)/lighttpd-network_write.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='network_write.c' object='lighttpd-network_write.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-network_write.obj `if test -f 'network_write.c'; then $(CYGPATH_W) 'network_write.c'; else $(CYGPATH_W) '$(srcdir)/network_write.c'; fi` + +lighttpd-configfile.o: configfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-configfile.o -MD -MP -MF $(DEPDIR)/lighttpd-configfile.Tpo -c -o lighttpd-configfile.o `test -f 'configfile.c' || echo '$(srcdir)/'`configfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-configfile.Tpo $(DEPDIR)/lighttpd-configfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='configfile.c' object='lighttpd-configfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-configfile.o `test -f 'configfile.c' || echo '$(srcdir)/'`configfile.c + +lighttpd-configfile.obj: configfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-configfile.obj -MD -MP -MF $(DEPDIR)/lighttpd-configfile.Tpo -c -o lighttpd-configfile.obj `if test -f 'configfile.c'; then $(CYGPATH_W) 'configfile.c'; else $(CYGPATH_W) '$(srcdir)/configfile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-configfile.Tpo $(DEPDIR)/lighttpd-configfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='configfile.c' object='lighttpd-configfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-configfile.obj `if test -f 'configfile.c'; then $(CYGPATH_W) 'configfile.c'; else $(CYGPATH_W) '$(srcdir)/configfile.c'; fi` + +lighttpd-configparser.o: configparser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-configparser.o -MD -MP -MF $(DEPDIR)/lighttpd-configparser.Tpo -c -o lighttpd-configparser.o `test -f 'configparser.c' || echo '$(srcdir)/'`configparser.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-configparser.Tpo $(DEPDIR)/lighttpd-configparser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='configparser.c' object='lighttpd-configparser.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-configparser.o `test -f 'configparser.c' || echo '$(srcdir)/'`configparser.c + +lighttpd-configparser.obj: configparser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-configparser.obj -MD -MP -MF $(DEPDIR)/lighttpd-configparser.Tpo -c -o lighttpd-configparser.obj `if test -f 'configparser.c'; then $(CYGPATH_W) 'configparser.c'; else $(CYGPATH_W) '$(srcdir)/configparser.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-configparser.Tpo $(DEPDIR)/lighttpd-configparser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='configparser.c' object='lighttpd-configparser.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-configparser.obj `if test -f 'configparser.c'; then $(CYGPATH_W) 'configparser.c'; else $(CYGPATH_W) '$(srcdir)/configparser.c'; fi` + +lighttpd-base64.o: base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-base64.o -MD -MP -MF $(DEPDIR)/lighttpd-base64.Tpo -c -o lighttpd-base64.o `test -f 'base64.c' || echo '$(srcdir)/'`base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-base64.Tpo $(DEPDIR)/lighttpd-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='lighttpd-base64.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-base64.o `test -f 'base64.c' || echo '$(srcdir)/'`base64.c + +lighttpd-base64.obj: base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-base64.obj -MD -MP -MF $(DEPDIR)/lighttpd-base64.Tpo -c -o lighttpd-base64.obj `if test -f 'base64.c'; then $(CYGPATH_W) 'base64.c'; else $(CYGPATH_W) '$(srcdir)/base64.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-base64.Tpo $(DEPDIR)/lighttpd-base64.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='lighttpd-base64.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-base64.obj `if test -f 'base64.c'; then $(CYGPATH_W) 'base64.c'; else $(CYGPATH_W) '$(srcdir)/base64.c'; fi` + +lighttpd-buffer.o: buffer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-buffer.o -MD -MP -MF $(DEPDIR)/lighttpd-buffer.Tpo -c -o lighttpd-buffer.o `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-buffer.Tpo $(DEPDIR)/lighttpd-buffer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='buffer.c' object='lighttpd-buffer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-buffer.o `test -f 'buffer.c' || echo '$(srcdir)/'`buffer.c + +lighttpd-buffer.obj: buffer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-buffer.obj -MD -MP -MF $(DEPDIR)/lighttpd-buffer.Tpo -c -o lighttpd-buffer.obj `if test -f 'buffer.c'; then $(CYGPATH_W) 'buffer.c'; else $(CYGPATH_W) '$(srcdir)/buffer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-buffer.Tpo $(DEPDIR)/lighttpd-buffer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='buffer.c' object='lighttpd-buffer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-buffer.obj `if test -f 'buffer.c'; then $(CYGPATH_W) 'buffer.c'; else $(CYGPATH_W) '$(srcdir)/buffer.c'; fi` + +lighttpd-burl.o: burl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-burl.o -MD -MP -MF $(DEPDIR)/lighttpd-burl.Tpo -c -o lighttpd-burl.o `test -f 'burl.c' || echo '$(srcdir)/'`burl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-burl.Tpo $(DEPDIR)/lighttpd-burl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='burl.c' object='lighttpd-burl.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-burl.o `test -f 'burl.c' || echo '$(srcdir)/'`burl.c + +lighttpd-burl.obj: burl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-burl.obj -MD -MP -MF $(DEPDIR)/lighttpd-burl.Tpo -c -o lighttpd-burl.obj `if test -f 'burl.c'; then $(CYGPATH_W) 'burl.c'; else $(CYGPATH_W) '$(srcdir)/burl.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-burl.Tpo $(DEPDIR)/lighttpd-burl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='burl.c' object='lighttpd-burl.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-burl.obj `if test -f 'burl.c'; then $(CYGPATH_W) 'burl.c'; else $(CYGPATH_W) '$(srcdir)/burl.c'; fi` + +lighttpd-log.o: log.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-log.o -MD -MP -MF $(DEPDIR)/lighttpd-log.Tpo -c -o lighttpd-log.o `test -f 'log.c' || echo '$(srcdir)/'`log.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-log.Tpo $(DEPDIR)/lighttpd-log.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='lighttpd-log.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-log.o `test -f 'log.c' || echo '$(srcdir)/'`log.c + +lighttpd-log.obj: log.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-log.obj -MD -MP -MF $(DEPDIR)/lighttpd-log.Tpo -c -o lighttpd-log.obj `if test -f 'log.c'; then $(CYGPATH_W) 'log.c'; else $(CYGPATH_W) '$(srcdir)/log.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-log.Tpo $(DEPDIR)/lighttpd-log.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='lighttpd-log.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-log.obj `if test -f 'log.c'; then $(CYGPATH_W) 'log.c'; else $(CYGPATH_W) '$(srcdir)/log.c'; fi` + +lighttpd-http_header.o: http_header.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-http_header.o -MD -MP -MF $(DEPDIR)/lighttpd-http_header.Tpo -c -o lighttpd-http_header.o `test -f 'http_header.c' || echo '$(srcdir)/'`http_header.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-http_header.Tpo $(DEPDIR)/lighttpd-http_header.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_header.c' object='lighttpd-http_header.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-http_header.o `test -f 'http_header.c' || echo '$(srcdir)/'`http_header.c + +lighttpd-http_header.obj: http_header.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-http_header.obj -MD -MP -MF $(DEPDIR)/lighttpd-http_header.Tpo -c -o lighttpd-http_header.obj `if test -f 'http_header.c'; then $(CYGPATH_W) 'http_header.c'; else $(CYGPATH_W) '$(srcdir)/http_header.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-http_header.Tpo $(DEPDIR)/lighttpd-http_header.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_header.c' object='lighttpd-http_header.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-http_header.obj `if test -f 'http_header.c'; then $(CYGPATH_W) 'http_header.c'; else $(CYGPATH_W) '$(srcdir)/http_header.c'; fi` + +lighttpd-http_kv.o: http_kv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-http_kv.o -MD -MP -MF $(DEPDIR)/lighttpd-http_kv.Tpo -c -o lighttpd-http_kv.o `test -f 'http_kv.c' || echo '$(srcdir)/'`http_kv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-http_kv.Tpo $(DEPDIR)/lighttpd-http_kv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_kv.c' object='lighttpd-http_kv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-http_kv.o `test -f 'http_kv.c' || echo '$(srcdir)/'`http_kv.c + +lighttpd-http_kv.obj: http_kv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-http_kv.obj -MD -MP -MF $(DEPDIR)/lighttpd-http_kv.Tpo -c -o lighttpd-http_kv.obj `if test -f 'http_kv.c'; then $(CYGPATH_W) 'http_kv.c'; else $(CYGPATH_W) '$(srcdir)/http_kv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-http_kv.Tpo $(DEPDIR)/lighttpd-http_kv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_kv.c' object='lighttpd-http_kv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-http_kv.obj `if test -f 'http_kv.c'; then $(CYGPATH_W) 'http_kv.c'; else $(CYGPATH_W) '$(srcdir)/http_kv.c'; fi` + +lighttpd-keyvalue.o: keyvalue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-keyvalue.o -MD -MP -MF $(DEPDIR)/lighttpd-keyvalue.Tpo -c -o lighttpd-keyvalue.o `test -f 'keyvalue.c' || echo '$(srcdir)/'`keyvalue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-keyvalue.Tpo $(DEPDIR)/lighttpd-keyvalue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='keyvalue.c' object='lighttpd-keyvalue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-keyvalue.o `test -f 'keyvalue.c' || echo '$(srcdir)/'`keyvalue.c + +lighttpd-keyvalue.obj: keyvalue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-keyvalue.obj -MD -MP -MF $(DEPDIR)/lighttpd-keyvalue.Tpo -c -o lighttpd-keyvalue.obj `if test -f 'keyvalue.c'; then $(CYGPATH_W) 'keyvalue.c'; else $(CYGPATH_W) '$(srcdir)/keyvalue.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-keyvalue.Tpo $(DEPDIR)/lighttpd-keyvalue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='keyvalue.c' object='lighttpd-keyvalue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-keyvalue.obj `if test -f 'keyvalue.c'; then $(CYGPATH_W) 'keyvalue.c'; else $(CYGPATH_W) '$(srcdir)/keyvalue.c'; fi` + +lighttpd-chunk.o: chunk.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-chunk.o -MD -MP -MF $(DEPDIR)/lighttpd-chunk.Tpo -c -o lighttpd-chunk.o `test -f 'chunk.c' || echo '$(srcdir)/'`chunk.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-chunk.Tpo $(DEPDIR)/lighttpd-chunk.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='chunk.c' object='lighttpd-chunk.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-chunk.o `test -f 'chunk.c' || echo '$(srcdir)/'`chunk.c + +lighttpd-chunk.obj: chunk.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-chunk.obj -MD -MP -MF $(DEPDIR)/lighttpd-chunk.Tpo -c -o lighttpd-chunk.obj `if test -f 'chunk.c'; then $(CYGPATH_W) 'chunk.c'; else $(CYGPATH_W) '$(srcdir)/chunk.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-chunk.Tpo $(DEPDIR)/lighttpd-chunk.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='chunk.c' object='lighttpd-chunk.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-chunk.obj `if test -f 'chunk.c'; then $(CYGPATH_W) 'chunk.c'; else $(CYGPATH_W) '$(srcdir)/chunk.c'; fi` + +lighttpd-http_chunk.o: http_chunk.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-http_chunk.o -MD -MP -MF $(DEPDIR)/lighttpd-http_chunk.Tpo -c -o lighttpd-http_chunk.o `test -f 'http_chunk.c' || echo '$(srcdir)/'`http_chunk.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-http_chunk.Tpo $(DEPDIR)/lighttpd-http_chunk.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_chunk.c' object='lighttpd-http_chunk.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-http_chunk.o `test -f 'http_chunk.c' || echo '$(srcdir)/'`http_chunk.c + +lighttpd-http_chunk.obj: http_chunk.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-http_chunk.obj -MD -MP -MF $(DEPDIR)/lighttpd-http_chunk.Tpo -c -o lighttpd-http_chunk.obj `if test -f 'http_chunk.c'; then $(CYGPATH_W) 'http_chunk.c'; else $(CYGPATH_W) '$(srcdir)/http_chunk.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-http_chunk.Tpo $(DEPDIR)/lighttpd-http_chunk.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_chunk.c' object='lighttpd-http_chunk.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-http_chunk.obj `if test -f 'http_chunk.c'; then $(CYGPATH_W) 'http_chunk.c'; else $(CYGPATH_W) '$(srcdir)/http_chunk.c'; fi` + +lighttpd-stream.o: stream.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-stream.o -MD -MP -MF $(DEPDIR)/lighttpd-stream.Tpo -c -o lighttpd-stream.o `test -f 'stream.c' || echo '$(srcdir)/'`stream.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-stream.Tpo $(DEPDIR)/lighttpd-stream.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stream.c' object='lighttpd-stream.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-stream.o `test -f 'stream.c' || echo '$(srcdir)/'`stream.c + +lighttpd-stream.obj: stream.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-stream.obj -MD -MP -MF $(DEPDIR)/lighttpd-stream.Tpo -c -o lighttpd-stream.obj `if test -f 'stream.c'; then $(CYGPATH_W) 'stream.c'; else $(CYGPATH_W) '$(srcdir)/stream.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-stream.Tpo $(DEPDIR)/lighttpd-stream.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stream.c' object='lighttpd-stream.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-stream.obj `if test -f 'stream.c'; then $(CYGPATH_W) 'stream.c'; else $(CYGPATH_W) '$(srcdir)/stream.c'; fi` + +lighttpd-fdevent.o: fdevent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent.o -MD -MP -MF $(DEPDIR)/lighttpd-fdevent.Tpo -c -o lighttpd-fdevent.o `test -f 'fdevent.c' || echo '$(srcdir)/'`fdevent.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent.Tpo $(DEPDIR)/lighttpd-fdevent.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent.c' object='lighttpd-fdevent.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent.o `test -f 'fdevent.c' || echo '$(srcdir)/'`fdevent.c + +lighttpd-fdevent.obj: fdevent.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent.obj -MD -MP -MF $(DEPDIR)/lighttpd-fdevent.Tpo -c -o lighttpd-fdevent.obj `if test -f 'fdevent.c'; then $(CYGPATH_W) 'fdevent.c'; else $(CYGPATH_W) '$(srcdir)/fdevent.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent.Tpo $(DEPDIR)/lighttpd-fdevent.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent.c' object='lighttpd-fdevent.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent.obj `if test -f 'fdevent.c'; then $(CYGPATH_W) 'fdevent.c'; else $(CYGPATH_W) '$(srcdir)/fdevent.c'; fi` + +lighttpd-gw_backend.o: gw_backend.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-gw_backend.o -MD -MP -MF $(DEPDIR)/lighttpd-gw_backend.Tpo -c -o lighttpd-gw_backend.o `test -f 'gw_backend.c' || echo '$(srcdir)/'`gw_backend.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-gw_backend.Tpo $(DEPDIR)/lighttpd-gw_backend.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gw_backend.c' object='lighttpd-gw_backend.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-gw_backend.o `test -f 'gw_backend.c' || echo '$(srcdir)/'`gw_backend.c + +lighttpd-gw_backend.obj: gw_backend.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-gw_backend.obj -MD -MP -MF $(DEPDIR)/lighttpd-gw_backend.Tpo -c -o lighttpd-gw_backend.obj `if test -f 'gw_backend.c'; then $(CYGPATH_W) 'gw_backend.c'; else $(CYGPATH_W) '$(srcdir)/gw_backend.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-gw_backend.Tpo $(DEPDIR)/lighttpd-gw_backend.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gw_backend.c' object='lighttpd-gw_backend.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-gw_backend.obj `if test -f 'gw_backend.c'; then $(CYGPATH_W) 'gw_backend.c'; else $(CYGPATH_W) '$(srcdir)/gw_backend.c'; fi` + +lighttpd-stat_cache.o: stat_cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-stat_cache.o -MD -MP -MF $(DEPDIR)/lighttpd-stat_cache.Tpo -c -o lighttpd-stat_cache.o `test -f 'stat_cache.c' || echo '$(srcdir)/'`stat_cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-stat_cache.Tpo $(DEPDIR)/lighttpd-stat_cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stat_cache.c' object='lighttpd-stat_cache.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-stat_cache.o `test -f 'stat_cache.c' || echo '$(srcdir)/'`stat_cache.c + +lighttpd-stat_cache.obj: stat_cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-stat_cache.obj -MD -MP -MF $(DEPDIR)/lighttpd-stat_cache.Tpo -c -o lighttpd-stat_cache.obj `if test -f 'stat_cache.c'; then $(CYGPATH_W) 'stat_cache.c'; else $(CYGPATH_W) '$(srcdir)/stat_cache.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-stat_cache.Tpo $(DEPDIR)/lighttpd-stat_cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='stat_cache.c' object='lighttpd-stat_cache.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-stat_cache.obj `if test -f 'stat_cache.c'; then $(CYGPATH_W) 'stat_cache.c'; else $(CYGPATH_W) '$(srcdir)/stat_cache.c'; fi` + +lighttpd-plugin.o: plugin.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-plugin.o -MD -MP -MF $(DEPDIR)/lighttpd-plugin.Tpo -c -o lighttpd-plugin.o `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-plugin.Tpo $(DEPDIR)/lighttpd-plugin.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugin.c' object='lighttpd-plugin.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-plugin.o `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c + +lighttpd-plugin.obj: plugin.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-plugin.obj -MD -MP -MF $(DEPDIR)/lighttpd-plugin.Tpo -c -o lighttpd-plugin.obj `if test -f 'plugin.c'; then $(CYGPATH_W) 'plugin.c'; else $(CYGPATH_W) '$(srcdir)/plugin.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-plugin.Tpo $(DEPDIR)/lighttpd-plugin.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugin.c' object='lighttpd-plugin.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-plugin.obj `if test -f 'plugin.c'; then $(CYGPATH_W) 'plugin.c'; else $(CYGPATH_W) '$(srcdir)/plugin.c'; fi` + +lighttpd-joblist.o: joblist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-joblist.o -MD -MP -MF $(DEPDIR)/lighttpd-joblist.Tpo -c -o lighttpd-joblist.o `test -f 'joblist.c' || echo '$(srcdir)/'`joblist.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-joblist.Tpo $(DEPDIR)/lighttpd-joblist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='joblist.c' object='lighttpd-joblist.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-joblist.o `test -f 'joblist.c' || echo '$(srcdir)/'`joblist.c + +lighttpd-joblist.obj: joblist.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-joblist.obj -MD -MP -MF $(DEPDIR)/lighttpd-joblist.Tpo -c -o lighttpd-joblist.obj `if test -f 'joblist.c'; then $(CYGPATH_W) 'joblist.c'; else $(CYGPATH_W) '$(srcdir)/joblist.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-joblist.Tpo $(DEPDIR)/lighttpd-joblist.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='joblist.c' object='lighttpd-joblist.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-joblist.obj `if test -f 'joblist.c'; then $(CYGPATH_W) 'joblist.c'; else $(CYGPATH_W) '$(srcdir)/joblist.c'; fi` + +lighttpd-etag.o: etag.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-etag.o -MD -MP -MF $(DEPDIR)/lighttpd-etag.Tpo -c -o lighttpd-etag.o `test -f 'etag.c' || echo '$(srcdir)/'`etag.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-etag.Tpo $(DEPDIR)/lighttpd-etag.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='etag.c' object='lighttpd-etag.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-etag.o `test -f 'etag.c' || echo '$(srcdir)/'`etag.c + +lighttpd-etag.obj: etag.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-etag.obj -MD -MP -MF $(DEPDIR)/lighttpd-etag.Tpo -c -o lighttpd-etag.obj `if test -f 'etag.c'; then $(CYGPATH_W) 'etag.c'; else $(CYGPATH_W) '$(srcdir)/etag.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-etag.Tpo $(DEPDIR)/lighttpd-etag.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='etag.c' object='lighttpd-etag.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-etag.obj `if test -f 'etag.c'; then $(CYGPATH_W) 'etag.c'; else $(CYGPATH_W) '$(srcdir)/etag.c'; fi` + +lighttpd-array.o: array.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-array.o -MD -MP -MF $(DEPDIR)/lighttpd-array.Tpo -c -o lighttpd-array.o `test -f 'array.c' || echo '$(srcdir)/'`array.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-array.Tpo $(DEPDIR)/lighttpd-array.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='array.c' object='lighttpd-array.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-array.o `test -f 'array.c' || echo '$(srcdir)/'`array.c + +lighttpd-array.obj: array.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-array.obj -MD -MP -MF $(DEPDIR)/lighttpd-array.Tpo -c -o lighttpd-array.obj `if test -f 'array.c'; then $(CYGPATH_W) 'array.c'; else $(CYGPATH_W) '$(srcdir)/array.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-array.Tpo $(DEPDIR)/lighttpd-array.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='array.c' object='lighttpd-array.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-array.obj `if test -f 'array.c'; then $(CYGPATH_W) 'array.c'; else $(CYGPATH_W) '$(srcdir)/array.c'; fi` + +lighttpd-data_string.o: data_string.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-data_string.o -MD -MP -MF $(DEPDIR)/lighttpd-data_string.Tpo -c -o lighttpd-data_string.o `test -f 'data_string.c' || echo '$(srcdir)/'`data_string.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-data_string.Tpo $(DEPDIR)/lighttpd-data_string.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_string.c' object='lighttpd-data_string.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-data_string.o `test -f 'data_string.c' || echo '$(srcdir)/'`data_string.c + +lighttpd-data_string.obj: data_string.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-data_string.obj -MD -MP -MF $(DEPDIR)/lighttpd-data_string.Tpo -c -o lighttpd-data_string.obj `if test -f 'data_string.c'; then $(CYGPATH_W) 'data_string.c'; else $(CYGPATH_W) '$(srcdir)/data_string.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-data_string.Tpo $(DEPDIR)/lighttpd-data_string.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_string.c' object='lighttpd-data_string.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-data_string.obj `if test -f 'data_string.c'; then $(CYGPATH_W) 'data_string.c'; else $(CYGPATH_W) '$(srcdir)/data_string.c'; fi` + +lighttpd-data_array.o: data_array.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-data_array.o -MD -MP -MF $(DEPDIR)/lighttpd-data_array.Tpo -c -o lighttpd-data_array.o `test -f 'data_array.c' || echo '$(srcdir)/'`data_array.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-data_array.Tpo $(DEPDIR)/lighttpd-data_array.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_array.c' object='lighttpd-data_array.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-data_array.o `test -f 'data_array.c' || echo '$(srcdir)/'`data_array.c + +lighttpd-data_array.obj: data_array.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-data_array.obj -MD -MP -MF $(DEPDIR)/lighttpd-data_array.Tpo -c -o lighttpd-data_array.obj `if test -f 'data_array.c'; then $(CYGPATH_W) 'data_array.c'; else $(CYGPATH_W) '$(srcdir)/data_array.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-data_array.Tpo $(DEPDIR)/lighttpd-data_array.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_array.c' object='lighttpd-data_array.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-data_array.obj `if test -f 'data_array.c'; then $(CYGPATH_W) 'data_array.c'; else $(CYGPATH_W) '$(srcdir)/data_array.c'; fi` + +lighttpd-data_integer.o: data_integer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-data_integer.o -MD -MP -MF $(DEPDIR)/lighttpd-data_integer.Tpo -c -o lighttpd-data_integer.o `test -f 'data_integer.c' || echo '$(srcdir)/'`data_integer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-data_integer.Tpo $(DEPDIR)/lighttpd-data_integer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_integer.c' object='lighttpd-data_integer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-data_integer.o `test -f 'data_integer.c' || echo '$(srcdir)/'`data_integer.c + +lighttpd-data_integer.obj: data_integer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-data_integer.obj -MD -MP -MF $(DEPDIR)/lighttpd-data_integer.Tpo -c -o lighttpd-data_integer.obj `if test -f 'data_integer.c'; then $(CYGPATH_W) 'data_integer.c'; else $(CYGPATH_W) '$(srcdir)/data_integer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-data_integer.Tpo $(DEPDIR)/lighttpd-data_integer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_integer.c' object='lighttpd-data_integer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-data_integer.obj `if test -f 'data_integer.c'; then $(CYGPATH_W) 'data_integer.c'; else $(CYGPATH_W) '$(srcdir)/data_integer.c'; fi` + +lighttpd-algo_sha1.o: algo_sha1.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-algo_sha1.o -MD -MP -MF $(DEPDIR)/lighttpd-algo_sha1.Tpo -c -o lighttpd-algo_sha1.o `test -f 'algo_sha1.c' || echo '$(srcdir)/'`algo_sha1.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-algo_sha1.Tpo $(DEPDIR)/lighttpd-algo_sha1.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='algo_sha1.c' object='lighttpd-algo_sha1.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-algo_sha1.o `test -f 'algo_sha1.c' || echo '$(srcdir)/'`algo_sha1.c + +lighttpd-algo_sha1.obj: algo_sha1.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-algo_sha1.obj -MD -MP -MF $(DEPDIR)/lighttpd-algo_sha1.Tpo -c -o lighttpd-algo_sha1.obj `if test -f 'algo_sha1.c'; then $(CYGPATH_W) 'algo_sha1.c'; else $(CYGPATH_W) '$(srcdir)/algo_sha1.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-algo_sha1.Tpo $(DEPDIR)/lighttpd-algo_sha1.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='algo_sha1.c' object='lighttpd-algo_sha1.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-algo_sha1.obj `if test -f 'algo_sha1.c'; then $(CYGPATH_W) 'algo_sha1.c'; else $(CYGPATH_W) '$(srcdir)/algo_sha1.c'; fi` + +lighttpd-md5.o: md5.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-md5.o -MD -MP -MF $(DEPDIR)/lighttpd-md5.Tpo -c -o lighttpd-md5.o `test -f 'md5.c' || echo '$(srcdir)/'`md5.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-md5.Tpo $(DEPDIR)/lighttpd-md5.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md5.c' object='lighttpd-md5.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-md5.o `test -f 'md5.c' || echo '$(srcdir)/'`md5.c + +lighttpd-md5.obj: md5.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-md5.obj -MD -MP -MF $(DEPDIR)/lighttpd-md5.Tpo -c -o lighttpd-md5.obj `if test -f 'md5.c'; then $(CYGPATH_W) 'md5.c'; else $(CYGPATH_W) '$(srcdir)/md5.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-md5.Tpo $(DEPDIR)/lighttpd-md5.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md5.c' object='lighttpd-md5.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-md5.obj `if test -f 'md5.c'; then $(CYGPATH_W) 'md5.c'; else $(CYGPATH_W) '$(srcdir)/md5.c'; fi` + +lighttpd-vector.o: vector.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-vector.o -MD -MP -MF $(DEPDIR)/lighttpd-vector.Tpo -c -o lighttpd-vector.o `test -f 'vector.c' || echo '$(srcdir)/'`vector.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-vector.Tpo $(DEPDIR)/lighttpd-vector.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vector.c' object='lighttpd-vector.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-vector.o `test -f 'vector.c' || echo '$(srcdir)/'`vector.c + +lighttpd-vector.obj: vector.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-vector.obj -MD -MP -MF $(DEPDIR)/lighttpd-vector.Tpo -c -o lighttpd-vector.obj `if test -f 'vector.c'; then $(CYGPATH_W) 'vector.c'; else $(CYGPATH_W) '$(srcdir)/vector.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-vector.Tpo $(DEPDIR)/lighttpd-vector.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vector.c' object='lighttpd-vector.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-vector.obj `if test -f 'vector.c'; then $(CYGPATH_W) 'vector.c'; else $(CYGPATH_W) '$(srcdir)/vector.c'; fi` + +lighttpd-fdevent_select.o: fdevent_select.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_select.o -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_select.Tpo -c -o lighttpd-fdevent_select.o `test -f 'fdevent_select.c' || echo '$(srcdir)/'`fdevent_select.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_select.Tpo $(DEPDIR)/lighttpd-fdevent_select.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_select.c' object='lighttpd-fdevent_select.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_select.o `test -f 'fdevent_select.c' || echo '$(srcdir)/'`fdevent_select.c + +lighttpd-fdevent_select.obj: fdevent_select.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_select.obj -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_select.Tpo -c -o lighttpd-fdevent_select.obj `if test -f 'fdevent_select.c'; then $(CYGPATH_W) 'fdevent_select.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_select.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_select.Tpo $(DEPDIR)/lighttpd-fdevent_select.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_select.c' object='lighttpd-fdevent_select.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_select.obj `if test -f 'fdevent_select.c'; then $(CYGPATH_W) 'fdevent_select.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_select.c'; fi` + +lighttpd-fdevent_libev.o: fdevent_libev.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_libev.o -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_libev.Tpo -c -o lighttpd-fdevent_libev.o `test -f 'fdevent_libev.c' || echo '$(srcdir)/'`fdevent_libev.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_libev.Tpo $(DEPDIR)/lighttpd-fdevent_libev.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_libev.c' object='lighttpd-fdevent_libev.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_libev.o `test -f 'fdevent_libev.c' || echo '$(srcdir)/'`fdevent_libev.c + +lighttpd-fdevent_libev.obj: fdevent_libev.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_libev.obj -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_libev.Tpo -c -o lighttpd-fdevent_libev.obj `if test -f 'fdevent_libev.c'; then $(CYGPATH_W) 'fdevent_libev.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_libev.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_libev.Tpo $(DEPDIR)/lighttpd-fdevent_libev.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_libev.c' object='lighttpd-fdevent_libev.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_libev.obj `if test -f 'fdevent_libev.c'; then $(CYGPATH_W) 'fdevent_libev.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_libev.c'; fi` + +lighttpd-fdevent_poll.o: fdevent_poll.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_poll.o -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_poll.Tpo -c -o lighttpd-fdevent_poll.o `test -f 'fdevent_poll.c' || echo '$(srcdir)/'`fdevent_poll.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_poll.Tpo $(DEPDIR)/lighttpd-fdevent_poll.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_poll.c' object='lighttpd-fdevent_poll.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_poll.o `test -f 'fdevent_poll.c' || echo '$(srcdir)/'`fdevent_poll.c + +lighttpd-fdevent_poll.obj: fdevent_poll.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_poll.obj -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_poll.Tpo -c -o lighttpd-fdevent_poll.obj `if test -f 'fdevent_poll.c'; then $(CYGPATH_W) 'fdevent_poll.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_poll.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_poll.Tpo $(DEPDIR)/lighttpd-fdevent_poll.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_poll.c' object='lighttpd-fdevent_poll.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_poll.obj `if test -f 'fdevent_poll.c'; then $(CYGPATH_W) 'fdevent_poll.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_poll.c'; fi` + +lighttpd-fdevent_linux_sysepoll.o: fdevent_linux_sysepoll.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_linux_sysepoll.o -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_linux_sysepoll.Tpo -c -o lighttpd-fdevent_linux_sysepoll.o `test -f 'fdevent_linux_sysepoll.c' || echo '$(srcdir)/'`fdevent_linux_sysepoll.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_linux_sysepoll.Tpo $(DEPDIR)/lighttpd-fdevent_linux_sysepoll.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_linux_sysepoll.c' object='lighttpd-fdevent_linux_sysepoll.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_linux_sysepoll.o `test -f 'fdevent_linux_sysepoll.c' || echo '$(srcdir)/'`fdevent_linux_sysepoll.c + +lighttpd-fdevent_linux_sysepoll.obj: fdevent_linux_sysepoll.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_linux_sysepoll.obj -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_linux_sysepoll.Tpo -c -o lighttpd-fdevent_linux_sysepoll.obj `if test -f 'fdevent_linux_sysepoll.c'; then $(CYGPATH_W) 'fdevent_linux_sysepoll.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_linux_sysepoll.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_linux_sysepoll.Tpo $(DEPDIR)/lighttpd-fdevent_linux_sysepoll.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_linux_sysepoll.c' object='lighttpd-fdevent_linux_sysepoll.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_linux_sysepoll.obj `if test -f 'fdevent_linux_sysepoll.c'; then $(CYGPATH_W) 'fdevent_linux_sysepoll.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_linux_sysepoll.c'; fi` + +lighttpd-fdevent_solaris_devpoll.o: fdevent_solaris_devpoll.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_solaris_devpoll.o -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_solaris_devpoll.Tpo -c -o lighttpd-fdevent_solaris_devpoll.o `test -f 'fdevent_solaris_devpoll.c' || echo '$(srcdir)/'`fdevent_solaris_devpoll.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_solaris_devpoll.Tpo $(DEPDIR)/lighttpd-fdevent_solaris_devpoll.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_solaris_devpoll.c' object='lighttpd-fdevent_solaris_devpoll.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_solaris_devpoll.o `test -f 'fdevent_solaris_devpoll.c' || echo '$(srcdir)/'`fdevent_solaris_devpoll.c + +lighttpd-fdevent_solaris_devpoll.obj: fdevent_solaris_devpoll.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_solaris_devpoll.obj -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_solaris_devpoll.Tpo -c -o lighttpd-fdevent_solaris_devpoll.obj `if test -f 'fdevent_solaris_devpoll.c'; then $(CYGPATH_W) 'fdevent_solaris_devpoll.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_solaris_devpoll.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_solaris_devpoll.Tpo $(DEPDIR)/lighttpd-fdevent_solaris_devpoll.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_solaris_devpoll.c' object='lighttpd-fdevent_solaris_devpoll.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_solaris_devpoll.obj `if test -f 'fdevent_solaris_devpoll.c'; then $(CYGPATH_W) 'fdevent_solaris_devpoll.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_solaris_devpoll.c'; fi` + +lighttpd-fdevent_solaris_port.o: fdevent_solaris_port.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_solaris_port.o -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_solaris_port.Tpo -c -o lighttpd-fdevent_solaris_port.o `test -f 'fdevent_solaris_port.c' || echo '$(srcdir)/'`fdevent_solaris_port.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_solaris_port.Tpo $(DEPDIR)/lighttpd-fdevent_solaris_port.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_solaris_port.c' object='lighttpd-fdevent_solaris_port.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_solaris_port.o `test -f 'fdevent_solaris_port.c' || echo '$(srcdir)/'`fdevent_solaris_port.c + +lighttpd-fdevent_solaris_port.obj: fdevent_solaris_port.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_solaris_port.obj -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_solaris_port.Tpo -c -o lighttpd-fdevent_solaris_port.obj `if test -f 'fdevent_solaris_port.c'; then $(CYGPATH_W) 'fdevent_solaris_port.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_solaris_port.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_solaris_port.Tpo $(DEPDIR)/lighttpd-fdevent_solaris_port.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_solaris_port.c' object='lighttpd-fdevent_solaris_port.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_solaris_port.obj `if test -f 'fdevent_solaris_port.c'; then $(CYGPATH_W) 'fdevent_solaris_port.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_solaris_port.c'; fi` + +lighttpd-fdevent_freebsd_kqueue.o: fdevent_freebsd_kqueue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_freebsd_kqueue.o -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_freebsd_kqueue.Tpo -c -o lighttpd-fdevent_freebsd_kqueue.o `test -f 'fdevent_freebsd_kqueue.c' || echo '$(srcdir)/'`fdevent_freebsd_kqueue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_freebsd_kqueue.Tpo $(DEPDIR)/lighttpd-fdevent_freebsd_kqueue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_freebsd_kqueue.c' object='lighttpd-fdevent_freebsd_kqueue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_freebsd_kqueue.o `test -f 'fdevent_freebsd_kqueue.c' || echo '$(srcdir)/'`fdevent_freebsd_kqueue.c + +lighttpd-fdevent_freebsd_kqueue.obj: fdevent_freebsd_kqueue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-fdevent_freebsd_kqueue.obj -MD -MP -MF $(DEPDIR)/lighttpd-fdevent_freebsd_kqueue.Tpo -c -o lighttpd-fdevent_freebsd_kqueue.obj `if test -f 'fdevent_freebsd_kqueue.c'; then $(CYGPATH_W) 'fdevent_freebsd_kqueue.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_freebsd_kqueue.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-fdevent_freebsd_kqueue.Tpo $(DEPDIR)/lighttpd-fdevent_freebsd_kqueue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fdevent_freebsd_kqueue.c' object='lighttpd-fdevent_freebsd_kqueue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-fdevent_freebsd_kqueue.obj `if test -f 'fdevent_freebsd_kqueue.c'; then $(CYGPATH_W) 'fdevent_freebsd_kqueue.c'; else $(CYGPATH_W) '$(srcdir)/fdevent_freebsd_kqueue.c'; fi` + +lighttpd-data_config.o: data_config.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-data_config.o -MD -MP -MF $(DEPDIR)/lighttpd-data_config.Tpo -c -o lighttpd-data_config.o `test -f 'data_config.c' || echo '$(srcdir)/'`data_config.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-data_config.Tpo $(DEPDIR)/lighttpd-data_config.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_config.c' object='lighttpd-data_config.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-data_config.o `test -f 'data_config.c' || echo '$(srcdir)/'`data_config.c + +lighttpd-data_config.obj: data_config.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-data_config.obj -MD -MP -MF $(DEPDIR)/lighttpd-data_config.Tpo -c -o lighttpd-data_config.obj `if test -f 'data_config.c'; then $(CYGPATH_W) 'data_config.c'; else $(CYGPATH_W) '$(srcdir)/data_config.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-data_config.Tpo $(DEPDIR)/lighttpd-data_config.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data_config.c' object='lighttpd-data_config.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-data_config.obj `if test -f 'data_config.c'; then $(CYGPATH_W) 'data_config.c'; else $(CYGPATH_W) '$(srcdir)/data_config.c'; fi` + +lighttpd-crc32.o: crc32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-crc32.o -MD -MP -MF $(DEPDIR)/lighttpd-crc32.Tpo -c -o lighttpd-crc32.o `test -f 'crc32.c' || echo '$(srcdir)/'`crc32.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-crc32.Tpo $(DEPDIR)/lighttpd-crc32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crc32.c' object='lighttpd-crc32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-crc32.o `test -f 'crc32.c' || echo '$(srcdir)/'`crc32.c + +lighttpd-crc32.obj: crc32.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-crc32.obj -MD -MP -MF $(DEPDIR)/lighttpd-crc32.Tpo -c -o lighttpd-crc32.obj `if test -f 'crc32.c'; then $(CYGPATH_W) 'crc32.c'; else $(CYGPATH_W) '$(srcdir)/crc32.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-crc32.Tpo $(DEPDIR)/lighttpd-crc32.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crc32.c' object='lighttpd-crc32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-crc32.obj `if test -f 'crc32.c'; then $(CYGPATH_W) 'crc32.c'; else $(CYGPATH_W) '$(srcdir)/crc32.c'; fi` + +lighttpd-connections-glue.o: connections-glue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-connections-glue.o -MD -MP -MF $(DEPDIR)/lighttpd-connections-glue.Tpo -c -o lighttpd-connections-glue.o `test -f 'connections-glue.c' || echo '$(srcdir)/'`connections-glue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-connections-glue.Tpo $(DEPDIR)/lighttpd-connections-glue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connections-glue.c' object='lighttpd-connections-glue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-connections-glue.o `test -f 'connections-glue.c' || echo '$(srcdir)/'`connections-glue.c + +lighttpd-connections-glue.obj: connections-glue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-connections-glue.obj -MD -MP -MF $(DEPDIR)/lighttpd-connections-glue.Tpo -c -o lighttpd-connections-glue.obj `if test -f 'connections-glue.c'; then $(CYGPATH_W) 'connections-glue.c'; else $(CYGPATH_W) '$(srcdir)/connections-glue.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-connections-glue.Tpo $(DEPDIR)/lighttpd-connections-glue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='connections-glue.c' object='lighttpd-connections-glue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-connections-glue.obj `if test -f 'connections-glue.c'; then $(CYGPATH_W) 'connections-glue.c'; else $(CYGPATH_W) '$(srcdir)/connections-glue.c'; fi` + +lighttpd-configfile-glue.o: configfile-glue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-configfile-glue.o -MD -MP -MF $(DEPDIR)/lighttpd-configfile-glue.Tpo -c -o lighttpd-configfile-glue.o `test -f 'configfile-glue.c' || echo '$(srcdir)/'`configfile-glue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-configfile-glue.Tpo $(DEPDIR)/lighttpd-configfile-glue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='configfile-glue.c' object='lighttpd-configfile-glue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-configfile-glue.o `test -f 'configfile-glue.c' || echo '$(srcdir)/'`configfile-glue.c + +lighttpd-configfile-glue.obj: configfile-glue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-configfile-glue.obj -MD -MP -MF $(DEPDIR)/lighttpd-configfile-glue.Tpo -c -o lighttpd-configfile-glue.obj `if test -f 'configfile-glue.c'; then $(CYGPATH_W) 'configfile-glue.c'; else $(CYGPATH_W) '$(srcdir)/configfile-glue.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-configfile-glue.Tpo $(DEPDIR)/lighttpd-configfile-glue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='configfile-glue.c' object='lighttpd-configfile-glue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-configfile-glue.obj `if test -f 'configfile-glue.c'; then $(CYGPATH_W) 'configfile-glue.c'; else $(CYGPATH_W) '$(srcdir)/configfile-glue.c'; fi` + +lighttpd-http-header-glue.o: http-header-glue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-http-header-glue.o -MD -MP -MF $(DEPDIR)/lighttpd-http-header-glue.Tpo -c -o lighttpd-http-header-glue.o `test -f 'http-header-glue.c' || echo '$(srcdir)/'`http-header-glue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-http-header-glue.Tpo $(DEPDIR)/lighttpd-http-header-glue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http-header-glue.c' object='lighttpd-http-header-glue.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-http-header-glue.o `test -f 'http-header-glue.c' || echo '$(srcdir)/'`http-header-glue.c + +lighttpd-http-header-glue.obj: http-header-glue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-http-header-glue.obj -MD -MP -MF $(DEPDIR)/lighttpd-http-header-glue.Tpo -c -o lighttpd-http-header-glue.obj `if test -f 'http-header-glue.c'; then $(CYGPATH_W) 'http-header-glue.c'; else $(CYGPATH_W) '$(srcdir)/http-header-glue.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-http-header-glue.Tpo $(DEPDIR)/lighttpd-http-header-glue.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http-header-glue.c' object='lighttpd-http-header-glue.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-http-header-glue.obj `if test -f 'http-header-glue.c'; then $(CYGPATH_W) 'http-header-glue.c'; else $(CYGPATH_W) '$(srcdir)/http-header-glue.c'; fi` + +lighttpd-http_auth.o: http_auth.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-http_auth.o -MD -MP -MF $(DEPDIR)/lighttpd-http_auth.Tpo -c -o lighttpd-http_auth.o `test -f 'http_auth.c' || echo '$(srcdir)/'`http_auth.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-http_auth.Tpo $(DEPDIR)/lighttpd-http_auth.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_auth.c' object='lighttpd-http_auth.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-http_auth.o `test -f 'http_auth.c' || echo '$(srcdir)/'`http_auth.c + +lighttpd-http_auth.obj: http_auth.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-http_auth.obj -MD -MP -MF $(DEPDIR)/lighttpd-http_auth.Tpo -c -o lighttpd-http_auth.obj `if test -f 'http_auth.c'; then $(CYGPATH_W) 'http_auth.c'; else $(CYGPATH_W) '$(srcdir)/http_auth.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-http_auth.Tpo $(DEPDIR)/lighttpd-http_auth.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_auth.c' object='lighttpd-http_auth.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-http_auth.obj `if test -f 'http_auth.c'; then $(CYGPATH_W) 'http_auth.c'; else $(CYGPATH_W) '$(srcdir)/http_auth.c'; fi` + +lighttpd-http_vhostdb.o: http_vhostdb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-http_vhostdb.o -MD -MP -MF $(DEPDIR)/lighttpd-http_vhostdb.Tpo -c -o lighttpd-http_vhostdb.o `test -f 'http_vhostdb.c' || echo '$(srcdir)/'`http_vhostdb.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-http_vhostdb.Tpo $(DEPDIR)/lighttpd-http_vhostdb.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_vhostdb.c' object='lighttpd-http_vhostdb.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-http_vhostdb.o `test -f 'http_vhostdb.c' || echo '$(srcdir)/'`http_vhostdb.c + +lighttpd-http_vhostdb.obj: http_vhostdb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-http_vhostdb.obj -MD -MP -MF $(DEPDIR)/lighttpd-http_vhostdb.Tpo -c -o lighttpd-http_vhostdb.obj `if test -f 'http_vhostdb.c'; then $(CYGPATH_W) 'http_vhostdb.c'; else $(CYGPATH_W) '$(srcdir)/http_vhostdb.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-http_vhostdb.Tpo $(DEPDIR)/lighttpd-http_vhostdb.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='http_vhostdb.c' object='lighttpd-http_vhostdb.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-http_vhostdb.obj `if test -f 'http_vhostdb.c'; then $(CYGPATH_W) 'http_vhostdb.c'; else $(CYGPATH_W) '$(srcdir)/http_vhostdb.c'; fi` + +lighttpd-rand.o: rand.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-rand.o -MD -MP -MF $(DEPDIR)/lighttpd-rand.Tpo -c -o lighttpd-rand.o `test -f 'rand.c' || echo '$(srcdir)/'`rand.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-rand.Tpo $(DEPDIR)/lighttpd-rand.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand.c' object='lighttpd-rand.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-rand.o `test -f 'rand.c' || echo '$(srcdir)/'`rand.c + +lighttpd-rand.obj: rand.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-rand.obj -MD -MP -MF $(DEPDIR)/lighttpd-rand.Tpo -c -o lighttpd-rand.obj `if test -f 'rand.c'; then $(CYGPATH_W) 'rand.c'; else $(CYGPATH_W) '$(srcdir)/rand.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-rand.Tpo $(DEPDIR)/lighttpd-rand.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand.c' object='lighttpd-rand.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-rand.obj `if test -f 'rand.c'; then $(CYGPATH_W) 'rand.c'; else $(CYGPATH_W) '$(srcdir)/rand.c'; fi` + +lighttpd-request.o: request.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-request.o -MD -MP -MF $(DEPDIR)/lighttpd-request.Tpo -c -o lighttpd-request.o `test -f 'request.c' || echo '$(srcdir)/'`request.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-request.Tpo $(DEPDIR)/lighttpd-request.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='request.c' object='lighttpd-request.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-request.o `test -f 'request.c' || echo '$(srcdir)/'`request.c + +lighttpd-request.obj: request.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-request.obj -MD -MP -MF $(DEPDIR)/lighttpd-request.Tpo -c -o lighttpd-request.obj `if test -f 'request.c'; then $(CYGPATH_W) 'request.c'; else $(CYGPATH_W) '$(srcdir)/request.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-request.Tpo $(DEPDIR)/lighttpd-request.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='request.c' object='lighttpd-request.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-request.obj `if test -f 'request.c'; then $(CYGPATH_W) 'request.c'; else $(CYGPATH_W) '$(srcdir)/request.c'; fi` + +lighttpd-sock_addr.o: sock_addr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-sock_addr.o -MD -MP -MF $(DEPDIR)/lighttpd-sock_addr.Tpo -c -o lighttpd-sock_addr.o `test -f 'sock_addr.c' || echo '$(srcdir)/'`sock_addr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-sock_addr.Tpo $(DEPDIR)/lighttpd-sock_addr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sock_addr.c' object='lighttpd-sock_addr.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-sock_addr.o `test -f 'sock_addr.c' || echo '$(srcdir)/'`sock_addr.c + +lighttpd-sock_addr.obj: sock_addr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-sock_addr.obj -MD -MP -MF $(DEPDIR)/lighttpd-sock_addr.Tpo -c -o lighttpd-sock_addr.obj `if test -f 'sock_addr.c'; then $(CYGPATH_W) 'sock_addr.c'; else $(CYGPATH_W) '$(srcdir)/sock_addr.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-sock_addr.Tpo $(DEPDIR)/lighttpd-sock_addr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sock_addr.c' object='lighttpd-sock_addr.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-sock_addr.obj `if test -f 'sock_addr.c'; then $(CYGPATH_W) 'sock_addr.c'; else $(CYGPATH_W) '$(srcdir)/sock_addr.c'; fi` + +lighttpd-splaytree.o: splaytree.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-splaytree.o -MD -MP -MF $(DEPDIR)/lighttpd-splaytree.Tpo -c -o lighttpd-splaytree.o `test -f 'splaytree.c' || echo '$(srcdir)/'`splaytree.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-splaytree.Tpo $(DEPDIR)/lighttpd-splaytree.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='splaytree.c' object='lighttpd-splaytree.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-splaytree.o `test -f 'splaytree.c' || echo '$(srcdir)/'`splaytree.c + +lighttpd-splaytree.obj: splaytree.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-splaytree.obj -MD -MP -MF $(DEPDIR)/lighttpd-splaytree.Tpo -c -o lighttpd-splaytree.obj `if test -f 'splaytree.c'; then $(CYGPATH_W) 'splaytree.c'; else $(CYGPATH_W) '$(srcdir)/splaytree.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-splaytree.Tpo $(DEPDIR)/lighttpd-splaytree.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='splaytree.c' object='lighttpd-splaytree.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-splaytree.obj `if test -f 'splaytree.c'; then $(CYGPATH_W) 'splaytree.c'; else $(CYGPATH_W) '$(srcdir)/splaytree.c'; fi` + +lighttpd-safe_memclear.o: safe_memclear.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-safe_memclear.o -MD -MP -MF $(DEPDIR)/lighttpd-safe_memclear.Tpo -c -o lighttpd-safe_memclear.o `test -f 'safe_memclear.c' || echo '$(srcdir)/'`safe_memclear.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-safe_memclear.Tpo $(DEPDIR)/lighttpd-safe_memclear.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='safe_memclear.c' object='lighttpd-safe_memclear.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-safe_memclear.o `test -f 'safe_memclear.c' || echo '$(srcdir)/'`safe_memclear.c + +lighttpd-safe_memclear.obj: safe_memclear.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-safe_memclear.obj -MD -MP -MF $(DEPDIR)/lighttpd-safe_memclear.Tpo -c -o lighttpd-safe_memclear.obj `if test -f 'safe_memclear.c'; then $(CYGPATH_W) 'safe_memclear.c'; else $(CYGPATH_W) '$(srcdir)/safe_memclear.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-safe_memclear.Tpo $(DEPDIR)/lighttpd-safe_memclear.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='safe_memclear.c' object='lighttpd-safe_memclear.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-safe_memclear.obj `if test -f 'safe_memclear.c'; then $(CYGPATH_W) 'safe_memclear.c'; else $(CYGPATH_W) '$(srcdir)/safe_memclear.c'; fi` + +lighttpd-mod_access.o: mod_access.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_access.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_access.Tpo -c -o lighttpd-mod_access.o `test -f 'mod_access.c' || echo '$(srcdir)/'`mod_access.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_access.Tpo $(DEPDIR)/lighttpd-mod_access.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_access.c' object='lighttpd-mod_access.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_access.o `test -f 'mod_access.c' || echo '$(srcdir)/'`mod_access.c + +lighttpd-mod_access.obj: mod_access.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_access.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_access.Tpo -c -o lighttpd-mod_access.obj `if test -f 'mod_access.c'; then $(CYGPATH_W) 'mod_access.c'; else $(CYGPATH_W) '$(srcdir)/mod_access.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_access.Tpo $(DEPDIR)/lighttpd-mod_access.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_access.c' object='lighttpd-mod_access.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_access.obj `if test -f 'mod_access.c'; then $(CYGPATH_W) 'mod_access.c'; else $(CYGPATH_W) '$(srcdir)/mod_access.c'; fi` + +lighttpd-mod_accesslog.o: mod_accesslog.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_accesslog.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_accesslog.Tpo -c -o lighttpd-mod_accesslog.o `test -f 'mod_accesslog.c' || echo '$(srcdir)/'`mod_accesslog.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_accesslog.Tpo $(DEPDIR)/lighttpd-mod_accesslog.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_accesslog.c' object='lighttpd-mod_accesslog.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_accesslog.o `test -f 'mod_accesslog.c' || echo '$(srcdir)/'`mod_accesslog.c + +lighttpd-mod_accesslog.obj: mod_accesslog.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_accesslog.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_accesslog.Tpo -c -o lighttpd-mod_accesslog.obj `if test -f 'mod_accesslog.c'; then $(CYGPATH_W) 'mod_accesslog.c'; else $(CYGPATH_W) '$(srcdir)/mod_accesslog.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_accesslog.Tpo $(DEPDIR)/lighttpd-mod_accesslog.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_accesslog.c' object='lighttpd-mod_accesslog.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_accesslog.obj `if test -f 'mod_accesslog.c'; then $(CYGPATH_W) 'mod_accesslog.c'; else $(CYGPATH_W) '$(srcdir)/mod_accesslog.c'; fi` + +lighttpd-mod_alias.o: mod_alias.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_alias.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_alias.Tpo -c -o lighttpd-mod_alias.o `test -f 'mod_alias.c' || echo '$(srcdir)/'`mod_alias.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_alias.Tpo $(DEPDIR)/lighttpd-mod_alias.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_alias.c' object='lighttpd-mod_alias.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_alias.o `test -f 'mod_alias.c' || echo '$(srcdir)/'`mod_alias.c + +lighttpd-mod_alias.obj: mod_alias.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_alias.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_alias.Tpo -c -o lighttpd-mod_alias.obj `if test -f 'mod_alias.c'; then $(CYGPATH_W) 'mod_alias.c'; else $(CYGPATH_W) '$(srcdir)/mod_alias.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_alias.Tpo $(DEPDIR)/lighttpd-mod_alias.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_alias.c' object='lighttpd-mod_alias.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_alias.obj `if test -f 'mod_alias.c'; then $(CYGPATH_W) 'mod_alias.c'; else $(CYGPATH_W) '$(srcdir)/mod_alias.c'; fi` + +lighttpd-mod_auth.o: mod_auth.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_auth.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_auth.Tpo -c -o lighttpd-mod_auth.o `test -f 'mod_auth.c' || echo '$(srcdir)/'`mod_auth.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_auth.Tpo $(DEPDIR)/lighttpd-mod_auth.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_auth.c' object='lighttpd-mod_auth.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_auth.o `test -f 'mod_auth.c' || echo '$(srcdir)/'`mod_auth.c + +lighttpd-mod_auth.obj: mod_auth.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_auth.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_auth.Tpo -c -o lighttpd-mod_auth.obj `if test -f 'mod_auth.c'; then $(CYGPATH_W) 'mod_auth.c'; else $(CYGPATH_W) '$(srcdir)/mod_auth.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_auth.Tpo $(DEPDIR)/lighttpd-mod_auth.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_auth.c' object='lighttpd-mod_auth.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_auth.obj `if test -f 'mod_auth.c'; then $(CYGPATH_W) 'mod_auth.c'; else $(CYGPATH_W) '$(srcdir)/mod_auth.c'; fi` + +lighttpd-mod_authn_file.o: mod_authn_file.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_authn_file.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_authn_file.Tpo -c -o lighttpd-mod_authn_file.o `test -f 'mod_authn_file.c' || echo '$(srcdir)/'`mod_authn_file.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_authn_file.Tpo $(DEPDIR)/lighttpd-mod_authn_file.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_authn_file.c' object='lighttpd-mod_authn_file.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_authn_file.o `test -f 'mod_authn_file.c' || echo '$(srcdir)/'`mod_authn_file.c + +lighttpd-mod_authn_file.obj: mod_authn_file.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_authn_file.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_authn_file.Tpo -c -o lighttpd-mod_authn_file.obj `if test -f 'mod_authn_file.c'; then $(CYGPATH_W) 'mod_authn_file.c'; else $(CYGPATH_W) '$(srcdir)/mod_authn_file.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_authn_file.Tpo $(DEPDIR)/lighttpd-mod_authn_file.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_authn_file.c' object='lighttpd-mod_authn_file.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_authn_file.obj `if test -f 'mod_authn_file.c'; then $(CYGPATH_W) 'mod_authn_file.c'; else $(CYGPATH_W) '$(srcdir)/mod_authn_file.c'; fi` + +lighttpd-mod_cgi.o: mod_cgi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_cgi.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_cgi.Tpo -c -o lighttpd-mod_cgi.o `test -f 'mod_cgi.c' || echo '$(srcdir)/'`mod_cgi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_cgi.Tpo $(DEPDIR)/lighttpd-mod_cgi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cgi.c' object='lighttpd-mod_cgi.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_cgi.o `test -f 'mod_cgi.c' || echo '$(srcdir)/'`mod_cgi.c + +lighttpd-mod_cgi.obj: mod_cgi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_cgi.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_cgi.Tpo -c -o lighttpd-mod_cgi.obj `if test -f 'mod_cgi.c'; then $(CYGPATH_W) 'mod_cgi.c'; else $(CYGPATH_W) '$(srcdir)/mod_cgi.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_cgi.Tpo $(DEPDIR)/lighttpd-mod_cgi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cgi.c' object='lighttpd-mod_cgi.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_cgi.obj `if test -f 'mod_cgi.c'; then $(CYGPATH_W) 'mod_cgi.c'; else $(CYGPATH_W) '$(srcdir)/mod_cgi.c'; fi` + +lighttpd-mod_compress.o: mod_compress.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_compress.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_compress.Tpo -c -o lighttpd-mod_compress.o `test -f 'mod_compress.c' || echo '$(srcdir)/'`mod_compress.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_compress.Tpo $(DEPDIR)/lighttpd-mod_compress.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_compress.c' object='lighttpd-mod_compress.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_compress.o `test -f 'mod_compress.c' || echo '$(srcdir)/'`mod_compress.c + +lighttpd-mod_compress.obj: mod_compress.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_compress.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_compress.Tpo -c -o lighttpd-mod_compress.obj `if test -f 'mod_compress.c'; then $(CYGPATH_W) 'mod_compress.c'; else $(CYGPATH_W) '$(srcdir)/mod_compress.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_compress.Tpo $(DEPDIR)/lighttpd-mod_compress.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_compress.c' object='lighttpd-mod_compress.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_compress.obj `if test -f 'mod_compress.c'; then $(CYGPATH_W) 'mod_compress.c'; else $(CYGPATH_W) '$(srcdir)/mod_compress.c'; fi` + +lighttpd-mod_deflate.o: mod_deflate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_deflate.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_deflate.Tpo -c -o lighttpd-mod_deflate.o `test -f 'mod_deflate.c' || echo '$(srcdir)/'`mod_deflate.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_deflate.Tpo $(DEPDIR)/lighttpd-mod_deflate.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_deflate.c' object='lighttpd-mod_deflate.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_deflate.o `test -f 'mod_deflate.c' || echo '$(srcdir)/'`mod_deflate.c + +lighttpd-mod_deflate.obj: mod_deflate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_deflate.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_deflate.Tpo -c -o lighttpd-mod_deflate.obj `if test -f 'mod_deflate.c'; then $(CYGPATH_W) 'mod_deflate.c'; else $(CYGPATH_W) '$(srcdir)/mod_deflate.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_deflate.Tpo $(DEPDIR)/lighttpd-mod_deflate.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_deflate.c' object='lighttpd-mod_deflate.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_deflate.obj `if test -f 'mod_deflate.c'; then $(CYGPATH_W) 'mod_deflate.c'; else $(CYGPATH_W) '$(srcdir)/mod_deflate.c'; fi` + +lighttpd-mod_dirlisting.o: mod_dirlisting.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_dirlisting.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_dirlisting.Tpo -c -o lighttpd-mod_dirlisting.o `test -f 'mod_dirlisting.c' || echo '$(srcdir)/'`mod_dirlisting.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_dirlisting.Tpo $(DEPDIR)/lighttpd-mod_dirlisting.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_dirlisting.c' object='lighttpd-mod_dirlisting.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_dirlisting.o `test -f 'mod_dirlisting.c' || echo '$(srcdir)/'`mod_dirlisting.c + +lighttpd-mod_dirlisting.obj: mod_dirlisting.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_dirlisting.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_dirlisting.Tpo -c -o lighttpd-mod_dirlisting.obj `if test -f 'mod_dirlisting.c'; then $(CYGPATH_W) 'mod_dirlisting.c'; else $(CYGPATH_W) '$(srcdir)/mod_dirlisting.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_dirlisting.Tpo $(DEPDIR)/lighttpd-mod_dirlisting.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_dirlisting.c' object='lighttpd-mod_dirlisting.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_dirlisting.obj `if test -f 'mod_dirlisting.c'; then $(CYGPATH_W) 'mod_dirlisting.c'; else $(CYGPATH_W) '$(srcdir)/mod_dirlisting.c'; fi` + +lighttpd-mod_evasive.o: mod_evasive.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_evasive.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_evasive.Tpo -c -o lighttpd-mod_evasive.o `test -f 'mod_evasive.c' || echo '$(srcdir)/'`mod_evasive.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_evasive.Tpo $(DEPDIR)/lighttpd-mod_evasive.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_evasive.c' object='lighttpd-mod_evasive.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_evasive.o `test -f 'mod_evasive.c' || echo '$(srcdir)/'`mod_evasive.c + +lighttpd-mod_evasive.obj: mod_evasive.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_evasive.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_evasive.Tpo -c -o lighttpd-mod_evasive.obj `if test -f 'mod_evasive.c'; then $(CYGPATH_W) 'mod_evasive.c'; else $(CYGPATH_W) '$(srcdir)/mod_evasive.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_evasive.Tpo $(DEPDIR)/lighttpd-mod_evasive.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_evasive.c' object='lighttpd-mod_evasive.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_evasive.obj `if test -f 'mod_evasive.c'; then $(CYGPATH_W) 'mod_evasive.c'; else $(CYGPATH_W) '$(srcdir)/mod_evasive.c'; fi` + +lighttpd-mod_expire.o: mod_expire.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_expire.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_expire.Tpo -c -o lighttpd-mod_expire.o `test -f 'mod_expire.c' || echo '$(srcdir)/'`mod_expire.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_expire.Tpo $(DEPDIR)/lighttpd-mod_expire.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_expire.c' object='lighttpd-mod_expire.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_expire.o `test -f 'mod_expire.c' || echo '$(srcdir)/'`mod_expire.c + +lighttpd-mod_expire.obj: mod_expire.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_expire.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_expire.Tpo -c -o lighttpd-mod_expire.obj `if test -f 'mod_expire.c'; then $(CYGPATH_W) 'mod_expire.c'; else $(CYGPATH_W) '$(srcdir)/mod_expire.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_expire.Tpo $(DEPDIR)/lighttpd-mod_expire.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_expire.c' object='lighttpd-mod_expire.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_expire.obj `if test -f 'mod_expire.c'; then $(CYGPATH_W) 'mod_expire.c'; else $(CYGPATH_W) '$(srcdir)/mod_expire.c'; fi` + +lighttpd-mod_extforward.o: mod_extforward.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_extforward.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_extforward.Tpo -c -o lighttpd-mod_extforward.o `test -f 'mod_extforward.c' || echo '$(srcdir)/'`mod_extforward.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_extforward.Tpo $(DEPDIR)/lighttpd-mod_extforward.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_extforward.c' object='lighttpd-mod_extforward.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_extforward.o `test -f 'mod_extforward.c' || echo '$(srcdir)/'`mod_extforward.c + +lighttpd-mod_extforward.obj: mod_extforward.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_extforward.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_extforward.Tpo -c -o lighttpd-mod_extforward.obj `if test -f 'mod_extforward.c'; then $(CYGPATH_W) 'mod_extforward.c'; else $(CYGPATH_W) '$(srcdir)/mod_extforward.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_extforward.Tpo $(DEPDIR)/lighttpd-mod_extforward.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_extforward.c' object='lighttpd-mod_extforward.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_extforward.obj `if test -f 'mod_extforward.c'; then $(CYGPATH_W) 'mod_extforward.c'; else $(CYGPATH_W) '$(srcdir)/mod_extforward.c'; fi` + +lighttpd-mod_fastcgi.o: mod_fastcgi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_fastcgi.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_fastcgi.Tpo -c -o lighttpd-mod_fastcgi.o `test -f 'mod_fastcgi.c' || echo '$(srcdir)/'`mod_fastcgi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_fastcgi.Tpo $(DEPDIR)/lighttpd-mod_fastcgi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_fastcgi.c' object='lighttpd-mod_fastcgi.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_fastcgi.o `test -f 'mod_fastcgi.c' || echo '$(srcdir)/'`mod_fastcgi.c + +lighttpd-mod_fastcgi.obj: mod_fastcgi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_fastcgi.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_fastcgi.Tpo -c -o lighttpd-mod_fastcgi.obj `if test -f 'mod_fastcgi.c'; then $(CYGPATH_W) 'mod_fastcgi.c'; else $(CYGPATH_W) '$(srcdir)/mod_fastcgi.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_fastcgi.Tpo $(DEPDIR)/lighttpd-mod_fastcgi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_fastcgi.c' object='lighttpd-mod_fastcgi.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_fastcgi.obj `if test -f 'mod_fastcgi.c'; then $(CYGPATH_W) 'mod_fastcgi.c'; else $(CYGPATH_W) '$(srcdir)/mod_fastcgi.c'; fi` + +lighttpd-mod_flv_streaming.o: mod_flv_streaming.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_flv_streaming.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_flv_streaming.Tpo -c -o lighttpd-mod_flv_streaming.o `test -f 'mod_flv_streaming.c' || echo '$(srcdir)/'`mod_flv_streaming.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_flv_streaming.Tpo $(DEPDIR)/lighttpd-mod_flv_streaming.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_flv_streaming.c' object='lighttpd-mod_flv_streaming.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_flv_streaming.o `test -f 'mod_flv_streaming.c' || echo '$(srcdir)/'`mod_flv_streaming.c + +lighttpd-mod_flv_streaming.obj: mod_flv_streaming.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_flv_streaming.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_flv_streaming.Tpo -c -o lighttpd-mod_flv_streaming.obj `if test -f 'mod_flv_streaming.c'; then $(CYGPATH_W) 'mod_flv_streaming.c'; else $(CYGPATH_W) '$(srcdir)/mod_flv_streaming.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_flv_streaming.Tpo $(DEPDIR)/lighttpd-mod_flv_streaming.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_flv_streaming.c' object='lighttpd-mod_flv_streaming.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_flv_streaming.obj `if test -f 'mod_flv_streaming.c'; then $(CYGPATH_W) 'mod_flv_streaming.c'; else $(CYGPATH_W) '$(srcdir)/mod_flv_streaming.c'; fi` + +lighttpd-mod_indexfile.o: mod_indexfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_indexfile.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_indexfile.Tpo -c -o lighttpd-mod_indexfile.o `test -f 'mod_indexfile.c' || echo '$(srcdir)/'`mod_indexfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_indexfile.Tpo $(DEPDIR)/lighttpd-mod_indexfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_indexfile.c' object='lighttpd-mod_indexfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_indexfile.o `test -f 'mod_indexfile.c' || echo '$(srcdir)/'`mod_indexfile.c + +lighttpd-mod_indexfile.obj: mod_indexfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_indexfile.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_indexfile.Tpo -c -o lighttpd-mod_indexfile.obj `if test -f 'mod_indexfile.c'; then $(CYGPATH_W) 'mod_indexfile.c'; else $(CYGPATH_W) '$(srcdir)/mod_indexfile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_indexfile.Tpo $(DEPDIR)/lighttpd-mod_indexfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_indexfile.c' object='lighttpd-mod_indexfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_indexfile.obj `if test -f 'mod_indexfile.c'; then $(CYGPATH_W) 'mod_indexfile.c'; else $(CYGPATH_W) '$(srcdir)/mod_indexfile.c'; fi` + +lighttpd-mod_proxy.o: mod_proxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_proxy.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_proxy.Tpo -c -o lighttpd-mod_proxy.o `test -f 'mod_proxy.c' || echo '$(srcdir)/'`mod_proxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_proxy.Tpo $(DEPDIR)/lighttpd-mod_proxy.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_proxy.c' object='lighttpd-mod_proxy.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_proxy.o `test -f 'mod_proxy.c' || echo '$(srcdir)/'`mod_proxy.c + +lighttpd-mod_proxy.obj: mod_proxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_proxy.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_proxy.Tpo -c -o lighttpd-mod_proxy.obj `if test -f 'mod_proxy.c'; then $(CYGPATH_W) 'mod_proxy.c'; else $(CYGPATH_W) '$(srcdir)/mod_proxy.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_proxy.Tpo $(DEPDIR)/lighttpd-mod_proxy.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_proxy.c' object='lighttpd-mod_proxy.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_proxy.obj `if test -f 'mod_proxy.c'; then $(CYGPATH_W) 'mod_proxy.c'; else $(CYGPATH_W) '$(srcdir)/mod_proxy.c'; fi` + +lighttpd-mod_redirect.o: mod_redirect.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_redirect.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_redirect.Tpo -c -o lighttpd-mod_redirect.o `test -f 'mod_redirect.c' || echo '$(srcdir)/'`mod_redirect.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_redirect.Tpo $(DEPDIR)/lighttpd-mod_redirect.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_redirect.c' object='lighttpd-mod_redirect.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_redirect.o `test -f 'mod_redirect.c' || echo '$(srcdir)/'`mod_redirect.c + +lighttpd-mod_redirect.obj: mod_redirect.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_redirect.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_redirect.Tpo -c -o lighttpd-mod_redirect.obj `if test -f 'mod_redirect.c'; then $(CYGPATH_W) 'mod_redirect.c'; else $(CYGPATH_W) '$(srcdir)/mod_redirect.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_redirect.Tpo $(DEPDIR)/lighttpd-mod_redirect.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_redirect.c' object='lighttpd-mod_redirect.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_redirect.obj `if test -f 'mod_redirect.c'; then $(CYGPATH_W) 'mod_redirect.c'; else $(CYGPATH_W) '$(srcdir)/mod_redirect.c'; fi` + +lighttpd-mod_rewrite.o: mod_rewrite.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_rewrite.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_rewrite.Tpo -c -o lighttpd-mod_rewrite.o `test -f 'mod_rewrite.c' || echo '$(srcdir)/'`mod_rewrite.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_rewrite.Tpo $(DEPDIR)/lighttpd-mod_rewrite.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_rewrite.c' object='lighttpd-mod_rewrite.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_rewrite.o `test -f 'mod_rewrite.c' || echo '$(srcdir)/'`mod_rewrite.c + +lighttpd-mod_rewrite.obj: mod_rewrite.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_rewrite.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_rewrite.Tpo -c -o lighttpd-mod_rewrite.obj `if test -f 'mod_rewrite.c'; then $(CYGPATH_W) 'mod_rewrite.c'; else $(CYGPATH_W) '$(srcdir)/mod_rewrite.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_rewrite.Tpo $(DEPDIR)/lighttpd-mod_rewrite.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_rewrite.c' object='lighttpd-mod_rewrite.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_rewrite.obj `if test -f 'mod_rewrite.c'; then $(CYGPATH_W) 'mod_rewrite.c'; else $(CYGPATH_W) '$(srcdir)/mod_rewrite.c'; fi` + +lighttpd-mod_rrdtool.o: mod_rrdtool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_rrdtool.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_rrdtool.Tpo -c -o lighttpd-mod_rrdtool.o `test -f 'mod_rrdtool.c' || echo '$(srcdir)/'`mod_rrdtool.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_rrdtool.Tpo $(DEPDIR)/lighttpd-mod_rrdtool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_rrdtool.c' object='lighttpd-mod_rrdtool.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_rrdtool.o `test -f 'mod_rrdtool.c' || echo '$(srcdir)/'`mod_rrdtool.c + +lighttpd-mod_rrdtool.obj: mod_rrdtool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_rrdtool.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_rrdtool.Tpo -c -o lighttpd-mod_rrdtool.obj `if test -f 'mod_rrdtool.c'; then $(CYGPATH_W) 'mod_rrdtool.c'; else $(CYGPATH_W) '$(srcdir)/mod_rrdtool.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_rrdtool.Tpo $(DEPDIR)/lighttpd-mod_rrdtool.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_rrdtool.c' object='lighttpd-mod_rrdtool.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_rrdtool.obj `if test -f 'mod_rrdtool.c'; then $(CYGPATH_W) 'mod_rrdtool.c'; else $(CYGPATH_W) '$(srcdir)/mod_rrdtool.c'; fi` + +lighttpd-mod_scgi.o: mod_scgi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_scgi.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_scgi.Tpo -c -o lighttpd-mod_scgi.o `test -f 'mod_scgi.c' || echo '$(srcdir)/'`mod_scgi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_scgi.Tpo $(DEPDIR)/lighttpd-mod_scgi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_scgi.c' object='lighttpd-mod_scgi.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_scgi.o `test -f 'mod_scgi.c' || echo '$(srcdir)/'`mod_scgi.c + +lighttpd-mod_scgi.obj: mod_scgi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_scgi.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_scgi.Tpo -c -o lighttpd-mod_scgi.obj `if test -f 'mod_scgi.c'; then $(CYGPATH_W) 'mod_scgi.c'; else $(CYGPATH_W) '$(srcdir)/mod_scgi.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_scgi.Tpo $(DEPDIR)/lighttpd-mod_scgi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_scgi.c' object='lighttpd-mod_scgi.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_scgi.obj `if test -f 'mod_scgi.c'; then $(CYGPATH_W) 'mod_scgi.c'; else $(CYGPATH_W) '$(srcdir)/mod_scgi.c'; fi` + +lighttpd-mod_secdownload.o: mod_secdownload.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_secdownload.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_secdownload.Tpo -c -o lighttpd-mod_secdownload.o `test -f 'mod_secdownload.c' || echo '$(srcdir)/'`mod_secdownload.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_secdownload.Tpo $(DEPDIR)/lighttpd-mod_secdownload.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_secdownload.c' object='lighttpd-mod_secdownload.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_secdownload.o `test -f 'mod_secdownload.c' || echo '$(srcdir)/'`mod_secdownload.c + +lighttpd-mod_secdownload.obj: mod_secdownload.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_secdownload.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_secdownload.Tpo -c -o lighttpd-mod_secdownload.obj `if test -f 'mod_secdownload.c'; then $(CYGPATH_W) 'mod_secdownload.c'; else $(CYGPATH_W) '$(srcdir)/mod_secdownload.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_secdownload.Tpo $(DEPDIR)/lighttpd-mod_secdownload.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_secdownload.c' object='lighttpd-mod_secdownload.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_secdownload.obj `if test -f 'mod_secdownload.c'; then $(CYGPATH_W) 'mod_secdownload.c'; else $(CYGPATH_W) '$(srcdir)/mod_secdownload.c'; fi` + +lighttpd-mod_setenv.o: mod_setenv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_setenv.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_setenv.Tpo -c -o lighttpd-mod_setenv.o `test -f 'mod_setenv.c' || echo '$(srcdir)/'`mod_setenv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_setenv.Tpo $(DEPDIR)/lighttpd-mod_setenv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_setenv.c' object='lighttpd-mod_setenv.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_setenv.o `test -f 'mod_setenv.c' || echo '$(srcdir)/'`mod_setenv.c + +lighttpd-mod_setenv.obj: mod_setenv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_setenv.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_setenv.Tpo -c -o lighttpd-mod_setenv.obj `if test -f 'mod_setenv.c'; then $(CYGPATH_W) 'mod_setenv.c'; else $(CYGPATH_W) '$(srcdir)/mod_setenv.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_setenv.Tpo $(DEPDIR)/lighttpd-mod_setenv.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_setenv.c' object='lighttpd-mod_setenv.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_setenv.obj `if test -f 'mod_setenv.c'; then $(CYGPATH_W) 'mod_setenv.c'; else $(CYGPATH_W) '$(srcdir)/mod_setenv.c'; fi` + +lighttpd-mod_simple_vhost.o: mod_simple_vhost.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_simple_vhost.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_simple_vhost.Tpo -c -o lighttpd-mod_simple_vhost.o `test -f 'mod_simple_vhost.c' || echo '$(srcdir)/'`mod_simple_vhost.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_simple_vhost.Tpo $(DEPDIR)/lighttpd-mod_simple_vhost.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_simple_vhost.c' object='lighttpd-mod_simple_vhost.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_simple_vhost.o `test -f 'mod_simple_vhost.c' || echo '$(srcdir)/'`mod_simple_vhost.c + +lighttpd-mod_simple_vhost.obj: mod_simple_vhost.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_simple_vhost.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_simple_vhost.Tpo -c -o lighttpd-mod_simple_vhost.obj `if test -f 'mod_simple_vhost.c'; then $(CYGPATH_W) 'mod_simple_vhost.c'; else $(CYGPATH_W) '$(srcdir)/mod_simple_vhost.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_simple_vhost.Tpo $(DEPDIR)/lighttpd-mod_simple_vhost.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_simple_vhost.c' object='lighttpd-mod_simple_vhost.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_simple_vhost.obj `if test -f 'mod_simple_vhost.c'; then $(CYGPATH_W) 'mod_simple_vhost.c'; else $(CYGPATH_W) '$(srcdir)/mod_simple_vhost.c'; fi` + +lighttpd-mod_ssi_exprparser.o: mod_ssi_exprparser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_ssi_exprparser.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_ssi_exprparser.Tpo -c -o lighttpd-mod_ssi_exprparser.o `test -f 'mod_ssi_exprparser.c' || echo '$(srcdir)/'`mod_ssi_exprparser.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_ssi_exprparser.Tpo $(DEPDIR)/lighttpd-mod_ssi_exprparser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_ssi_exprparser.c' object='lighttpd-mod_ssi_exprparser.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_ssi_exprparser.o `test -f 'mod_ssi_exprparser.c' || echo '$(srcdir)/'`mod_ssi_exprparser.c + +lighttpd-mod_ssi_exprparser.obj: mod_ssi_exprparser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_ssi_exprparser.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_ssi_exprparser.Tpo -c -o lighttpd-mod_ssi_exprparser.obj `if test -f 'mod_ssi_exprparser.c'; then $(CYGPATH_W) 'mod_ssi_exprparser.c'; else $(CYGPATH_W) '$(srcdir)/mod_ssi_exprparser.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_ssi_exprparser.Tpo $(DEPDIR)/lighttpd-mod_ssi_exprparser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_ssi_exprparser.c' object='lighttpd-mod_ssi_exprparser.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_ssi_exprparser.obj `if test -f 'mod_ssi_exprparser.c'; then $(CYGPATH_W) 'mod_ssi_exprparser.c'; else $(CYGPATH_W) '$(srcdir)/mod_ssi_exprparser.c'; fi` + +lighttpd-mod_ssi_expr.o: mod_ssi_expr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_ssi_expr.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_ssi_expr.Tpo -c -o lighttpd-mod_ssi_expr.o `test -f 'mod_ssi_expr.c' || echo '$(srcdir)/'`mod_ssi_expr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_ssi_expr.Tpo $(DEPDIR)/lighttpd-mod_ssi_expr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_ssi_expr.c' object='lighttpd-mod_ssi_expr.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_ssi_expr.o `test -f 'mod_ssi_expr.c' || echo '$(srcdir)/'`mod_ssi_expr.c + +lighttpd-mod_ssi_expr.obj: mod_ssi_expr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_ssi_expr.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_ssi_expr.Tpo -c -o lighttpd-mod_ssi_expr.obj `if test -f 'mod_ssi_expr.c'; then $(CYGPATH_W) 'mod_ssi_expr.c'; else $(CYGPATH_W) '$(srcdir)/mod_ssi_expr.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_ssi_expr.Tpo $(DEPDIR)/lighttpd-mod_ssi_expr.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_ssi_expr.c' object='lighttpd-mod_ssi_expr.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_ssi_expr.obj `if test -f 'mod_ssi_expr.c'; then $(CYGPATH_W) 'mod_ssi_expr.c'; else $(CYGPATH_W) '$(srcdir)/mod_ssi_expr.c'; fi` + +lighttpd-mod_ssi.o: mod_ssi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_ssi.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_ssi.Tpo -c -o lighttpd-mod_ssi.o `test -f 'mod_ssi.c' || echo '$(srcdir)/'`mod_ssi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_ssi.Tpo $(DEPDIR)/lighttpd-mod_ssi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_ssi.c' object='lighttpd-mod_ssi.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_ssi.o `test -f 'mod_ssi.c' || echo '$(srcdir)/'`mod_ssi.c + +lighttpd-mod_ssi.obj: mod_ssi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_ssi.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_ssi.Tpo -c -o lighttpd-mod_ssi.obj `if test -f 'mod_ssi.c'; then $(CYGPATH_W) 'mod_ssi.c'; else $(CYGPATH_W) '$(srcdir)/mod_ssi.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_ssi.Tpo $(DEPDIR)/lighttpd-mod_ssi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_ssi.c' object='lighttpd-mod_ssi.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_ssi.obj `if test -f 'mod_ssi.c'; then $(CYGPATH_W) 'mod_ssi.c'; else $(CYGPATH_W) '$(srcdir)/mod_ssi.c'; fi` + +lighttpd-mod_staticfile.o: mod_staticfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_staticfile.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_staticfile.Tpo -c -o lighttpd-mod_staticfile.o `test -f 'mod_staticfile.c' || echo '$(srcdir)/'`mod_staticfile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_staticfile.Tpo $(DEPDIR)/lighttpd-mod_staticfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_staticfile.c' object='lighttpd-mod_staticfile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_staticfile.o `test -f 'mod_staticfile.c' || echo '$(srcdir)/'`mod_staticfile.c + +lighttpd-mod_staticfile.obj: mod_staticfile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_staticfile.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_staticfile.Tpo -c -o lighttpd-mod_staticfile.obj `if test -f 'mod_staticfile.c'; then $(CYGPATH_W) 'mod_staticfile.c'; else $(CYGPATH_W) '$(srcdir)/mod_staticfile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_staticfile.Tpo $(DEPDIR)/lighttpd-mod_staticfile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_staticfile.c' object='lighttpd-mod_staticfile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_staticfile.obj `if test -f 'mod_staticfile.c'; then $(CYGPATH_W) 'mod_staticfile.c'; else $(CYGPATH_W) '$(srcdir)/mod_staticfile.c'; fi` + +lighttpd-mod_status.o: mod_status.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_status.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_status.Tpo -c -o lighttpd-mod_status.o `test -f 'mod_status.c' || echo '$(srcdir)/'`mod_status.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_status.Tpo $(DEPDIR)/lighttpd-mod_status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_status.c' object='lighttpd-mod_status.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_status.o `test -f 'mod_status.c' || echo '$(srcdir)/'`mod_status.c + +lighttpd-mod_status.obj: mod_status.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_status.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_status.Tpo -c -o lighttpd-mod_status.obj `if test -f 'mod_status.c'; then $(CYGPATH_W) 'mod_status.c'; else $(CYGPATH_W) '$(srcdir)/mod_status.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_status.Tpo $(DEPDIR)/lighttpd-mod_status.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_status.c' object='lighttpd-mod_status.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_status.obj `if test -f 'mod_status.c'; then $(CYGPATH_W) 'mod_status.c'; else $(CYGPATH_W) '$(srcdir)/mod_status.c'; fi` + +lighttpd-mod_uploadprogress.o: mod_uploadprogress.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_uploadprogress.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_uploadprogress.Tpo -c -o lighttpd-mod_uploadprogress.o `test -f 'mod_uploadprogress.c' || echo '$(srcdir)/'`mod_uploadprogress.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_uploadprogress.Tpo $(DEPDIR)/lighttpd-mod_uploadprogress.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_uploadprogress.c' object='lighttpd-mod_uploadprogress.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_uploadprogress.o `test -f 'mod_uploadprogress.c' || echo '$(srcdir)/'`mod_uploadprogress.c + +lighttpd-mod_uploadprogress.obj: mod_uploadprogress.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_uploadprogress.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_uploadprogress.Tpo -c -o lighttpd-mod_uploadprogress.obj `if test -f 'mod_uploadprogress.c'; then $(CYGPATH_W) 'mod_uploadprogress.c'; else $(CYGPATH_W) '$(srcdir)/mod_uploadprogress.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_uploadprogress.Tpo $(DEPDIR)/lighttpd-mod_uploadprogress.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_uploadprogress.c' object='lighttpd-mod_uploadprogress.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_uploadprogress.obj `if test -f 'mod_uploadprogress.c'; then $(CYGPATH_W) 'mod_uploadprogress.c'; else $(CYGPATH_W) '$(srcdir)/mod_uploadprogress.c'; fi` + +lighttpd-mod_userdir.o: mod_userdir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_userdir.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_userdir.Tpo -c -o lighttpd-mod_userdir.o `test -f 'mod_userdir.c' || echo '$(srcdir)/'`mod_userdir.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_userdir.Tpo $(DEPDIR)/lighttpd-mod_userdir.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_userdir.c' object='lighttpd-mod_userdir.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_userdir.o `test -f 'mod_userdir.c' || echo '$(srcdir)/'`mod_userdir.c + +lighttpd-mod_userdir.obj: mod_userdir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_userdir.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_userdir.Tpo -c -o lighttpd-mod_userdir.obj `if test -f 'mod_userdir.c'; then $(CYGPATH_W) 'mod_userdir.c'; else $(CYGPATH_W) '$(srcdir)/mod_userdir.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_userdir.Tpo $(DEPDIR)/lighttpd-mod_userdir.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_userdir.c' object='lighttpd-mod_userdir.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_userdir.obj `if test -f 'mod_userdir.c'; then $(CYGPATH_W) 'mod_userdir.c'; else $(CYGPATH_W) '$(srcdir)/mod_userdir.c'; fi` + +lighttpd-mod_usertrack.o: mod_usertrack.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_usertrack.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_usertrack.Tpo -c -o lighttpd-mod_usertrack.o `test -f 'mod_usertrack.c' || echo '$(srcdir)/'`mod_usertrack.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_usertrack.Tpo $(DEPDIR)/lighttpd-mod_usertrack.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_usertrack.c' object='lighttpd-mod_usertrack.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_usertrack.o `test -f 'mod_usertrack.c' || echo '$(srcdir)/'`mod_usertrack.c + +lighttpd-mod_usertrack.obj: mod_usertrack.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_usertrack.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_usertrack.Tpo -c -o lighttpd-mod_usertrack.obj `if test -f 'mod_usertrack.c'; then $(CYGPATH_W) 'mod_usertrack.c'; else $(CYGPATH_W) '$(srcdir)/mod_usertrack.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_usertrack.Tpo $(DEPDIR)/lighttpd-mod_usertrack.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_usertrack.c' object='lighttpd-mod_usertrack.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_usertrack.obj `if test -f 'mod_usertrack.c'; then $(CYGPATH_W) 'mod_usertrack.c'; else $(CYGPATH_W) '$(srcdir)/mod_usertrack.c'; fi` + +lighttpd-mod_vhostdb.o: mod_vhostdb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_vhostdb.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_vhostdb.Tpo -c -o lighttpd-mod_vhostdb.o `test -f 'mod_vhostdb.c' || echo '$(srcdir)/'`mod_vhostdb.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_vhostdb.Tpo $(DEPDIR)/lighttpd-mod_vhostdb.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb.c' object='lighttpd-mod_vhostdb.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_vhostdb.o `test -f 'mod_vhostdb.c' || echo '$(srcdir)/'`mod_vhostdb.c + +lighttpd-mod_vhostdb.obj: mod_vhostdb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_vhostdb.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_vhostdb.Tpo -c -o lighttpd-mod_vhostdb.obj `if test -f 'mod_vhostdb.c'; then $(CYGPATH_W) 'mod_vhostdb.c'; else $(CYGPATH_W) '$(srcdir)/mod_vhostdb.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_vhostdb.Tpo $(DEPDIR)/lighttpd-mod_vhostdb.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb.c' object='lighttpd-mod_vhostdb.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_vhostdb.obj `if test -f 'mod_vhostdb.c'; then $(CYGPATH_W) 'mod_vhostdb.c'; else $(CYGPATH_W) '$(srcdir)/mod_vhostdb.c'; fi` + +lighttpd-mod_webdav.o: mod_webdav.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_webdav.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_webdav.Tpo -c -o lighttpd-mod_webdav.o `test -f 'mod_webdav.c' || echo '$(srcdir)/'`mod_webdav.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_webdav.Tpo $(DEPDIR)/lighttpd-mod_webdav.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_webdav.c' object='lighttpd-mod_webdav.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_webdav.o `test -f 'mod_webdav.c' || echo '$(srcdir)/'`mod_webdav.c + +lighttpd-mod_webdav.obj: mod_webdav.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_webdav.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_webdav.Tpo -c -o lighttpd-mod_webdav.obj `if test -f 'mod_webdav.c'; then $(CYGPATH_W) 'mod_webdav.c'; else $(CYGPATH_W) '$(srcdir)/mod_webdav.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_webdav.Tpo $(DEPDIR)/lighttpd-mod_webdav.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_webdav.c' object='lighttpd-mod_webdav.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_webdav.obj `if test -f 'mod_webdav.c'; then $(CYGPATH_W) 'mod_webdav.c'; else $(CYGPATH_W) '$(srcdir)/mod_webdav.c'; fi` + +lighttpd-mod_geoip.o: mod_geoip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_geoip.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_geoip.Tpo -c -o lighttpd-mod_geoip.o `test -f 'mod_geoip.c' || echo '$(srcdir)/'`mod_geoip.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_geoip.Tpo $(DEPDIR)/lighttpd-mod_geoip.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_geoip.c' object='lighttpd-mod_geoip.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_geoip.o `test -f 'mod_geoip.c' || echo '$(srcdir)/'`mod_geoip.c + +lighttpd-mod_geoip.obj: mod_geoip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_geoip.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_geoip.Tpo -c -o lighttpd-mod_geoip.obj `if test -f 'mod_geoip.c'; then $(CYGPATH_W) 'mod_geoip.c'; else $(CYGPATH_W) '$(srcdir)/mod_geoip.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_geoip.Tpo $(DEPDIR)/lighttpd-mod_geoip.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_geoip.c' object='lighttpd-mod_geoip.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_geoip.obj `if test -f 'mod_geoip.c'; then $(CYGPATH_W) 'mod_geoip.c'; else $(CYGPATH_W) '$(srcdir)/mod_geoip.c'; fi` + +lighttpd-mod_cml.o: mod_cml.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_cml.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_cml.Tpo -c -o lighttpd-mod_cml.o `test -f 'mod_cml.c' || echo '$(srcdir)/'`mod_cml.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_cml.Tpo $(DEPDIR)/lighttpd-mod_cml.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cml.c' object='lighttpd-mod_cml.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_cml.o `test -f 'mod_cml.c' || echo '$(srcdir)/'`mod_cml.c + +lighttpd-mod_cml.obj: mod_cml.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_cml.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_cml.Tpo -c -o lighttpd-mod_cml.obj `if test -f 'mod_cml.c'; then $(CYGPATH_W) 'mod_cml.c'; else $(CYGPATH_W) '$(srcdir)/mod_cml.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_cml.Tpo $(DEPDIR)/lighttpd-mod_cml.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cml.c' object='lighttpd-mod_cml.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_cml.obj `if test -f 'mod_cml.c'; then $(CYGPATH_W) 'mod_cml.c'; else $(CYGPATH_W) '$(srcdir)/mod_cml.c'; fi` + +lighttpd-mod_cml_lua.o: mod_cml_lua.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_cml_lua.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_cml_lua.Tpo -c -o lighttpd-mod_cml_lua.o `test -f 'mod_cml_lua.c' || echo '$(srcdir)/'`mod_cml_lua.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_cml_lua.Tpo $(DEPDIR)/lighttpd-mod_cml_lua.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cml_lua.c' object='lighttpd-mod_cml_lua.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_cml_lua.o `test -f 'mod_cml_lua.c' || echo '$(srcdir)/'`mod_cml_lua.c + +lighttpd-mod_cml_lua.obj: mod_cml_lua.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_cml_lua.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_cml_lua.Tpo -c -o lighttpd-mod_cml_lua.obj `if test -f 'mod_cml_lua.c'; then $(CYGPATH_W) 'mod_cml_lua.c'; else $(CYGPATH_W) '$(srcdir)/mod_cml_lua.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_cml_lua.Tpo $(DEPDIR)/lighttpd-mod_cml_lua.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cml_lua.c' object='lighttpd-mod_cml_lua.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_cml_lua.obj `if test -f 'mod_cml_lua.c'; then $(CYGPATH_W) 'mod_cml_lua.c'; else $(CYGPATH_W) '$(srcdir)/mod_cml_lua.c'; fi` + +lighttpd-mod_cml_funcs.o: mod_cml_funcs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_cml_funcs.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_cml_funcs.Tpo -c -o lighttpd-mod_cml_funcs.o `test -f 'mod_cml_funcs.c' || echo '$(srcdir)/'`mod_cml_funcs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_cml_funcs.Tpo $(DEPDIR)/lighttpd-mod_cml_funcs.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cml_funcs.c' object='lighttpd-mod_cml_funcs.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_cml_funcs.o `test -f 'mod_cml_funcs.c' || echo '$(srcdir)/'`mod_cml_funcs.c + +lighttpd-mod_cml_funcs.obj: mod_cml_funcs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_cml_funcs.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_cml_funcs.Tpo -c -o lighttpd-mod_cml_funcs.obj `if test -f 'mod_cml_funcs.c'; then $(CYGPATH_W) 'mod_cml_funcs.c'; else $(CYGPATH_W) '$(srcdir)/mod_cml_funcs.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_cml_funcs.Tpo $(DEPDIR)/lighttpd-mod_cml_funcs.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_cml_funcs.c' object='lighttpd-mod_cml_funcs.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_cml_funcs.obj `if test -f 'mod_cml_funcs.c'; then $(CYGPATH_W) 'mod_cml_funcs.c'; else $(CYGPATH_W) '$(srcdir)/mod_cml_funcs.c'; fi` + +lighttpd-mod_magnet.o: mod_magnet.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_magnet.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_magnet.Tpo -c -o lighttpd-mod_magnet.o `test -f 'mod_magnet.c' || echo '$(srcdir)/'`mod_magnet.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_magnet.Tpo $(DEPDIR)/lighttpd-mod_magnet.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_magnet.c' object='lighttpd-mod_magnet.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_magnet.o `test -f 'mod_magnet.c' || echo '$(srcdir)/'`mod_magnet.c + +lighttpd-mod_magnet.obj: mod_magnet.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_magnet.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_magnet.Tpo -c -o lighttpd-mod_magnet.obj `if test -f 'mod_magnet.c'; then $(CYGPATH_W) 'mod_magnet.c'; else $(CYGPATH_W) '$(srcdir)/mod_magnet.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_magnet.Tpo $(DEPDIR)/lighttpd-mod_magnet.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_magnet.c' object='lighttpd-mod_magnet.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_magnet.obj `if test -f 'mod_magnet.c'; then $(CYGPATH_W) 'mod_magnet.c'; else $(CYGPATH_W) '$(srcdir)/mod_magnet.c'; fi` + +lighttpd-mod_magnet_cache.o: mod_magnet_cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_magnet_cache.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_magnet_cache.Tpo -c -o lighttpd-mod_magnet_cache.o `test -f 'mod_magnet_cache.c' || echo '$(srcdir)/'`mod_magnet_cache.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_magnet_cache.Tpo $(DEPDIR)/lighttpd-mod_magnet_cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_magnet_cache.c' object='lighttpd-mod_magnet_cache.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_magnet_cache.o `test -f 'mod_magnet_cache.c' || echo '$(srcdir)/'`mod_magnet_cache.c + +lighttpd-mod_magnet_cache.obj: mod_magnet_cache.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_magnet_cache.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_magnet_cache.Tpo -c -o lighttpd-mod_magnet_cache.obj `if test -f 'mod_magnet_cache.c'; then $(CYGPATH_W) 'mod_magnet_cache.c'; else $(CYGPATH_W) '$(srcdir)/mod_magnet_cache.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_magnet_cache.Tpo $(DEPDIR)/lighttpd-mod_magnet_cache.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_magnet_cache.c' object='lighttpd-mod_magnet_cache.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_magnet_cache.obj `if test -f 'mod_magnet_cache.c'; then $(CYGPATH_W) 'mod_magnet_cache.c'; else $(CYGPATH_W) '$(srcdir)/mod_magnet_cache.c'; fi` + +lighttpd-mod_authn_gssapi.o: mod_authn_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_authn_gssapi.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_authn_gssapi.Tpo -c -o lighttpd-mod_authn_gssapi.o `test -f 'mod_authn_gssapi.c' || echo '$(srcdir)/'`mod_authn_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_authn_gssapi.Tpo $(DEPDIR)/lighttpd-mod_authn_gssapi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_authn_gssapi.c' object='lighttpd-mod_authn_gssapi.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_authn_gssapi.o `test -f 'mod_authn_gssapi.c' || echo '$(srcdir)/'`mod_authn_gssapi.c + +lighttpd-mod_authn_gssapi.obj: mod_authn_gssapi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_authn_gssapi.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_authn_gssapi.Tpo -c -o lighttpd-mod_authn_gssapi.obj `if test -f 'mod_authn_gssapi.c'; then $(CYGPATH_W) 'mod_authn_gssapi.c'; else $(CYGPATH_W) '$(srcdir)/mod_authn_gssapi.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_authn_gssapi.Tpo $(DEPDIR)/lighttpd-mod_authn_gssapi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_authn_gssapi.c' object='lighttpd-mod_authn_gssapi.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_authn_gssapi.obj `if test -f 'mod_authn_gssapi.c'; then $(CYGPATH_W) 'mod_authn_gssapi.c'; else $(CYGPATH_W) '$(srcdir)/mod_authn_gssapi.c'; fi` + +lighttpd-mod_authn_ldap.o: mod_authn_ldap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_authn_ldap.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_authn_ldap.Tpo -c -o lighttpd-mod_authn_ldap.o `test -f 'mod_authn_ldap.c' || echo '$(srcdir)/'`mod_authn_ldap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_authn_ldap.Tpo $(DEPDIR)/lighttpd-mod_authn_ldap.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_authn_ldap.c' object='lighttpd-mod_authn_ldap.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_authn_ldap.o `test -f 'mod_authn_ldap.c' || echo '$(srcdir)/'`mod_authn_ldap.c + +lighttpd-mod_authn_ldap.obj: mod_authn_ldap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_authn_ldap.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_authn_ldap.Tpo -c -o lighttpd-mod_authn_ldap.obj `if test -f 'mod_authn_ldap.c'; then $(CYGPATH_W) 'mod_authn_ldap.c'; else $(CYGPATH_W) '$(srcdir)/mod_authn_ldap.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_authn_ldap.Tpo $(DEPDIR)/lighttpd-mod_authn_ldap.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_authn_ldap.c' object='lighttpd-mod_authn_ldap.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_authn_ldap.obj `if test -f 'mod_authn_ldap.c'; then $(CYGPATH_W) 'mod_authn_ldap.c'; else $(CYGPATH_W) '$(srcdir)/mod_authn_ldap.c'; fi` + +lighttpd-mod_vhostdb_ldap.o: mod_vhostdb_ldap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_vhostdb_ldap.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_vhostdb_ldap.Tpo -c -o lighttpd-mod_vhostdb_ldap.o `test -f 'mod_vhostdb_ldap.c' || echo '$(srcdir)/'`mod_vhostdb_ldap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_vhostdb_ldap.Tpo $(DEPDIR)/lighttpd-mod_vhostdb_ldap.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb_ldap.c' object='lighttpd-mod_vhostdb_ldap.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_vhostdb_ldap.o `test -f 'mod_vhostdb_ldap.c' || echo '$(srcdir)/'`mod_vhostdb_ldap.c + +lighttpd-mod_vhostdb_ldap.obj: mod_vhostdb_ldap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_vhostdb_ldap.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_vhostdb_ldap.Tpo -c -o lighttpd-mod_vhostdb_ldap.obj `if test -f 'mod_vhostdb_ldap.c'; then $(CYGPATH_W) 'mod_vhostdb_ldap.c'; else $(CYGPATH_W) '$(srcdir)/mod_vhostdb_ldap.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_vhostdb_ldap.Tpo $(DEPDIR)/lighttpd-mod_vhostdb_ldap.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb_ldap.c' object='lighttpd-mod_vhostdb_ldap.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_vhostdb_ldap.obj `if test -f 'mod_vhostdb_ldap.c'; then $(CYGPATH_W) 'mod_vhostdb_ldap.c'; else $(CYGPATH_W) '$(srcdir)/mod_vhostdb_ldap.c'; fi` + +lighttpd-mod_authn_pam.o: mod_authn_pam.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_authn_pam.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_authn_pam.Tpo -c -o lighttpd-mod_authn_pam.o `test -f 'mod_authn_pam.c' || echo '$(srcdir)/'`mod_authn_pam.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_authn_pam.Tpo $(DEPDIR)/lighttpd-mod_authn_pam.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_authn_pam.c' object='lighttpd-mod_authn_pam.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_authn_pam.o `test -f 'mod_authn_pam.c' || echo '$(srcdir)/'`mod_authn_pam.c + +lighttpd-mod_authn_pam.obj: mod_authn_pam.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_authn_pam.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_authn_pam.Tpo -c -o lighttpd-mod_authn_pam.obj `if test -f 'mod_authn_pam.c'; then $(CYGPATH_W) 'mod_authn_pam.c'; else $(CYGPATH_W) '$(srcdir)/mod_authn_pam.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_authn_pam.Tpo $(DEPDIR)/lighttpd-mod_authn_pam.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_authn_pam.c' object='lighttpd-mod_authn_pam.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_authn_pam.obj `if test -f 'mod_authn_pam.c'; then $(CYGPATH_W) 'mod_authn_pam.c'; else $(CYGPATH_W) '$(srcdir)/mod_authn_pam.c'; fi` + +lighttpd-mod_authn_mysql.o: mod_authn_mysql.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_authn_mysql.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_authn_mysql.Tpo -c -o lighttpd-mod_authn_mysql.o `test -f 'mod_authn_mysql.c' || echo '$(srcdir)/'`mod_authn_mysql.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_authn_mysql.Tpo $(DEPDIR)/lighttpd-mod_authn_mysql.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_authn_mysql.c' object='lighttpd-mod_authn_mysql.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_authn_mysql.o `test -f 'mod_authn_mysql.c' || echo '$(srcdir)/'`mod_authn_mysql.c + +lighttpd-mod_authn_mysql.obj: mod_authn_mysql.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_authn_mysql.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_authn_mysql.Tpo -c -o lighttpd-mod_authn_mysql.obj `if test -f 'mod_authn_mysql.c'; then $(CYGPATH_W) 'mod_authn_mysql.c'; else $(CYGPATH_W) '$(srcdir)/mod_authn_mysql.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_authn_mysql.Tpo $(DEPDIR)/lighttpd-mod_authn_mysql.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_authn_mysql.c' object='lighttpd-mod_authn_mysql.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_authn_mysql.obj `if test -f 'mod_authn_mysql.c'; then $(CYGPATH_W) 'mod_authn_mysql.c'; else $(CYGPATH_W) '$(srcdir)/mod_authn_mysql.c'; fi` + +lighttpd-mod_mysql_vhost.o: mod_mysql_vhost.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_mysql_vhost.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_mysql_vhost.Tpo -c -o lighttpd-mod_mysql_vhost.o `test -f 'mod_mysql_vhost.c' || echo '$(srcdir)/'`mod_mysql_vhost.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_mysql_vhost.Tpo $(DEPDIR)/lighttpd-mod_mysql_vhost.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_mysql_vhost.c' object='lighttpd-mod_mysql_vhost.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_mysql_vhost.o `test -f 'mod_mysql_vhost.c' || echo '$(srcdir)/'`mod_mysql_vhost.c + +lighttpd-mod_mysql_vhost.obj: mod_mysql_vhost.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_mysql_vhost.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_mysql_vhost.Tpo -c -o lighttpd-mod_mysql_vhost.obj `if test -f 'mod_mysql_vhost.c'; then $(CYGPATH_W) 'mod_mysql_vhost.c'; else $(CYGPATH_W) '$(srcdir)/mod_mysql_vhost.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_mysql_vhost.Tpo $(DEPDIR)/lighttpd-mod_mysql_vhost.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_mysql_vhost.c' object='lighttpd-mod_mysql_vhost.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_mysql_vhost.obj `if test -f 'mod_mysql_vhost.c'; then $(CYGPATH_W) 'mod_mysql_vhost.c'; else $(CYGPATH_W) '$(srcdir)/mod_mysql_vhost.c'; fi` + +lighttpd-mod_vhostdb_mysql.o: mod_vhostdb_mysql.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_vhostdb_mysql.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_vhostdb_mysql.Tpo -c -o lighttpd-mod_vhostdb_mysql.o `test -f 'mod_vhostdb_mysql.c' || echo '$(srcdir)/'`mod_vhostdb_mysql.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_vhostdb_mysql.Tpo $(DEPDIR)/lighttpd-mod_vhostdb_mysql.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb_mysql.c' object='lighttpd-mod_vhostdb_mysql.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_vhostdb_mysql.o `test -f 'mod_vhostdb_mysql.c' || echo '$(srcdir)/'`mod_vhostdb_mysql.c + +lighttpd-mod_vhostdb_mysql.obj: mod_vhostdb_mysql.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_vhostdb_mysql.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_vhostdb_mysql.Tpo -c -o lighttpd-mod_vhostdb_mysql.obj `if test -f 'mod_vhostdb_mysql.c'; then $(CYGPATH_W) 'mod_vhostdb_mysql.c'; else $(CYGPATH_W) '$(srcdir)/mod_vhostdb_mysql.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_vhostdb_mysql.Tpo $(DEPDIR)/lighttpd-mod_vhostdb_mysql.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb_mysql.c' object='lighttpd-mod_vhostdb_mysql.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_vhostdb_mysql.obj `if test -f 'mod_vhostdb_mysql.c'; then $(CYGPATH_W) 'mod_vhostdb_mysql.c'; else $(CYGPATH_W) '$(srcdir)/mod_vhostdb_mysql.c'; fi` + +lighttpd-mod_vhostdb_pgsql.o: mod_vhostdb_pgsql.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_vhostdb_pgsql.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_vhostdb_pgsql.Tpo -c -o lighttpd-mod_vhostdb_pgsql.o `test -f 'mod_vhostdb_pgsql.c' || echo '$(srcdir)/'`mod_vhostdb_pgsql.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_vhostdb_pgsql.Tpo $(DEPDIR)/lighttpd-mod_vhostdb_pgsql.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb_pgsql.c' object='lighttpd-mod_vhostdb_pgsql.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_vhostdb_pgsql.o `test -f 'mod_vhostdb_pgsql.c' || echo '$(srcdir)/'`mod_vhostdb_pgsql.c + +lighttpd-mod_vhostdb_pgsql.obj: mod_vhostdb_pgsql.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_vhostdb_pgsql.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_vhostdb_pgsql.Tpo -c -o lighttpd-mod_vhostdb_pgsql.obj `if test -f 'mod_vhostdb_pgsql.c'; then $(CYGPATH_W) 'mod_vhostdb_pgsql.c'; else $(CYGPATH_W) '$(srcdir)/mod_vhostdb_pgsql.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_vhostdb_pgsql.Tpo $(DEPDIR)/lighttpd-mod_vhostdb_pgsql.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb_pgsql.c' object='lighttpd-mod_vhostdb_pgsql.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_vhostdb_pgsql.obj `if test -f 'mod_vhostdb_pgsql.c'; then $(CYGPATH_W) 'mod_vhostdb_pgsql.c'; else $(CYGPATH_W) '$(srcdir)/mod_vhostdb_pgsql.c'; fi` + +lighttpd-mod_vhostdb_dbi.o: mod_vhostdb_dbi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_vhostdb_dbi.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_vhostdb_dbi.Tpo -c -o lighttpd-mod_vhostdb_dbi.o `test -f 'mod_vhostdb_dbi.c' || echo '$(srcdir)/'`mod_vhostdb_dbi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_vhostdb_dbi.Tpo $(DEPDIR)/lighttpd-mod_vhostdb_dbi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb_dbi.c' object='lighttpd-mod_vhostdb_dbi.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_vhostdb_dbi.o `test -f 'mod_vhostdb_dbi.c' || echo '$(srcdir)/'`mod_vhostdb_dbi.c + +lighttpd-mod_vhostdb_dbi.obj: mod_vhostdb_dbi.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_vhostdb_dbi.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_vhostdb_dbi.Tpo -c -o lighttpd-mod_vhostdb_dbi.obj `if test -f 'mod_vhostdb_dbi.c'; then $(CYGPATH_W) 'mod_vhostdb_dbi.c'; else $(CYGPATH_W) '$(srcdir)/mod_vhostdb_dbi.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_vhostdb_dbi.Tpo $(DEPDIR)/lighttpd-mod_vhostdb_dbi.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_vhostdb_dbi.c' object='lighttpd-mod_vhostdb_dbi.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_vhostdb_dbi.obj `if test -f 'mod_vhostdb_dbi.c'; then $(CYGPATH_W) 'mod_vhostdb_dbi.c'; else $(CYGPATH_W) '$(srcdir)/mod_vhostdb_dbi.c'; fi` + +lighttpd-mod_openssl.o: mod_openssl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_openssl.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_openssl.Tpo -c -o lighttpd-mod_openssl.o `test -f 'mod_openssl.c' || echo '$(srcdir)/'`mod_openssl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_openssl.Tpo $(DEPDIR)/lighttpd-mod_openssl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_openssl.c' object='lighttpd-mod_openssl.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_openssl.o `test -f 'mod_openssl.c' || echo '$(srcdir)/'`mod_openssl.c + +lighttpd-mod_openssl.obj: mod_openssl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_openssl.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_openssl.Tpo -c -o lighttpd-mod_openssl.obj `if test -f 'mod_openssl.c'; then $(CYGPATH_W) 'mod_openssl.c'; else $(CYGPATH_W) '$(srcdir)/mod_openssl.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_openssl.Tpo $(DEPDIR)/lighttpd-mod_openssl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_openssl.c' object='lighttpd-mod_openssl.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_openssl.obj `if test -f 'mod_openssl.c'; then $(CYGPATH_W) 'mod_openssl.c'; else $(CYGPATH_W) '$(srcdir)/mod_openssl.c'; fi` + +lighttpd-mod_trigger_b4_dl.o: mod_trigger_b4_dl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_trigger_b4_dl.o -MD -MP -MF $(DEPDIR)/lighttpd-mod_trigger_b4_dl.Tpo -c -o lighttpd-mod_trigger_b4_dl.o `test -f 'mod_trigger_b4_dl.c' || echo '$(srcdir)/'`mod_trigger_b4_dl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_trigger_b4_dl.Tpo $(DEPDIR)/lighttpd-mod_trigger_b4_dl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_trigger_b4_dl.c' object='lighttpd-mod_trigger_b4_dl.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_trigger_b4_dl.o `test -f 'mod_trigger_b4_dl.c' || echo '$(srcdir)/'`mod_trigger_b4_dl.c + +lighttpd-mod_trigger_b4_dl.obj: mod_trigger_b4_dl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lighttpd-mod_trigger_b4_dl.obj -MD -MP -MF $(DEPDIR)/lighttpd-mod_trigger_b4_dl.Tpo -c -o lighttpd-mod_trigger_b4_dl.obj `if test -f 'mod_trigger_b4_dl.c'; then $(CYGPATH_W) 'mod_trigger_b4_dl.c'; else $(CYGPATH_W) '$(srcdir)/mod_trigger_b4_dl.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lighttpd-mod_trigger_b4_dl.Tpo $(DEPDIR)/lighttpd-mod_trigger_b4_dl.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_trigger_b4_dl.c' object='lighttpd-mod_trigger_b4_dl.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lighttpd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o lighttpd-mod_trigger_b4_dl.obj `if test -f 'mod_trigger_b4_dl.c'; then $(CYGPATH_W) 'mod_trigger_b4_dl.c'; else $(CYGPATH_W) '$(srcdir)/mod_trigger_b4_dl.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf t/.libs t/_libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f t/$(DEPDIR)/$(am__dirstamp) + -rm -f t/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstPROGRAMS clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/array.Po + -rm -f ./$(DEPDIR)/base64.Po + -rm -f ./$(DEPDIR)/buffer.Po + -rm -f ./$(DEPDIR)/burl.Po + -rm -f ./$(DEPDIR)/configfile-glue.Po + -rm -f ./$(DEPDIR)/data_array.Po + -rm -f ./$(DEPDIR)/data_config.Po + -rm -f ./$(DEPDIR)/data_integer.Po + -rm -f ./$(DEPDIR)/data_string.Po + -rm -f ./$(DEPDIR)/http_header.Po + -rm -f ./$(DEPDIR)/http_kv.Po + -rm -f ./$(DEPDIR)/liblightcomp_la-algo_sha1.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-array.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-base64.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-buffer.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-burl.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-chunk.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-configfile-glue.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-connections-glue.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-crc32.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-data_array.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-data_config.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-data_integer.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-data_string.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-etag.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_libev.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_poll.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_select.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_solaris_port.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-gw_backend.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-http-header-glue.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-http_auth.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-http_chunk.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-http_header.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-http_kv.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-http_vhostdb.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-joblist.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-keyvalue.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-log.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-md5.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-plugin.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-rand.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-request.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-safe_memclear.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-sock_addr.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-splaytree.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-stat_cache.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-stream.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-vector.Plo + -rm -f ./$(DEPDIR)/lighttpd-algo_sha1.Po + -rm -f ./$(DEPDIR)/lighttpd-angel.Po + -rm -f ./$(DEPDIR)/lighttpd-array.Po + -rm -f ./$(DEPDIR)/lighttpd-base64.Po + -rm -f ./$(DEPDIR)/lighttpd-buffer.Po + -rm -f ./$(DEPDIR)/lighttpd-burl.Po + -rm -f ./$(DEPDIR)/lighttpd-chunk.Po + -rm -f ./$(DEPDIR)/lighttpd-configfile-glue.Po + -rm -f ./$(DEPDIR)/lighttpd-configfile.Po + -rm -f ./$(DEPDIR)/lighttpd-configparser.Po + -rm -f ./$(DEPDIR)/lighttpd-connections-glue.Po + -rm -f ./$(DEPDIR)/lighttpd-connections.Po + -rm -f ./$(DEPDIR)/lighttpd-crc32.Po + -rm -f ./$(DEPDIR)/lighttpd-data_array.Po + -rm -f ./$(DEPDIR)/lighttpd-data_config.Po + -rm -f ./$(DEPDIR)/lighttpd-data_integer.Po + -rm -f ./$(DEPDIR)/lighttpd-data_string.Po + -rm -f ./$(DEPDIR)/lighttpd-etag.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_freebsd_kqueue.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_libev.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_linux_sysepoll.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_poll.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_select.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_solaris_devpoll.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_solaris_port.Po + -rm -f ./$(DEPDIR)/lighttpd-gw_backend.Po + -rm -f ./$(DEPDIR)/lighttpd-http-header-glue.Po + -rm -f ./$(DEPDIR)/lighttpd-http_auth.Po + -rm -f ./$(DEPDIR)/lighttpd-http_chunk.Po + -rm -f ./$(DEPDIR)/lighttpd-http_header.Po + -rm -f ./$(DEPDIR)/lighttpd-http_kv.Po + -rm -f ./$(DEPDIR)/lighttpd-http_vhostdb.Po + -rm -f ./$(DEPDIR)/lighttpd-inet_ntop_cache.Po + -rm -f ./$(DEPDIR)/lighttpd-joblist.Po + -rm -f ./$(DEPDIR)/lighttpd-keyvalue.Po + -rm -f ./$(DEPDIR)/lighttpd-log.Po + -rm -f ./$(DEPDIR)/lighttpd-md5.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_access.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_accesslog.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_alias.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_auth.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_authn_file.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_authn_gssapi.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_authn_ldap.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_authn_mysql.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_authn_pam.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_cgi.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_cml.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_cml_funcs.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_cml_lua.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_compress.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_deflate.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_dirlisting.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_evasive.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_expire.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_extforward.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_fastcgi.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_flv_streaming.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_geoip.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_indexfile.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_magnet.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_magnet_cache.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_mysql_vhost.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_openssl.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_proxy.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_redirect.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_rewrite.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_rrdtool.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_scgi.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_secdownload.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_setenv.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_simple_vhost.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_ssi.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_ssi_expr.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_ssi_exprparser.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_staticfile.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_status.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_trigger_b4_dl.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_uploadprogress.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_userdir.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_usertrack.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_vhostdb.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_vhostdb_dbi.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_vhostdb_ldap.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_vhostdb_mysql.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_vhostdb_pgsql.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_webdav.Po + -rm -f ./$(DEPDIR)/lighttpd-network.Po + -rm -f ./$(DEPDIR)/lighttpd-network_write.Po + -rm -f ./$(DEPDIR)/lighttpd-plugin.Po + -rm -f ./$(DEPDIR)/lighttpd-rand.Po + -rm -f ./$(DEPDIR)/lighttpd-request.Po + -rm -f ./$(DEPDIR)/lighttpd-response.Po + -rm -f ./$(DEPDIR)/lighttpd-safe_memclear.Po + -rm -f ./$(DEPDIR)/lighttpd-server.Po + -rm -f ./$(DEPDIR)/lighttpd-sock_addr.Po + -rm -f ./$(DEPDIR)/lighttpd-splaytree.Po + -rm -f ./$(DEPDIR)/lighttpd-stat_cache.Po + -rm -f ./$(DEPDIR)/lighttpd-stream.Po + -rm -f ./$(DEPDIR)/lighttpd-vector.Po + -rm -f ./$(DEPDIR)/log.Po + -rm -f ./$(DEPDIR)/mod_access.Plo + -rm -f ./$(DEPDIR)/mod_accesslog.Plo + -rm -f ./$(DEPDIR)/mod_alias.Plo + -rm -f ./$(DEPDIR)/mod_auth.Plo + -rm -f ./$(DEPDIR)/mod_authn_file.Plo + -rm -f ./$(DEPDIR)/mod_authn_gssapi.Plo + -rm -f ./$(DEPDIR)/mod_authn_ldap.Plo + -rm -f ./$(DEPDIR)/mod_authn_mysql_la-mod_authn_mysql.Plo + -rm -f ./$(DEPDIR)/mod_authn_pam.Plo + -rm -f ./$(DEPDIR)/mod_authn_sasl_la-mod_authn_sasl.Plo + -rm -f ./$(DEPDIR)/mod_cgi.Plo + -rm -f ./$(DEPDIR)/mod_cml_la-mod_cml.Plo + -rm -f ./$(DEPDIR)/mod_cml_la-mod_cml_funcs.Plo + -rm -f ./$(DEPDIR)/mod_cml_la-mod_cml_lua.Plo + -rm -f ./$(DEPDIR)/mod_compress.Plo + -rm -f ./$(DEPDIR)/mod_deflate.Plo + -rm -f ./$(DEPDIR)/mod_dirlisting.Plo + -rm -f ./$(DEPDIR)/mod_evasive.Plo + -rm -f ./$(DEPDIR)/mod_evhost.Plo + -rm -f ./$(DEPDIR)/mod_expire.Plo + -rm -f ./$(DEPDIR)/mod_extforward.Plo + -rm -f ./$(DEPDIR)/mod_fastcgi.Plo + -rm -f ./$(DEPDIR)/mod_flv_streaming.Plo + -rm -f ./$(DEPDIR)/mod_geoip.Plo + -rm -f ./$(DEPDIR)/mod_indexfile.Plo + -rm -f ./$(DEPDIR)/mod_magnet_la-mod_magnet.Plo + -rm -f ./$(DEPDIR)/mod_magnet_la-mod_magnet_cache.Plo + -rm -f ./$(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Plo + -rm -f ./$(DEPDIR)/mod_openssl.Plo + -rm -f ./$(DEPDIR)/mod_proxy.Plo + -rm -f ./$(DEPDIR)/mod_redirect.Plo + -rm -f ./$(DEPDIR)/mod_rewrite.Plo + -rm -f ./$(DEPDIR)/mod_rrdtool.Plo + -rm -f ./$(DEPDIR)/mod_scgi.Plo + -rm -f ./$(DEPDIR)/mod_secdownload.Plo + -rm -f ./$(DEPDIR)/mod_setenv.Plo + -rm -f ./$(DEPDIR)/mod_simple_vhost.Plo + -rm -f ./$(DEPDIR)/mod_sockproxy.Plo + -rm -f ./$(DEPDIR)/mod_ssi.Plo + -rm -f ./$(DEPDIR)/mod_ssi_expr.Plo + -rm -f ./$(DEPDIR)/mod_ssi_exprparser.Plo + -rm -f ./$(DEPDIR)/mod_staticfile.Plo + -rm -f ./$(DEPDIR)/mod_status.Plo + -rm -f ./$(DEPDIR)/mod_trigger_b4_dl.Plo + -rm -f ./$(DEPDIR)/mod_uploadprogress.Plo + -rm -f ./$(DEPDIR)/mod_userdir.Plo + -rm -f ./$(DEPDIR)/mod_usertrack.Plo + -rm -f ./$(DEPDIR)/mod_vhostdb.Plo + -rm -f ./$(DEPDIR)/mod_vhostdb_dbi_la-mod_vhostdb_dbi.Plo + -rm -f ./$(DEPDIR)/mod_vhostdb_ldap.Plo + -rm -f ./$(DEPDIR)/mod_vhostdb_mysql_la-mod_vhostdb_mysql.Plo + -rm -f ./$(DEPDIR)/mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.Plo + -rm -f ./$(DEPDIR)/mod_webdav_la-mod_webdav.Plo + -rm -f ./$(DEPDIR)/mod_wstunnel.Plo + -rm -f ./$(DEPDIR)/request.Po + -rm -f ./$(DEPDIR)/sock_addr.Po + -rm -f ./$(DEPDIR)/vector.Po + -rm -f t/$(DEPDIR)/test_array.Po + -rm -f t/$(DEPDIR)/test_base64.Po + -rm -f t/$(DEPDIR)/test_buffer.Po + -rm -f t/$(DEPDIR)/test_burl.Po + -rm -f t/$(DEPDIR)/test_configfile.Po + -rm -f t/$(DEPDIR)/test_keyvalue.Po + -rm -f t/$(DEPDIR)/test_mod_access.Po + -rm -f t/$(DEPDIR)/test_mod_evhost.Po + -rm -f t/$(DEPDIR)/test_mod_simple_vhost.Po + -rm -f t/$(DEPDIR)/test_request.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/array.Po + -rm -f ./$(DEPDIR)/base64.Po + -rm -f ./$(DEPDIR)/buffer.Po + -rm -f ./$(DEPDIR)/burl.Po + -rm -f ./$(DEPDIR)/configfile-glue.Po + -rm -f ./$(DEPDIR)/data_array.Po + -rm -f ./$(DEPDIR)/data_config.Po + -rm -f ./$(DEPDIR)/data_integer.Po + -rm -f ./$(DEPDIR)/data_string.Po + -rm -f ./$(DEPDIR)/http_header.Po + -rm -f ./$(DEPDIR)/http_kv.Po + -rm -f ./$(DEPDIR)/liblightcomp_la-algo_sha1.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-array.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-base64.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-buffer.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-burl.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-chunk.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-configfile-glue.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-connections-glue.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-crc32.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-data_array.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-data_config.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-data_integer.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-data_string.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-etag.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_freebsd_kqueue.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_libev.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_linux_sysepoll.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_poll.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_select.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_solaris_devpoll.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-fdevent_solaris_port.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-gw_backend.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-http-header-glue.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-http_auth.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-http_chunk.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-http_header.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-http_kv.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-http_vhostdb.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-joblist.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-keyvalue.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-log.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-md5.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-plugin.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-rand.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-request.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-safe_memclear.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-sock_addr.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-splaytree.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-stat_cache.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-stream.Plo + -rm -f ./$(DEPDIR)/liblightcomp_la-vector.Plo + -rm -f ./$(DEPDIR)/lighttpd-algo_sha1.Po + -rm -f ./$(DEPDIR)/lighttpd-angel.Po + -rm -f ./$(DEPDIR)/lighttpd-array.Po + -rm -f ./$(DEPDIR)/lighttpd-base64.Po + -rm -f ./$(DEPDIR)/lighttpd-buffer.Po + -rm -f ./$(DEPDIR)/lighttpd-burl.Po + -rm -f ./$(DEPDIR)/lighttpd-chunk.Po + -rm -f ./$(DEPDIR)/lighttpd-configfile-glue.Po + -rm -f ./$(DEPDIR)/lighttpd-configfile.Po + -rm -f ./$(DEPDIR)/lighttpd-configparser.Po + -rm -f ./$(DEPDIR)/lighttpd-connections-glue.Po + -rm -f ./$(DEPDIR)/lighttpd-connections.Po + -rm -f ./$(DEPDIR)/lighttpd-crc32.Po + -rm -f ./$(DEPDIR)/lighttpd-data_array.Po + -rm -f ./$(DEPDIR)/lighttpd-data_config.Po + -rm -f ./$(DEPDIR)/lighttpd-data_integer.Po + -rm -f ./$(DEPDIR)/lighttpd-data_string.Po + -rm -f ./$(DEPDIR)/lighttpd-etag.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_freebsd_kqueue.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_libev.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_linux_sysepoll.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_poll.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_select.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_solaris_devpoll.Po + -rm -f ./$(DEPDIR)/lighttpd-fdevent_solaris_port.Po + -rm -f ./$(DEPDIR)/lighttpd-gw_backend.Po + -rm -f ./$(DEPDIR)/lighttpd-http-header-glue.Po + -rm -f ./$(DEPDIR)/lighttpd-http_auth.Po + -rm -f ./$(DEPDIR)/lighttpd-http_chunk.Po + -rm -f ./$(DEPDIR)/lighttpd-http_header.Po + -rm -f ./$(DEPDIR)/lighttpd-http_kv.Po + -rm -f ./$(DEPDIR)/lighttpd-http_vhostdb.Po + -rm -f ./$(DEPDIR)/lighttpd-inet_ntop_cache.Po + -rm -f ./$(DEPDIR)/lighttpd-joblist.Po + -rm -f ./$(DEPDIR)/lighttpd-keyvalue.Po + -rm -f ./$(DEPDIR)/lighttpd-log.Po + -rm -f ./$(DEPDIR)/lighttpd-md5.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_access.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_accesslog.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_alias.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_auth.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_authn_file.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_authn_gssapi.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_authn_ldap.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_authn_mysql.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_authn_pam.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_cgi.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_cml.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_cml_funcs.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_cml_lua.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_compress.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_deflate.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_dirlisting.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_evasive.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_expire.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_extforward.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_fastcgi.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_flv_streaming.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_geoip.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_indexfile.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_magnet.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_magnet_cache.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_mysql_vhost.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_openssl.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_proxy.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_redirect.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_rewrite.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_rrdtool.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_scgi.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_secdownload.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_setenv.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_simple_vhost.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_ssi.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_ssi_expr.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_ssi_exprparser.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_staticfile.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_status.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_trigger_b4_dl.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_uploadprogress.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_userdir.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_usertrack.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_vhostdb.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_vhostdb_dbi.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_vhostdb_ldap.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_vhostdb_mysql.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_vhostdb_pgsql.Po + -rm -f ./$(DEPDIR)/lighttpd-mod_webdav.Po + -rm -f ./$(DEPDIR)/lighttpd-network.Po + -rm -f ./$(DEPDIR)/lighttpd-network_write.Po + -rm -f ./$(DEPDIR)/lighttpd-plugin.Po + -rm -f ./$(DEPDIR)/lighttpd-rand.Po + -rm -f ./$(DEPDIR)/lighttpd-request.Po + -rm -f ./$(DEPDIR)/lighttpd-response.Po + -rm -f ./$(DEPDIR)/lighttpd-safe_memclear.Po + -rm -f ./$(DEPDIR)/lighttpd-server.Po + -rm -f ./$(DEPDIR)/lighttpd-sock_addr.Po + -rm -f ./$(DEPDIR)/lighttpd-splaytree.Po + -rm -f ./$(DEPDIR)/lighttpd-stat_cache.Po + -rm -f ./$(DEPDIR)/lighttpd-stream.Po + -rm -f ./$(DEPDIR)/lighttpd-vector.Po + -rm -f ./$(DEPDIR)/log.Po + -rm -f ./$(DEPDIR)/mod_access.Plo + -rm -f ./$(DEPDIR)/mod_accesslog.Plo + -rm -f ./$(DEPDIR)/mod_alias.Plo + -rm -f ./$(DEPDIR)/mod_auth.Plo + -rm -f ./$(DEPDIR)/mod_authn_file.Plo + -rm -f ./$(DEPDIR)/mod_authn_gssapi.Plo + -rm -f ./$(DEPDIR)/mod_authn_ldap.Plo + -rm -f ./$(DEPDIR)/mod_authn_mysql_la-mod_authn_mysql.Plo + -rm -f ./$(DEPDIR)/mod_authn_pam.Plo + -rm -f ./$(DEPDIR)/mod_authn_sasl_la-mod_authn_sasl.Plo + -rm -f ./$(DEPDIR)/mod_cgi.Plo + -rm -f ./$(DEPDIR)/mod_cml_la-mod_cml.Plo + -rm -f ./$(DEPDIR)/mod_cml_la-mod_cml_funcs.Plo + -rm -f ./$(DEPDIR)/mod_cml_la-mod_cml_lua.Plo + -rm -f ./$(DEPDIR)/mod_compress.Plo + -rm -f ./$(DEPDIR)/mod_deflate.Plo + -rm -f ./$(DEPDIR)/mod_dirlisting.Plo + -rm -f ./$(DEPDIR)/mod_evasive.Plo + -rm -f ./$(DEPDIR)/mod_evhost.Plo + -rm -f ./$(DEPDIR)/mod_expire.Plo + -rm -f ./$(DEPDIR)/mod_extforward.Plo + -rm -f ./$(DEPDIR)/mod_fastcgi.Plo + -rm -f ./$(DEPDIR)/mod_flv_streaming.Plo + -rm -f ./$(DEPDIR)/mod_geoip.Plo + -rm -f ./$(DEPDIR)/mod_indexfile.Plo + -rm -f ./$(DEPDIR)/mod_magnet_la-mod_magnet.Plo + -rm -f ./$(DEPDIR)/mod_magnet_la-mod_magnet_cache.Plo + -rm -f ./$(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Plo + -rm -f ./$(DEPDIR)/mod_openssl.Plo + -rm -f ./$(DEPDIR)/mod_proxy.Plo + -rm -f ./$(DEPDIR)/mod_redirect.Plo + -rm -f ./$(DEPDIR)/mod_rewrite.Plo + -rm -f ./$(DEPDIR)/mod_rrdtool.Plo + -rm -f ./$(DEPDIR)/mod_scgi.Plo + -rm -f ./$(DEPDIR)/mod_secdownload.Plo + -rm -f ./$(DEPDIR)/mod_setenv.Plo + -rm -f ./$(DEPDIR)/mod_simple_vhost.Plo + -rm -f ./$(DEPDIR)/mod_sockproxy.Plo + -rm -f ./$(DEPDIR)/mod_ssi.Plo + -rm -f ./$(DEPDIR)/mod_ssi_expr.Plo + -rm -f ./$(DEPDIR)/mod_ssi_exprparser.Plo + -rm -f ./$(DEPDIR)/mod_staticfile.Plo + -rm -f ./$(DEPDIR)/mod_status.Plo + -rm -f ./$(DEPDIR)/mod_trigger_b4_dl.Plo + -rm -f ./$(DEPDIR)/mod_uploadprogress.Plo + -rm -f ./$(DEPDIR)/mod_userdir.Plo + -rm -f ./$(DEPDIR)/mod_usertrack.Plo + -rm -f ./$(DEPDIR)/mod_vhostdb.Plo + -rm -f ./$(DEPDIR)/mod_vhostdb_dbi_la-mod_vhostdb_dbi.Plo + -rm -f ./$(DEPDIR)/mod_vhostdb_ldap.Plo + -rm -f ./$(DEPDIR)/mod_vhostdb_mysql_la-mod_vhostdb_mysql.Plo + -rm -f ./$(DEPDIR)/mod_vhostdb_pgsql_la-mod_vhostdb_pgsql.Plo + -rm -f ./$(DEPDIR)/mod_webdav_la-mod_webdav.Plo + -rm -f ./$(DEPDIR)/mod_wstunnel.Plo + -rm -f ./$(DEPDIR)/request.Po + -rm -f ./$(DEPDIR)/sock_addr.Po + -rm -f ./$(DEPDIR)/vector.Po + -rm -f t/$(DEPDIR)/test_array.Po + -rm -f t/$(DEPDIR)/test_base64.Po + -rm -f t/$(DEPDIR)/test_buffer.Po + -rm -f t/$(DEPDIR)/test_burl.Po + -rm -f t/$(DEPDIR)/test_configfile.Po + -rm -f t/$(DEPDIR)/test_keyvalue.Po + -rm -f t/$(DEPDIR)/test_mod_access.Po + -rm -f t/$(DEPDIR)/test_mod_evhost.Po + -rm -f t/$(DEPDIR)/test_mod_simple_vhost.Po + -rm -f t/$(DEPDIR)/test_request.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES uninstall-sbinPROGRAMS + +.MAKE: all check check-am install install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \ + check-am clean clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-noinstPROGRAMS clean-sbinPROGRAMS \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-libLTLIBRARIES \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-sbinPROGRAMS install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \ + uninstall-sbinPROGRAMS + +.PRECIOUS: Makefile + + +lemon$(BUILD_EXEEXT): lemon.c + $(AM_V_CC)$(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) $(LDFLAGS_FOR_BUILD) -o $@ $(srcdir)/lemon.c + +.PHONY: versionstamp parsers + +versionstamp: + @test -f versionstamp.h || touch versionstamp.h; \ + REVISION=""; \ + if test -d "$(top_srcdir)/.svn" -a -x "`which svnversion`"; then \ + REVISION="$$(LANG= LC_ALL=C svnversion "$(top_srcdir)" 2>/dev/null || echo exported)"; \ + if test "$$REVISION" = "exported"; then \ + REVISION=""; \ + fi; \ + fi; \ + if test -z "$$REVISION" -a -d "$(top_srcdir)/.git" -a -x "`which git`"; then \ + REVISION="$$(cd "$(top_srcdir)"; LANG= LC_ALL=C git describe --always 2>/dev/null || echo)"; \ + fi; \ + if test -n "$$REVISION"; then \ + echo "#define REPO_VERSION \"-devel-$$REVISION\"" > versionstamp.h.tmp; \ + else \ + echo "#define REPO_VERSION \"\"" > versionstamp.h.tmp; \ + fi; \ + if ! diff versionstamp.h.tmp versionstamp.h >/dev/null 2>/dev/null; then \ + mv versionstamp.h.tmp versionstamp.h; \ + else \ + rm versionstamp.h.tmp; \ + fi + +configparser.h: configparser.c +configparser.c: $(srcdir)/configparser.y $(srcdir)/lempar.c lemon$(BUILD_EXEEXT) + rm -f configparser.h + $(LEMON) -q $(srcdir)/configparser.y $(srcdir)/lempar.c + +mod_ssi_exprparser.h: mod_ssi_exprparser.c +mod_ssi_exprparser.c: $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c lemon$(BUILD_EXEEXT) + rm -f mod_ssi_exprparser.h + $(LEMON) -q $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c + +parsers: configparser.c mod_ssi_exprparser.c + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/data/lighttpd/lighttpd-1.4.53/src/SConscript b/data/lighttpd/lighttpd-1.4.53/src/SConscript new file mode 100644 index 000000000..b785ca879 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/SConscript @@ -0,0 +1,337 @@ +import itertools +import os +import re +from collections import OrderedDict +from copy import copy + +try: + string_types = basestring +except NameError: + string_types = str + + +# search any file, not just executables +def WhereIsFile(file, paths): + for d in paths: + f = os.path.join(d, file) + if os.path.isfile(f): + try: + st = os.stat(f) + except OSError: + # os.stat() raises OSError, not IOError if the file + # doesn't exist, so in this case we let IOError get + # raised so as to not mask possibly serious disk or + # network issues. + continue + return os.path.normpath(f) + return None + +def FlattenLibs(libs): + if isinstance(libs, string_types): + return [libs] + else: + return [item for sublibs in libs for item in FlattenLibs(sublibs)] + +# removes all but the *LAST* occurance of a lib in the list +def RemoveDuplicateLibs(libs): + libs = FlattenLibs(libs) + # remove empty strings from list + libs = list(filter(lambda x: x != '', libs)) + return list(reversed(OrderedDict.fromkeys(reversed(libs)))) + +Import('env') + +def WorkaroundFreeBSDLibOrder(libs): + # lib(re)ssl includes (weak) arc4random functions + # which "on purpose" might conflict with those in libc + # => link libc first solves this + # (required for FreeBSD11 fullstatic build) + import platform + if ('c' in libs) and (platform.system() == 'FreeBSD'): + return ['c'] + libs + return libs + +def GatherLibs(env, *libs): + libs = RemoveDuplicateLibs(env['LIBS'] + list(libs) + [env['APPEND_LIBS']]) + return WorkaroundFreeBSDLibOrder(libs) + +common_src = Split("base64.c buffer.c burl.c log.c \ + http_header.c http_kv.c keyvalue.c chunk.c \ + http_chunk.c stream.c fdevent.c gw_backend.c \ + stat_cache.c plugin.c joblist.c etag.c array.c \ + data_string.c data_array.c \ + data_integer.c algo_sha1.c md5.c \ + vector.c \ + fdevent_select.c fdevent_libev.c \ + fdevent_poll.c fdevent_linux_sysepoll.c \ + fdevent_solaris_devpoll.c fdevent_solaris_port.c \ + fdevent_freebsd_kqueue.c \ + data_config.c \ + crc32.c \ + connections-glue.c \ + configfile-glue.c \ + http-header-glue.c \ + http_auth.c \ + http_vhostdb.c \ + request.c \ + sock_addr.c \ + splaytree.c \ + rand.c \ + safe_memclear.c \ +") + +src = Split("server.c response.c connections.c \ + inet_ntop_cache.c \ + network.c \ + network_write.c \ + configfile.c configparser.c") + +lemon = env.Program('lemon', 'lemon.c', LIBS = GatherLibs(env)) + +def Lemon(env, input): + parser = env.Command([input.replace('.y', '.c'),input.replace('.y', '.h')], input, '(cd sconsbuild/build; ../../' + lemon[0].path + ' -q ../../$SOURCE ../../src/lempar.c)') + env.Depends(parser, lemon) + +configparser = Lemon(env, 'configparser.y') +mod_ssi_exprparser = Lemon(env, 'mod_ssi_exprparser.y') + +## the modules and how they are built +modules = { + 'mod_access' : { 'src' : [ 'mod_access.c' ] }, + 'mod_accesslog' : { 'src' : [ 'mod_accesslog.c' ] }, + 'mod_alias' : { 'src' : [ 'mod_alias.c' ] }, + 'mod_auth' : { 'src' : [ 'mod_auth.c' ] }, + 'mod_authn_file' : { 'src' : [ 'mod_authn_file.c' ], 'lib' : [ env['LIBCRYPT'], env['LIBCRYPTO'] ] }, + 'mod_cgi' : { 'src' : [ 'mod_cgi.c' ] }, + 'mod_compress' : { 'src' : [ 'mod_compress.c' ], 'lib' : [ env['LIBZ'], env['LIBBZ2'] ] }, + 'mod_deflate' : { 'src' : [ 'mod_deflate.c' ], 'lib' : [ env['LIBZ'], env['LIBBZ2'] ] }, + 'mod_dirlisting' : { 'src' : [ 'mod_dirlisting.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_evasive' : { 'src' : [ 'mod_evasive.c' ] }, + 'mod_evhost' : { 'src' : [ 'mod_evhost.c' ] }, + 'mod_expire' : { 'src' : [ 'mod_expire.c' ] }, + 'mod_extforward' : { 'src' : [ 'mod_extforward.c' ] }, + 'mod_fastcgi' : { 'src' : [ 'mod_fastcgi.c' ] }, + 'mod_flv_streaming' : { 'src' : [ 'mod_flv_streaming.c' ] }, + 'mod_indexfile' : { 'src' : [ 'mod_indexfile.c' ] }, + 'mod_proxy' : { 'src' : [ 'mod_proxy.c' ] }, + 'mod_redirect' : { 'src' : [ 'mod_redirect.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_rewrite' : { 'src' : [ 'mod_rewrite.c' ], 'lib' : [ env['LIBPCRE'] ] }, + 'mod_rrdtool' : { 'src' : [ 'mod_rrdtool.c' ] }, + 'mod_scgi' : { 'src' : [ 'mod_scgi.c' ] }, + 'mod_secdownload' : { 'src' : [ 'mod_secdownload.c' ], 'lib' : [ env['LIBCRYPTO'] ] }, + 'mod_setenv' : { 'src' : [ 'mod_setenv.c' ] }, + 'mod_simple_vhost' : { 'src' : [ 'mod_simple_vhost.c' ] }, + 'mod_sockproxy' : { 'src' : [ 'mod_sockproxy.c' ] }, + 'mod_ssi' : { 'src' : [ 'mod_ssi_exprparser.c', 'mod_ssi_expr.c', 'mod_ssi.c' ] }, + 'mod_staticfile' : { 'src' : [ 'mod_staticfile.c' ] }, + 'mod_status' : { 'src' : [ 'mod_status.c' ] }, + 'mod_uploadprogress' : { 'src' : [ 'mod_uploadprogress.c' ] }, + 'mod_userdir' : { 'src' : [ 'mod_userdir.c' ] }, + 'mod_usertrack' : { 'src' : [ 'mod_usertrack.c' ] }, + 'mod_vhostdb' : { 'src' : [ 'mod_vhostdb.c' ] }, + 'mod_webdav' : { 'src' : [ 'mod_webdav.c' ], 'lib' : [ env['LIBXML2'], env['LIBSQLITE3'], env['LIBUUID'] ] }, + 'mod_wstunnel' : { 'src' : [ 'mod_wstunnel.c' ], 'lib' : [ env['LIBCRYPTO'] ] }, +} + +if env['with_geoip']: + modules['mod_geoip'] = { 'src' : [ 'mod_geoip.c' ], 'lib' : [ env['LIBGEOIP'] ] } + +if env['with_krb5']: + modules['mod_authn_gssapi'] = { 'src' : [ 'mod_authn_gssapi.c' ], 'lib' : [ env['LIBKRB5'], env['LIBGSSAPI_KRB5'] ] } + +if env['with_ldap']: + modules['mod_authn_ldap'] = { 'src' : [ 'mod_authn_ldap.c' ], 'lib' : [ env['LIBLDAP'], env['LIBLBER'] ] } + modules['mod_vhostdb_ldap'] = { 'src' : [ 'mod_vhostdb_ldap.c' ], 'lib' : [ env['LIBLDAP'], env['LIBLBER'] ] } + +if env['with_lua']: + modules['mod_magnet'] = { 'src' : [ 'mod_magnet.c', 'mod_magnet_cache.c' ], 'lib' : [ env['LIBLUA'] ] } + modules['mod_cml'] = { + 'src' : [ 'mod_cml_lua.c', 'mod_cml.c', 'mod_cml_funcs.c' ], + 'lib' : [ env['LIBMEMCACHED'], env['LIBLUA'] ] + } + +if env['with_pam']: + modules['mod_authn_pam'] = { 'src' : [ 'mod_authn_pam.c' ], 'lib' : [ env['LIBPAM'] ] } + +if env['with_pcre'] and (env['with_memcached'] or env['with_gdbm']): + modules['mod_trigger_b4_dl'] = { 'src' : [ 'mod_trigger_b4_dl.c' ], 'lib' : [ env['LIBPCRE'], env['LIBMEMCACHED'], env['LIBGDBM'] ] } + +if env['with_mysql']: + modules['mod_authn_mysql'] = { 'src' : [ 'mod_authn_mysql.c' ], 'lib' : [ env['LIBCRYPT'], env['LIBMYSQL'] ] } + modules['mod_mysql_vhost'] = { 'src' : [ 'mod_mysql_vhost.c' ], 'lib' : [ env['LIBMYSQL'] ] } + modules['mod_vhostdb_mysql'] = { 'src' : [ 'mod_vhostdb_mysql.c' ], 'lib' : [ env['LIBMYSQL'] ] } + +if env['with_pgsql']: + modules['mod_vhostdb_pgsql'] = { 'src' : [ 'mod_vhostdb_pgsql.c' ], 'lib' : [ env['LIBPGSQL'] ] } + +if env['with_dbi']: + modules['mod_vhostdb_dbi'] = { 'src' : [ 'mod_vhostdb_dbi.c' ], 'lib' : [ env['LIBDBI'] ] } + +if env['with_sasl']: + modules['mod_authn_sasl'] = { 'src' : [ 'mod_authn_sasl.c' ], 'lib' : [ env['LIBSASL'] ] } + +if env['with_openssl']: + modules['mod_openssl'] = { 'src' : [ 'mod_openssl.c' ], 'lib' : [ env['LIBSSL'], env['LIBCRYPTO'] ] } + +if env['with_wolfssl']: + modules['mod_openssl'] = { 'src' : [ 'mod_openssl.c' ], 'lib' : [ env['LIBCRYPTO'], 'm' ] } + +staticenv = env.Clone(CPPFLAGS=[ env['CPPFLAGS'], '-DLIGHTTPD_STATIC' ]) + +## all the core-sources + the modules +staticsrc = src + common_src + +staticlib = copy(env['LIBS']) +staticinit = '' +for module in modules.keys(): + staticsrc += modules[module]['src'] + staticinit += "PLUGIN_INIT(%s)\n"%module + if 'lib' in modules[module]: + staticlib += modules[module]['lib'] + +def WriteStaticPluginHeader(target, source, env): + do_write = True + data = env['STATICINIT'] + # only touch the file if content actually changes + try: + with open(target[0].abspath, 'r') as f: + do_write = (data != f.read()) + except IOError: + pass + if do_write: + with open(target[0].abspath, 'w+') as f: + f.write(env['STATICINIT']) + +env['STATICINIT'] = staticinit +staticheader = env.AlwaysBuild(env.Command('plugin-static.h', [], WriteStaticPluginHeader)) + +## turn all src-files into objects +staticobj = [] +static_plugin_obj = None +for cfile in staticsrc: + if cfile == 'plugin.c': + static_plugin_obj = [ staticenv.Object('static-' + cfile.replace('.c', ''), cfile) ] + staticobj += static_plugin_obj + else: + staticobj += [ staticenv.Object('static-' + cfile.replace('.c', ''), cfile) ] +env.Depends(static_plugin_obj, 'plugin-static.h') + +## includes all modules, but links dynamically against other libs +staticbin = staticenv.Program('../static/build/lighttpd', + staticobj, + LIBS = GatherLibs(env, staticlib) + ) + +## you might have to adjust the list of libs and the order for your setup +## this is tricky, be warned +fullstaticlib = [] + +## try to calculate the libs for fullstatic with ldd +## 1. find the lib +## 2. check the deps +## 3. add them to the libs +#searchlibs = os.pathsep.join([ '/lib/', '/usr/lib/', '/usr/local/lib/' ]) +searchlibs = [] +searchpathre = re.compile(r'\bSEARCH_DIR\("=([^"]+)"\)') +f = os.popen('ld --verbose | grep SEARCH_DIR', 'r') +for aword in searchpathre.findall(f.read()): + searchlibs += [ aword] +f.close + +lddre = re.compile(r'^\s+lib([^=-]+)(?:-[\.0-9]+)?\.so\.[0-9]+ =>', re.MULTILINE) +for libs in staticlib: + if isinstance(libs, string_types): libs = [ libs ] + for lib in libs: + fullstaticlib += [ lib ] + solibpath = WhereIsFile('lib' + lib + '.so', paths = searchlibs) + if solibpath is None: + continue + + f = os.popen('ldd ' + solibpath, 'r') + for aword in lddre.findall(f.read()): + fullstaticlib += [ aword ] + f.close + +## glibc pthread needs to be linked completely (especially nptl-init.o) +## or not at all, or else pthread structures may not be initialized correctly +import platform +fullstatic_libs = GatherLibs(env, fullstaticlib) +fullstatic_linkflags = [staticenv['LINKFLAGS'], '-static'] +if (('pthread' in fullstatic_libs) or ('pcre' in fullstatic_libs)) and (platform.system() == 'Linux'): + fullstatic_linkflags += ['-Wl,--whole-archive','-lpthread','-Wl,--no-whole-archive'] + fullstatic_libs.remove('pthread') +if 'gcc_s' in fullstatic_libs: + fullstatic_linkflags += ['-static-libgcc'] + fullstatic_libs.remove('gcc_s') + +## includes all modules, linked statically +fullstaticbin = staticenv.Program('../fullstatic/build/lighttpd', + staticobj, + LIBS = fullstatic_libs, + LINKFLAGS= fullstatic_linkflags + ) + +Alias('static', staticbin) +Alias('fullstatic', fullstaticbin) + +implib = 'lighttpd.exe.a' +bin_targets = ['lighttpd'] +bin_linkflags = [ env['LINKFLAGS'] ] +if env['COMMON_LIB'] == 'lib': + common_lib = env.SharedLibrary('liblighttpd', common_src, LINKFLAGS = [ env['LINKFLAGS'], '-Wl,--export-dynamic' ]) +else: + src += common_src + common_lib = [] + if env['COMMON_LIB'] == 'bin': + bin_linkflags += [ '-Wl,--export-all-symbols', '-Wl,--out-implib=build/' + implib ] + bin_targets += [ implib ] + else: + bin_linkflags += [ '-Wl,--export-dynamic' ] + +instbin = env.Program(bin_targets, src, LINKFLAGS = bin_linkflags, + LIBS = GatherLibs( + env, + common_lib, + env['LIBCRYPTO'], + env['LIBDL'], + env['LIBPCRE'], + ) +) +env.Depends(instbin, configparser) + +if env['COMMON_LIB'] == 'bin': + common_lib = instbin[1] + +env['SHLIBPREFIX'] = '' +instlib = [] +for module in modules.keys(): + libs = [ common_lib ] + if 'lib' in modules[module]: + libs += modules[module]['lib'] + instlib += env.SharedLibrary(module, modules[module]['src'], LIBS = GatherLibs(env, libs)) +env.Alias('modules', instlib) + +inst = [] + +if env['build_dynamic']: + Default(instbin[0], instlib) + inst += env.Install('${sbindir}', instbin[0]) + inst += env.Install('${libdir}', instlib) + if env['COMMON_LIB'] == 'lib': + Default(common_lib) + inst += env.Install('${bindir}', common_lib) + +if env['build_static']: + Default(staticbin) + inst += env.Install('${sbindir}', staticbin) + +if env['build_fullstatic']: + Default(fullstaticbin) + inst += env.Install('${sbindir}', fullstaticbin) + +env.Alias('dynamic', instbin) +# default all to be installed +env.Alias('install', inst) + +pkgdir = '.' +tarname = env['package'] + '-' + env['version'] diff --git a/data/lighttpd/lighttpd-1.4.53/src/algo_sha1.c b/data/lighttpd/lighttpd-1.4.53/src/algo_sha1.c new file mode 100644 index 000000000..fd251cd8f --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/algo_sha1.c @@ -0,0 +1,173 @@ +#include "first.h" +typedef int innocuous_typedef_to_quiet_empty_translation_unit_compiler_warning; + +#include "sys-crypto.h" +#ifndef USE_OPENSSL_CRYPTO + +#include "sys-endian.h" +#include "algo_sha1.h" + +/* + * obtained from https://github.com/nori0428/mod_websocket + */ + +/* + * sha1.c + * + * Originally written by Steve Reid <steve@edmweb.com> + * + * Modified by Aaron D. Gifford <agifford@infowest.com> + * + * NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN + * + * The original unmodified version is available at: + * ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) 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 AUTHOR(S) 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. + */ + +#include <string.h> + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ + +#ifdef __LITTLE_ENDIAN__ +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&(sha1_quadbyte)0xFF00FF00) \ + |(rol(block->l[i],8)&(sha1_quadbyte)0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif + +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + +typedef union _BYTE64QUAD16 { + sha1_byte c[64]; + sha1_quadbyte l[16]; +} BYTE64QUAD16; + +/* Hash a single 512-bit block. This is the core of the algorithm. */ +void SHA1_Transform(sha1_quadbyte state[5], const sha1_byte buffer[64]) { + sha1_quadbyte a, b, c, d, e; + BYTE64QUAD16 src; + BYTE64QUAD16 *block; + + /* slow but cast-align */ + memcpy(src.c, buffer, sizeof(sha1_byte) * 64); + block = &src; + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1_Init - Initialize new context */ +void SHA1_Init(SHA_CTX* context) { + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + +/* Run your data through this. */ +void SHA1_Update(SHA_CTX *context, const sha1_byte *data, unsigned int len) { + unsigned int i, j; + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1_Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1_Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ +void SHA1_Final(sha1_byte digest[SHA1_DIGEST_LENGTH], SHA_CTX *context) { + sha1_quadbyte i, j; + sha1_byte finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (sha1_byte)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1_Update(context, (sha1_byte *)"\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1_Update(context, (sha1_byte *)"\0", 1); + } + /* Should cause a SHA1_Transform() */ + SHA1_Update(context, finalcount, 8); + for (i = 0; i < SHA1_DIGEST_LENGTH; i++) { + digest[i] = (sha1_byte) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + i = j = 0; + memset(context->buffer, 0, SHA1_BLOCK_LENGTH); + memset(context->state, 0, SHA1_DIGEST_LENGTH); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); +} + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/algo_sha1.h b/data/lighttpd/lighttpd-1.4.53/src/algo_sha1.h new file mode 100644 index 000000000..346d1c989 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/algo_sha1.h @@ -0,0 +1,82 @@ +#ifndef INCLUDED_ALGO_SHA1_H +#define INCLUDED_ALGO_SHA1_H +#include "first.h" + +#include "sys-crypto.h" +#ifdef USE_OPENSSL_CRYPTO + +#include <openssl/sha.h> + +#else + +/* + * sha.h + * + * Originally taken from the public domain SHA1 implementation + * written by by Steve Reid <steve@edmweb.com> + * + * Modified by Aaron D. Gifford <agifford@infowest.com> + * + * NO COPYRIGHT - THIS IS 100% IN THE PUBLIC DOMAIN + * + * The original unmodified version is available at: + * ftp://ftp.funet.fi/pub/crypt/hash/sha/sha1.c + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) 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 AUTHOR(S) 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. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#include <sys/types.h> + +/* Make sure you define these types for your architecture: */ +typedef uint32_t sha1_quadbyte; /* 4 byte type */ +typedef unsigned char sha1_byte; /* single byte type */ + +#define SHA1_BLOCK_LENGTH 64 +#define SHA1_DIGEST_LENGTH 20 +/*(added for lighttpd)*/ +#define SHA_DIGEST_LENGTH SHA1_DIGEST_LENGTH + +/* The SHA1 structure: */ +typedef struct _SHA_CTX { + sha1_quadbyte state[5]; + sha1_quadbyte count[2]; + sha1_byte buffer[SHA1_BLOCK_LENGTH]; +} SHA_CTX; + +#ifndef NOPROTO +void SHA1_Init(SHA_CTX *context); +void SHA1_Update(SHA_CTX *context, const sha1_byte *data, unsigned int len); +void SHA1_Final(sha1_byte digest[SHA1_DIGEST_LENGTH], SHA_CTX *context); +#else +void SHA1_Init(); +void SHA1_Update(); +void SHA1_Final(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/array.c b/data/lighttpd/lighttpd-1.4.53/src/array.c new file mode 100644 index 000000000..9abaeada6 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/array.c @@ -0,0 +1,606 @@ +#include "first.h" + +#include "array.h" +#include "buffer.h" + +#include <string.h> +#include <stdlib.h> +#include <limits.h> + +#include <errno.h> +#include <assert.h> + +#define ARRAY_NOT_FOUND ((size_t)(-1)) + +array *array_init(void) { + array *a; + + a = calloc(1, sizeof(*a)); + force_assert(a); + + return a; +} + +array *array_init_array(array *src) { + size_t i; + array *a = array_init(); + + if (0 == src->size) return a; + + a->used = src->used; + a->size = src->size; + a->unique_ndx = src->unique_ndx; + + a->data = malloc(sizeof(*src->data) * src->size); + force_assert(NULL != a->data); + for (i = 0; i < src->size; i++) { + if (src->data[i]) a->data[i] = src->data[i]->fn->copy(src->data[i]); + else a->data[i] = NULL; + } + + a->sorted = malloc(sizeof(*src->sorted) * src->size); + force_assert(NULL != a->sorted); + memcpy(a->sorted, src->sorted, sizeof(*src->sorted) * src->size); + return a; +} + +void array_free(array *a) { + size_t i; + if (!a) return; + + for (i = 0; i < a->size; i++) { + if (a->data[i]) a->data[i]->fn->free(a->data[i]); + } + + if (a->data) free(a->data); + if (a->sorted) free(a->sorted); + + free(a); +} + +void array_reset(array *a) { + size_t i; + if (!a) return; + + for (i = 0; i < a->used; i++) { + a->data[i]->fn->reset(a->data[i]); + } + + a->used = 0; + a->unique_ndx = 0; +} + +void array_reset_data_strings(array *a) { + if (!a) return; + + for (size_t i = 0; i < a->used; ++i) { + data_string * const ds = (data_string *)a->data[i]; + /*force_assert(ds->type == TYPE_STRING);*/ + buffer_reset(ds->key); + buffer_reset(ds->value); + } + + a->used = 0; + a->unique_ndx = 0; +} + +data_unset *array_pop(array *a) { + data_unset *du; + + force_assert(a->used != 0); + + a->used --; + du = a->data[a->used]; + force_assert(a->sorted[a->used] == a->used); /* only works on "simple" lists */ + a->data[a->used] = NULL; + + return du; +} + +static int array_keycmp(const char *a, size_t alen, const char *b, size_t blen) { + return alen < blen ? -1 : alen > blen ? 1 : buffer_caseless_compare(a, alen, b, blen); +} + +/* returns index of element or ARRAY_NOT_FOUND + * if rndx != NULL it stores the position in a->sorted[] where the key needs + * to be inserted + */ +static size_t array_get_index(const array *a, const char *key, size_t keylen, size_t *rndx) { + /* invariant: [lower-1] < key < [upper] + * "virtual elements": [-1] = -INFTY, [a->used] = +INFTY + * also an invariant: 0 <= lower <= upper <= a->used + */ + size_t lower = 0, upper = a->used; + force_assert(upper <= SSIZE_MAX); /* (lower + upper) can't overflow */ + + while (lower != upper) { + size_t probe = (lower + upper) / 2; + const buffer *b = a->data[a->sorted[probe]]->key; + int cmp = array_keycmp(key, keylen, CONST_BUF_LEN(b)); + + if (cmp == 0) { + /* found */ + if (rndx) *rndx = probe; + return a->sorted[probe]; + } else if (cmp < 0) { + /* key < [probe] */ + upper = probe; /* still: lower <= upper */ + } else { + /* key > [probe] */ + lower = probe + 1; /* still: lower <= upper */ + } + } + + /* not found: [lower-1] < key < [upper] = [lower] ==> insert at [lower] */ + if (rndx) *rndx = lower; + return ARRAY_NOT_FOUND; +} + +data_unset *array_get_element_klen(const array *a, const char *key, size_t klen) { + size_t ndx; + force_assert(NULL != key); + + if (ARRAY_NOT_FOUND != (ndx = array_get_index(a, key, klen, NULL))) { + /* found, return it */ + return a->data[ndx]; + } + + return NULL; +} + +data_unset *array_extract_element_klen(array *a, const char *key, size_t klen) { + size_t ndx, pos; + force_assert(NULL != key); + + if (ARRAY_NOT_FOUND != (ndx = array_get_index(a, key, klen, &pos))) { + /* found */ + const size_t last_ndx = a->used - 1; + data_unset *entry = a->data[ndx]; + + /* now we need to swap it with the last element (if it isn't already the last element) */ + if (ndx != last_ndx) { + /* to swap we also need to modify the index in a->sorted - find pos of last_elem there */ + size_t last_elem_pos; + /* last element must be present at the expected position */ + force_assert(last_ndx == array_get_index(a, CONST_BUF_LEN(a->data[last_ndx]->key), &last_elem_pos)); + + /* move entry from last_ndx to ndx */ + a->data[ndx] = a->data[last_ndx]; + a->data[last_ndx] = NULL; + + /* fix index entry for moved entry */ + a->sorted[last_elem_pos] = ndx; + } else { + a->data[ndx] = NULL; + } + + /* remove entry in a->sorted: move everything after pos one step to the left */ + if (pos != last_ndx) { + memmove(a->sorted + pos, a->sorted + pos + 1, (last_ndx - pos) * sizeof(*a->sorted)); + } + a->sorted[last_ndx] = ARRAY_NOT_FOUND; + --a->used; + + return entry; + } + + return NULL; +} + +static data_unset *array_get_unused_element(array *a, data_type_t t) { + data_unset *ds = NULL; + unsigned int i; + + for (i = a->used; i < a->size; i++) { + if (a->data[i] && a->data[i]->type == t) { + ds = a->data[i]; + + /* make empty slot at a->used for next insert */ + a->data[i] = a->data[a->used]; + a->data[a->used] = NULL; + + return ds; + } + } + + return NULL; +} + +void array_set_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len) { + data_string *ds; + + if (NULL != (ds = (data_string *)array_get_element_klen(hdrs, key, key_len))) { + buffer_copy_string_len(ds->value, value, val_len); + return; + } + + array_insert_key_value(hdrs, key, key_len, value, val_len); +} + +void array_insert_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len) { + data_string *ds; + + if (NULL == (ds = (data_string *)array_get_unused_element(hdrs, TYPE_STRING))) { + ds = data_string_init(); + } + + buffer_copy_string_len(ds->key, key, key_len); + buffer_copy_string_len(ds->value, value, val_len); + array_insert_unique(hdrs, (data_unset *)ds); +} + +void array_insert_value(array *hdrs, const char *value, size_t val_len) { + data_string *ds; + + if (NULL == (ds = (data_string *)array_get_unused_element(hdrs, TYPE_STRING))) { + ds = data_string_init(); + } + + buffer_copy_string_len(ds->value, value, val_len); + array_insert_unique(hdrs, (data_unset *)ds); +} + +int * array_get_int_ptr(array *a, const char *k, size_t klen) { + data_integer *di = (data_integer *)array_get_element_klen(a, k, klen); + + if (NULL == di) { + di = (data_integer *)array_get_unused_element(a, TYPE_INTEGER); + if (NULL == di) di = data_integer_init(); + buffer_copy_string_len(di->key, k, klen); + array_insert_unique(a, (data_unset *)di); + } + + return &di->value; +} + +/* if entry already exists return pointer to existing entry, otherwise insert entry and return NULL */ +static data_unset **array_find_or_insert(array *a, data_unset *entry) { + size_t ndx, pos, j; + + /* generate unique index if neccesary */ + if (buffer_is_empty(entry->key) || entry->is_index_key) { + buffer_copy_int(entry->key, a->unique_ndx++); + entry->is_index_key = 1; + force_assert(0 != a->unique_ndx); /* must not wrap or we'll get problems */ + } + + /* try to find the entry */ + if (ARRAY_NOT_FOUND != (ndx = array_get_index(a, CONST_BUF_LEN(entry->key), &pos))) { + /* found collision, return it */ + return &a->data[ndx]; + } + + /* insert */ + + /* there couldn't possibly be enough memory to store so many entries */ + force_assert(a->used + 1 <= SSIZE_MAX); + + if (a->size == 0) { + a->size = 16; + a->data = malloc(sizeof(*a->data) * a->size); + a->sorted = malloc(sizeof(*a->sorted) * a->size); + force_assert(a->data); + force_assert(a->sorted); + for (j = a->used; j < a->size; j++) a->data[j] = NULL; + } else if (a->size == a->used) { + a->size += 16; + a->data = realloc(a->data, sizeof(*a->data) * a->size); + a->sorted = realloc(a->sorted, sizeof(*a->sorted) * a->size); + force_assert(a->data); + force_assert(a->sorted); + for (j = a->used; j < a->size; j++) a->data[j] = NULL; + } + + ndx = a->used; + + /* make sure there is nothing here */ + if (a->data[ndx]) a->data[ndx]->fn->free(a->data[ndx]); + + a->data[a->used++] = entry; + + /* move everything one step to the right */ + if (pos != ndx) { + memmove(a->sorted + (pos + 1), a->sorted + (pos), (ndx - pos) * sizeof(*a->sorted)); + } + + /* insert */ + a->sorted[pos] = ndx; + + return NULL; +} + +/* replace or insert data (free existing entry) */ +void array_replace(array *a, data_unset *entry) { + data_unset **old; + + force_assert(NULL != entry); + if (NULL != (old = array_find_or_insert(a, entry))) { + force_assert(*old != entry); + (*old)->fn->free(*old); + *old = entry; + } +} + +void array_insert_unique(array *a, data_unset *entry) { + data_unset **old; + + force_assert(NULL != entry); + if (NULL != (old = array_find_or_insert(a, entry))) { + force_assert((*old)->type == entry->type); + entry->fn->insert_dup(*old, entry); + } +} + +int array_is_vlist(array *a) { + for (size_t i = 0; i < a->used; ++i) { + data_unset *du = a->data[i]; + if (!du->is_index_key || du->type != TYPE_STRING) return 0; + } + return 1; +} + +int array_is_kvany(array *a) { + for (size_t i = 0; i < a->used; ++i) { + data_unset *du = a->data[i]; + if (du->is_index_key) return 0; + } + return 1; +} + +int array_is_kvarray(array *a) { + for (size_t i = 0; i < a->used; ++i) { + data_unset *du = a->data[i]; + if (du->is_index_key || du->type != TYPE_ARRAY) return 0; + } + return 1; +} + +int array_is_kvstring(array *a) { + for (size_t i = 0; i < a->used; ++i) { + data_unset *du = a->data[i]; + if (du->is_index_key || du->type != TYPE_STRING) return 0; + } + return 1; +} + +/* array_match_*() routines follow very similar pattern, but operate on slightly + * different data: array key/value, prefix/suffix match, case-insensitive or not + * While these could be combined into fewer routines with flags to modify the + * behavior, the interface distinctions are useful to add clarity to the code, + * and the specialized routines run slightly faster */ + +data_unset * +array_match_key_prefix_klen (const array * const a, const char * const s, const size_t slen) +{ + for (size_t i = 0; i < a->used; ++i) { + const buffer * const key = a->data[i]->key; + const size_t klen = buffer_string_length(key); + if (klen <= slen && 0 == memcmp(s, key->ptr, klen)) + return a->data[i]; + } + return NULL; +} + +data_unset * +array_match_key_prefix_nc_klen (const array * const a, const char * const s, const size_t slen) +{ + for (size_t i = 0; i < a->used; ++i) { + const buffer * const key = a->data[i]->key; + const size_t klen = buffer_string_length(key); + if (klen <= slen && 0 == strncasecmp(s, key->ptr, klen)) + return a->data[i]; + } + return NULL; +} + +data_unset * +array_match_key_prefix (const array * const a, const buffer * const b) +{ + return array_match_key_prefix_klen(a, CONST_BUF_LEN(b)); +} + +data_unset * +array_match_key_prefix_nc (const array * const a, const buffer * const b) +{ + return array_match_key_prefix_nc_klen(a, CONST_BUF_LEN(b)); +} + +const buffer * +array_match_value_prefix (const array * const a, const buffer * const b) +{ + const size_t blen = buffer_string_length(b); + + for (size_t i = 0; i < a->used; ++i) { + const buffer * const value = ((data_string *)a->data[i])->value; + const size_t vlen = buffer_string_length(value); + if (vlen <= blen && 0 == memcmp(b->ptr, value->ptr, vlen)) + return value; + } + return NULL; +} + +const buffer * +array_match_value_prefix_nc (const array * const a, const buffer * const b) +{ + const size_t blen = buffer_string_length(b); + + for (size_t i = 0; i < a->used; ++i) { + const buffer * const value = ((data_string *)a->data[i])->value; + const size_t vlen = buffer_string_length(value); + if (vlen <= blen && 0 == strncasecmp(b->ptr, value->ptr, vlen)) + return value; + } + return NULL; +} + +data_unset * +array_match_key_suffix (const array * const a, const buffer * const b) +{ + const size_t blen = buffer_string_length(b); + const char * const end = b->ptr + blen; + + for (size_t i = 0; i < a->used; ++i) { + const buffer * const key = a->data[i]->key; + const size_t klen = buffer_string_length(key); + if (klen <= blen && 0 == memcmp(end - klen, key->ptr, klen)) + return a->data[i]; + } + return NULL; +} + +data_unset * +array_match_key_suffix_nc (const array * const a, const buffer * const b) +{ + const size_t blen = buffer_string_length(b); + const char * const end = b->ptr + blen; + + for (size_t i = 0; i < a->used; ++i) { + const buffer * const key = a->data[i]->key; + const size_t klen = buffer_string_length(key); + if (klen <= blen && 0 == strncasecmp(end - klen, key->ptr, klen)) + return a->data[i]; + } + return NULL; +} + +const buffer * +array_match_value_suffix (const array * const a, const buffer * const b) +{ + const size_t blen = buffer_string_length(b); + const char * const end = b->ptr + blen; + + for (size_t i = 0; i < a->used; ++i) { + const buffer * const value = ((data_string *)a->data[i])->value; + const size_t vlen = buffer_string_length(value); + if (vlen <= blen && 0 == memcmp(end - vlen, value->ptr, vlen)) + return value; + } + return NULL; +} + +const buffer * +array_match_value_suffix_nc (const array * const a, const buffer * const b) +{ + const size_t blen = buffer_string_length(b); + const char * const end = b->ptr + blen; + + for (size_t i = 0; i < a->used; ++i) { + const buffer * const value = ((data_string *)a->data[i])->value; + const size_t vlen = buffer_string_length(value); + if (vlen <= blen && 0 == strncasecmp(end - vlen, value->ptr, vlen)) + return value; + } + return NULL; +} + +data_unset * +array_match_path_or_ext (const array * const a, const buffer * const b) +{ + const size_t blen = buffer_string_length(b); + + for (size_t i = 0; i < a->used; ++i) { + /* check extension in the form "^/path" or ".ext$" */ + const buffer * const key = a->data[i]->key; + const size_t klen = buffer_string_length(key); + if (klen <= blen + && 0 == memcmp((*(key->ptr) == '/' ? b->ptr : b->ptr + blen - klen), + key->ptr, klen)) + return a->data[i]; + } + return NULL; +} + + + + + +#include <stdio.h> + +void array_print_indent(int depth) { + int i; + for (i = 0; i < depth; i ++) { + fprintf(stdout, " "); + } +} + +size_t array_get_max_key_length(array *a) { + size_t maxlen, i; + + maxlen = 0; + for (i = 0; i < a->used; i ++) { + data_unset *du = a->data[i]; + size_t len = buffer_string_length(du->key); + + if (len > maxlen) { + maxlen = len; + } + } + return maxlen; +} + +int array_print(array *a, int depth) { + size_t i; + size_t maxlen; + int oneline = 1; + + if (a->used > 5) { + oneline = 0; + } + for (i = 0; i < a->used && oneline; i++) { + data_unset *du = a->data[i]; + if (!du->is_index_key) { + oneline = 0; + break; + } + switch (du->type) { + case TYPE_INTEGER: + case TYPE_STRING: + break; + default: + oneline = 0; + break; + } + } + if (oneline) { + fprintf(stdout, "("); + for (i = 0; i < a->used; i++) { + data_unset *du = a->data[i]; + if (i != 0) { + fprintf(stdout, ", "); + } + du->fn->print(du, depth + 1); + } + fprintf(stdout, ")"); + return 0; + } + + maxlen = array_get_max_key_length(a); + fprintf(stdout, "(\n"); + for (i = 0; i < a->used; i++) { + data_unset *du = a->data[i]; + array_print_indent(depth + 1); + if (!du->is_index_key) { + int j; + + if (i && (i % 5) == 0) { + fprintf(stdout, "# %zu\n", i); + array_print_indent(depth + 1); + } + fprintf(stdout, "\"%s\"", du->key->ptr); + for (j = maxlen - buffer_string_length(du->key); j > 0; j--) { + fprintf(stdout, " "); + } + fprintf(stdout, " => "); + } + du->fn->print(du, depth + 1); + fprintf(stdout, ",\n"); + } + if (!(i && (i - 1 % 5) == 0)) { + array_print_indent(depth + 1); + fprintf(stdout, "# %zu\n", i); + } + array_print_indent(depth); + fprintf(stdout, ")"); + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/array.h b/data/lighttpd/lighttpd-1.4.53/src/array.h new file mode 100644 index 000000000..8dbcbf688 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/array.h @@ -0,0 +1,98 @@ +#ifndef ARRAY_H +#define ARRAY_H +#include "first.h" + +#include "buffer.h" + +struct data_unset; /* declaration */ + +struct data_methods { + void (*reset)(struct data_unset *p); \ + struct data_unset *(*copy)(const struct data_unset *src); \ + void (*free)(struct data_unset *p); \ + int (*insert_dup)(struct data_unset *dst, struct data_unset *src); \ + void (*print)(const struct data_unset *p, int depth); +}; + +typedef enum { TYPE_UNSET, TYPE_STRING, TYPE_OTHER, TYPE_ARRAY, TYPE_INTEGER, TYPE_DONOTUSE, TYPE_CONFIG } data_type_t; +#define DATA_UNSET \ + buffer *key; \ + data_type_t type; \ + int is_index_key; /* 1 if key is a array index (autogenerated keys) */ \ + const struct data_methods *fn /* function table */ + +typedef struct data_unset { + DATA_UNSET; +} data_unset; + +typedef struct { + data_unset **data; + + size_t *sorted; + + size_t used; /* <= SSIZE_MAX */ + size_t size; + + size_t unique_ndx; +} array; + +typedef struct { + DATA_UNSET; + + buffer *value; +} data_string; + +data_string *data_string_init(void); + +typedef struct { + DATA_UNSET; + + array *value; +} data_array; + +data_array *data_array_init(void); + +typedef struct { + DATA_UNSET; + + int value; +} data_integer; + +data_integer *data_integer_init(void); + +array *array_init(void); +array *array_init_array(array *a); +void array_free(array *a); +void array_reset(array *a); +void array_reset_data_strings(array *a); +void array_insert_unique(array *a, data_unset *entry); +data_unset *array_pop(array *a); /* only works on "simple" lists with autogenerated keys */ +int array_is_vlist(array *a); +int array_is_kvany(array *a); +int array_is_kvarray(array *a); +int array_is_kvstring(array *a); +int array_print(array *a, int depth); +#define array_get_element(a, key) array_get_element_klen((a), (key), sizeof(key)-1) +data_unset *array_get_element_klen(const array *a, const char *key, size_t klen); +data_unset *array_extract_element_klen(array *a, const char *key, size_t klen); /* removes found entry from array */ +void array_set_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len); +void array_insert_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len); +void array_insert_value(array *hdrs, const char *value, size_t val_len); +int * array_get_int_ptr(array *a, const char *k, size_t klen); +void array_replace(array *a, data_unset *entry); +void array_print_indent(int depth); +size_t array_get_max_key_length(array *a); + +data_unset * array_match_key_prefix_klen (const array * const a, const char * const s, const size_t slen); +data_unset * array_match_key_prefix_nc_klen (const array * const a, const char * const s, const size_t slen); +data_unset * array_match_key_prefix (const array * const a, const buffer * const b); +data_unset * array_match_key_prefix_nc (const array * const a, const buffer * const b); +const buffer * array_match_value_prefix (const array * const a, const buffer * const b); +const buffer * array_match_value_prefix_nc (const array * const a, const buffer * const b); +data_unset * array_match_key_suffix (const array * const a, const buffer * const b); +data_unset * array_match_key_suffix_nc (const array * const a, const buffer * const b); +const buffer * array_match_value_suffix (const array * const a, const buffer * const b); +const buffer * array_match_value_suffix_nc (const array * const a, const buffer * const b); +data_unset * array_match_path_or_ext (const array * const a, const buffer * const b); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/base.h b/data/lighttpd/lighttpd-1.4.53/src/base.h new file mode 100644 index 000000000..b3c508c83 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/base.h @@ -0,0 +1,468 @@ +#ifndef _BASE_H_ +#define _BASE_H_ +#include "first.h" + +#include "settings.h" + +#include <sys/types.h> +#include <sys/time.h> + +#include "base_decls.h" +#include "buffer.h" +#include "array.h" +#include "chunk.h" +#include "http_kv.h" +#include "sock_addr.h" +#include "etag.h" + +struct fdevents; /* declaration */ +struct stat_cache; /* declaration */ + +#define DIRECT 0 /* con->mode */ + + +typedef struct { + /** HEADER */ + /* the request-line */ + buffer *request; + buffer *uri; + + buffer *orig_uri; + + http_method_t http_method; + http_version_t http_version; + + buffer *request_line; + + /* strings to the header */ + buffer *http_host; /* not alloced */ + + unsigned int htags; /* bitfield of flagged headers present in request */ + array *headers; + + /* CONTENT */ + off_t content_length; /* returned by strtoll() */ + off_t te_chunked; + + /* internal */ + buffer *pathinfo; +} request; + +typedef struct { + off_t content_length; + int keep_alive; /* used by the subrequests in proxy, cgi and fcgi to say the subrequest was keep-alive or not */ + + unsigned int htags; /* bitfield of flagged headers present in response */ + array *headers; + int send_chunked; +} response; + +typedef struct { + buffer *scheme; /* scheme without colon or slashes ( "http" or "https" ) */ + + /* authority with optional portnumber ("site.name" or "site.name:8080" ) NOTE: without "username:password@" */ + buffer *authority; + + /* path including leading slash ("/" or "/index.html") - urldecoded, and sanitized ( buffer_path_simplify() && buffer_urldecode_path() ) */ + buffer *path; + buffer *path_raw; /* raw path, as sent from client. no urldecoding or path simplifying */ + buffer *query; /* querystring ( everything after "?", ie: in "/index.php?foo=1", query is "foo=1" ) */ +} request_uri; + +typedef struct { + buffer *path; + buffer *basedir; /* path = "(basedir)(.*)" */ + + buffer *doc_root; /* path = doc_root + rel_path */ + buffer *rel_path; + + buffer *etag; +} physical; + +typedef struct { + array *mimetypes; + + /* virtual-servers */ + buffer *document_root; + buffer *server_name; + buffer *error_handler; + buffer *error_handler_404; + buffer *server_tag; + buffer *dirlist_encoding; + buffer *errorfile_prefix; + buffer *socket_perms; + + unsigned short high_precision_timestamps; + unsigned short max_keep_alive_requests; + unsigned short max_keep_alive_idle; + unsigned short max_read_idle; + unsigned short max_write_idle; + unsigned short use_xattr; + unsigned short follow_symlink; + unsigned short range_requests; + unsigned short stream_request_body; + unsigned short stream_response_body; + unsigned short error_intercept; + + /* debug */ + + unsigned short log_file_not_found; + unsigned short log_request_header; + unsigned short log_request_handling; + unsigned short log_response_header; + unsigned short log_condition_handling; + unsigned short log_timeouts; + + + /* server wide */ + unsigned short use_ipv6, set_v6only; /* set_v6only is only a temporary option */ + unsigned short defer_accept; + unsigned short ssl_enabled; /* only interesting for setting up listening sockets. don't use at runtime */ + unsigned short allow_http11; + unsigned short etag_use_inode; + unsigned short etag_use_mtime; + unsigned short etag_use_size; + unsigned short force_lowercase_filenames; /* if the FS is case-insensitive, force all files to lower-case */ + unsigned int http_parseopts; + unsigned int max_request_size; + int listen_backlog; + + unsigned short kbytes_per_second; /* connection kb/s limit */ + + /* configside */ + unsigned short global_kbytes_per_second; /* */ + + off_t global_bytes_per_second_cnt; + /* server-wide traffic-shaper + * + * each context has the counter which is inited once + * a second by the global_kbytes_per_second config-var + * + * as soon as global_kbytes_per_second gets below 0 + * the connected conns are "offline" a little bit + * + * the problem: + * we somehow have to loose our "we are writable" signal + * on the way. + * + */ + off_t *global_bytes_per_second_cnt_ptr; /* */ + +#if defined(__FreeBSD__) || defined(__NetBSD__) \ + || defined(__OpenBSD__) || defined(__DragonFly__) + buffer *bsd_accept_filter; +#endif + +} specific_config; + +/* the order of the items should be the same as they are processed + * read before write as we use this later */ +typedef enum { + CON_STATE_CONNECT, + CON_STATE_REQUEST_START, + CON_STATE_READ, + CON_STATE_REQUEST_END, + CON_STATE_READ_POST, + CON_STATE_HANDLE_REQUEST, + CON_STATE_RESPONSE_START, + CON_STATE_WRITE, + CON_STATE_RESPONSE_END, + CON_STATE_ERROR, + CON_STATE_CLOSE +} connection_state_t; + +typedef enum { + /* condition not active at the moment because itself or some + * pre-condition depends on data not available yet + */ + COND_RESULT_UNSET, + + /* special "unset" for branches not selected due to pre-conditions + * not met (but pre-conditions are not "unset" anymore) + */ + COND_RESULT_SKIP, + + /* actually evaluated the condition itself */ + COND_RESULT_FALSE, /* not active */ + COND_RESULT_TRUE /* active */ +} cond_result_t; + +typedef struct cond_cache_t { + /* current result (with preconditions) */ + cond_result_t result; + /* result without preconditions (must never be "skip") */ + cond_result_t local_result; + int patterncount; + int matches[3 * 10]; + buffer *comp_value; /* just a pointer */ +} cond_cache_t; + +struct connection { + connection_state_t state; + + /* timestamps */ + time_t read_idle_ts; + time_t close_timeout_ts; + time_t write_request_ts; + + time_t connection_start; + time_t request_start; + struct timespec request_start_hp; + + size_t request_count; /* number of requests handled in this connection */ + size_t loops_per_request; /* to catch endless loops in a single request + * + * used by mod_rewrite, mod_fastcgi, ... and others + * this is self-protection + */ + + int fd; /* the FD for this connection */ + int fde_ndx; /* index for the fdevent-handler */ + int ndx; /* reverse mapping to server->connection[ndx] */ + + /* fd states */ + int is_readable; + int is_writable; + + int keep_alive; /* only request.c can enable it, all other just disable */ + int keep_alive_idle; /* remember max_keep_alive_idle from config */ + + int file_started; + int file_finished; + + chunkqueue *write_queue; /* a large queue for low-level write ( HTTP response ) [ file, mem ] */ + chunkqueue *read_queue; /* a small queue for low-level read ( HTTP request ) [ mem ] */ + chunkqueue *request_content_queue; /* takes request-content into tempfile if necessary [ tempfile, mem ]*/ + + int traffic_limit_reached; + + off_t bytes_written; /* used by mod_accesslog, mod_rrd */ + off_t bytes_written_cur_second; /* used by mod_accesslog, mod_rrd */ + off_t bytes_read; /* used by mod_accesslog, mod_rrd */ + off_t bytes_header; + + int http_status; + + sock_addr dst_addr; + buffer *dst_addr_buf; + + /* request */ + buffer *parse_request; + + request request; + request_uri uri; + physical physical; + response response; + + size_t header_len; + + array *environment; /* used to pass lighttpd internal stuff to the FastCGI/CGI apps, setenv does that */ + + unsigned int mode; /* DIRECT (0) or plugin id */ + int async_callback; + + void **plugin_ctx; /* plugin connection specific config */ + + specific_config conf; /* global connection specific config */ + cond_cache_t *cond_cache; + + buffer *server_name; + buffer *proto; + + /* error-handler */ + int error_handler_saved_status; + http_method_t error_handler_saved_method; + + struct server_socket *srv_socket; /* reference to the server-socket */ + int (* network_write)(struct server *srv, struct connection *con, chunkqueue *cq, off_t max_bytes); + int (* network_read)(struct server *srv, struct connection *con, chunkqueue *cq, off_t max_bytes); + + /* etag handling */ + etag_flags_t etag_flags; + + int8_t conditional_is_valid[16]; /* MUST be >= COMP_LAST_ELEMENT] */ +}; + +typedef struct { + connection **ptr; + size_t size; + size_t used; +} connections; + +typedef struct { + time_t mtime; /* the key */ + buffer *str; /* a buffer for the string represenation */ +} mtime_cache_type; + +typedef struct { + void *ptr; + size_t used; + size_t size; +} buffer_plugin; + +typedef struct { + unsigned short port; + buffer *bindhost; + + buffer *errorlog_file; + unsigned short errorlog_use_syslog; + buffer *breakagelog_file; + + unsigned short dont_daemonize; + unsigned short preflight_check; + buffer *changeroot; + buffer *username; + buffer *groupname; + + buffer *pid_file; + + buffer *event_handler; + + buffer *modules_dir; + buffer *network_backend; + array *modules; + array *upload_tempdirs; + unsigned int upload_temp_file_size; + unsigned int max_request_field_size; + + unsigned short max_worker; + unsigned short max_fds; + unsigned short max_conns; + + unsigned short log_request_header_on_error; + unsigned short log_state_handling; + + int stat_cache_engine; + unsigned short enable_cores; + unsigned short reject_expect_100_with_417; + buffer *xattr_name; + + unsigned short http_header_strict; + unsigned short http_host_strict; + unsigned short http_host_normalize; + unsigned short http_url_normalize; + unsigned short high_precision_timestamps; + time_t loadts; + double loadavg[3]; + buffer *syslog_facility; + + unsigned short compat_module_load; + unsigned short systemd_socket_activation; +} server_config; + +typedef struct server_socket { + sock_addr addr; + int fd; + int fde_ndx; + + unsigned short is_ssl; + unsigned short sidx; + + buffer *srv_token; +} server_socket; + +typedef struct { + server_socket **ptr; + + size_t size; + size_t used; +} server_socket_array; + +struct server { + server_socket_array srv_sockets; + + /* the errorlog */ + int errorlog_fd; + enum { ERRORLOG_FILE, ERRORLOG_FD, ERRORLOG_SYSLOG, ERRORLOG_PIPE } errorlog_mode; + buffer *errorlog_buf; + + struct fdevents *ev; + + buffer_plugin plugins; + void *plugin_slots; + + /* counters */ + int con_opened; + int con_read; + int con_written; + int con_closed; + + int max_fds; /* max possible fds */ + int cur_fds; /* currently used fds */ + int want_fds; /* waiting fds */ + int sockets_disabled; + + size_t max_conns; + + /* buffers */ + buffer *parse_full_path; + buffer *response_header; + buffer *response_range; + buffer *tmp_buf; + + buffer *tmp_chunk_len; + + buffer *empty_string; /* is necessary for cond_match */ + + buffer *cond_check_buf; + + /* caches */ + mtime_cache_type mtime_cache[FILE_CACHE_MAX]; + + array *split_vals; + + /* Timestamps */ + time_t cur_ts; + time_t last_generated_date_ts; + time_t last_generated_debug_ts; + time_t startup_ts; + + buffer *ts_debug_str; + buffer *ts_date_str; + + /* config-file */ + array *config_touched; + + array *config_context; + specific_config **config_storage; + + server_config srvconf; + + short int config_deprecated; + short int config_unsupported; + + connections *conns; + connections *joblist; + connections *fdwaitqueue; + + struct stat_cache *stat_cache; + + /** + * The status array can carry all the status information you want + * the key to the array is <module-prefix>.<name> + * and the values are counters + * + * example: + * fastcgi.backends = 10 + * fastcgi.active-backends = 6 + * fastcgi.backend.<key>.load = 24 + * fastcgi.backend.<key>.... + * + * fastcgi.backend.<key>.disconnects = ... + */ + array *status; + + int event_handler; + + int (* network_backend_write)(struct server *srv, int fd, chunkqueue *cq, off_t max_bytes); + handler_t (* request_env)(struct server *srv, connection *con); + + uid_t uid; + gid_t gid; + pid_t pid; + + server_socket_array srv_sockets_inherited; +}; + + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/base64.c b/data/lighttpd/lighttpd-1.4.53/src/base64.c new file mode 100644 index 000000000..3034181a5 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/base64.c @@ -0,0 +1,235 @@ +#include "first.h" + +#include "base64.h" + +/* reverse mapping: + * >= 0: base64 value + * -1: invalid character + * -2: skip character (whitespace/control) + * -3: padding + */ + +/* BASE64_STANDARD: "A-Z a-z 0-9 + /" maps to 0-63, pad with "=" */ +static const char base64_standard_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; +static const signed char base64_standard_reverse_table[] = { +/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 0x00 - 0x0F */ + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 0x10 - 0x1F */ + -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 - 0x2F */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -3, -1, -1, /* 0x30 - 0x3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 - 0x4F */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 - 0x5F */ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 - 0x6F */ + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 - 0x7F */ +}; + +/* BASE64_URL: "A-Z a-z 0-9 - _" maps to 0-63, pad with "." */ +static const char base64_url_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_."; +static const signed char base64_url_reverse_table[] = { +/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */ + -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 0x00 - 0x0F */ + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 0x10 - 0x1F */ + -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -3, -1, /* 0x20 - 0x2F */ + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 - 0x3F */ + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 - 0x4F */ + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, /* 0x50 - 0x5F */ + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 - 0x6F */ + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 - 0x7F */ +}; + +unsigned char* buffer_append_base64_decode(buffer *out, const char* in, size_t in_length, base64_charset charset) { + unsigned char *result; + size_t out_pos = 0; /* current output character (position) that is decoded. can contain partial result */ + unsigned int group = 0; /* how many base64 digits in the current group were decoded already. each group has up to 4 digits */ + size_t i; + const signed char *base64_reverse_table; + + switch (charset) { + case BASE64_STANDARD: + base64_reverse_table = base64_standard_reverse_table; + break; + case BASE64_URL: + base64_reverse_table = base64_url_reverse_table; + break; + default: + return NULL; + } + + result = (unsigned char *) buffer_string_prepare_append(out, 3*(in_length / 4) + 3); + + /* run through the whole string, converting as we go */ + for (i = 0; i < in_length; i++) { + unsigned char c = (unsigned char) in[i]; + int ch; + + if (c == '\0') break; + if (c >= 128) return NULL; /* only 7-bit characters allowed */ + + ch = base64_reverse_table[c]; + if (-3 == ch) { + /* pad character; can only come after 2 base64 digits in a group */ + if (group < 2) return NULL; + break; + } else if (-2 == ch) { + continue; /* skip character */ + } else if (ch < 0) { + return NULL; /* invalid character, abort */ + } + + switch(group) { + case 0: + result[out_pos] = ch << 2; + group = 1; + break; + case 1: + result[out_pos++] |= ch >> 4; + result[out_pos] = (ch & 0x0f) << 4; + group = 2; + break; + case 2: + result[out_pos++] |= ch >>2; + result[out_pos] = (ch & 0x03) << 6; + group = 3; + break; + case 3: + result[out_pos++] |= ch; + group = 0; + break; + } + } + + switch(group) { + case 0: + /* ended on boundary */ + break; + case 1: + /* need at least 2 base64 digits per group */ + return NULL; + case 2: + /* have 2 base64 digits in last group => one real octect, two zeroes padded */ + case 3: + /* have 3 base64 digits in last group => two real octects, one zero padded */ + + /* for both cases the current index already is on the first zero padded octet + * - check it really is zero (overlapping bits) */ + if (0 != result[out_pos]) return NULL; + break; + } + + buffer_commit(out, out_pos); + + return result; +} + +size_t li_to_base64_no_padding(char* out, size_t out_length, const unsigned char* in, size_t in_length, base64_charset charset) { + const size_t full_tuples = in_length / 3; + const size_t in_tuple_remainder = in_length % 3; + const size_t out_tuple_remainder = in_tuple_remainder ? 1 + in_tuple_remainder : 0; + const size_t require_space = 4 * full_tuples + out_tuple_remainder; + const char* base64_table; + size_t i; + size_t out_pos = 0; + + switch (charset) { + case BASE64_STANDARD: + base64_table = base64_standard_table; + break; + case BASE64_URL: + base64_table = base64_url_table; + break; + default: + force_assert(0 && "invalid charset"); + } + + /* check overflows */ + force_assert(full_tuples <= 2*full_tuples); + force_assert(full_tuples <= 4*full_tuples); + force_assert(4*full_tuples <= 4*full_tuples + out_tuple_remainder); + force_assert(require_space <= out_length); + + for (i = 2; i < in_length; i += 3) { + unsigned int v = (in[i-2] << 16) | (in[i-1] << 8) | in[i]; + out[out_pos+3] = base64_table[v & 0x3f]; v >>= 6; + out[out_pos+2] = base64_table[v & 0x3f]; v >>= 6; + out[out_pos+1] = base64_table[v & 0x3f]; v >>= 6; + out[out_pos+0] = base64_table[v & 0x3f]; + out_pos += 4; + } + switch (in_tuple_remainder) { + case 0: + break; + case 1: + { + /* pretend in[i-1] = in[i] = 0, don't write last two (out_pos+3, out_pos+2) characters */ + unsigned int v = (in[i-2] << 4); + out[out_pos+1] = base64_table[v & 0x3f]; v >>= 6; + out[out_pos+0] = base64_table[v & 0x3f]; + out_pos += 2; + } + break; + case 2: + { + /* pretend in[i] = 0, don't write last (out_pos+3) character */ + unsigned int v = (in[i-2] << 10) | (in[i-1] << 2); + out[out_pos+2] = base64_table[v & 0x3f]; v >>= 6; + out[out_pos+1] = base64_table[v & 0x3f]; v >>= 6; + out[out_pos+0] = base64_table[v & 0x3f]; + out_pos += 3; + } + break; + } + force_assert(out_pos <= out_length); + return out_pos; +} + +size_t li_to_base64(char* out, size_t out_length, const unsigned char* in, size_t in_length, base64_charset charset) { + const size_t in_tuple_remainder = in_length % 3; + size_t padding_length = in_tuple_remainder ? 3 - in_tuple_remainder : 0; + + char padding; + size_t out_pos; + + switch (charset) { + case BASE64_STANDARD: + padding = base64_standard_table[64]; + break; + case BASE64_URL: + padding = base64_url_table[64]; + break; + default: + force_assert(0 && "invalid charset"); + } + + force_assert(out_length >= padding_length); + out_pos = li_to_base64_no_padding(out, out_length - padding_length, in, in_length, charset); + + while (padding_length > 0) { + out[out_pos++] = padding; + padding_length--; + } + + force_assert(out_pos <= out_length); + + return out_pos; +} + + +char* buffer_append_base64_encode_no_padding(buffer *out, const unsigned char* in, size_t in_length, base64_charset charset) { + size_t reserve = 4*(in_length/3) + 4; + char* result = buffer_string_prepare_append(out, reserve); + size_t out_pos = li_to_base64_no_padding(result, reserve, in, in_length, charset); + + buffer_commit(out, out_pos); + + return result; +} + +char* buffer_append_base64_encode(buffer *out, const unsigned char* in, size_t in_length, base64_charset charset) { + size_t reserve = 4*(in_length/3) + 4; + char* result = buffer_string_prepare_append(out, reserve); + size_t out_pos = li_to_base64(result, reserve, in, in_length, charset); + + buffer_commit(out, out_pos); + + return result; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/base64.h b/data/lighttpd/lighttpd-1.4.53/src/base64.h new file mode 100644 index 000000000..615f32f2a --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/base64.h @@ -0,0 +1,20 @@ +#ifndef _BASE64_H_ +#define _BASE64_H_ +#include "first.h" + +#include "buffer.h" + +typedef enum { + BASE64_STANDARD, + BASE64_URL, +} base64_charset; + +unsigned char* buffer_append_base64_decode(buffer *out, const char* in, size_t in_length, base64_charset charset); + +size_t li_to_base64_no_padding(char* out, size_t out_length, const unsigned char* in, size_t in_length, base64_charset charset); +size_t li_to_base64(char* out, size_t out_length, const unsigned char* in, size_t in_length, base64_charset charset); + +char* buffer_append_base64_encode_no_padding(buffer *out, const unsigned char* in, size_t in_length, base64_charset charset); +char* buffer_append_base64_encode(buffer *out, const unsigned char* in, size_t in_length, base64_charset charset); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/base_decls.h b/data/lighttpd/lighttpd-1.4.53/src/base_decls.h new file mode 100644 index 000000000..33c559bf5 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/base_decls.h @@ -0,0 +1,30 @@ +#ifndef INCLUDED_BASE_DECLS_H +#define INCLUDED_BASE_DECLS_H + +#include "first.h" + +struct server; +typedef struct server server; + +struct connection; +typedef struct connection connection; + +union sock_addr; +typedef union sock_addr sock_addr; + + +enum handler_t { + HANDLER_UNSET, + HANDLER_GO_ON, + HANDLER_FINISHED, + HANDLER_COMEBACK, + HANDLER_WAIT_FOR_EVENT, + HANDLER_ERROR, + HANDLER_WAIT_FOR_FD +}; +typedef enum handler_t handler_t; + +#define BV(x) (1 << x) + + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/buffer.c b/data/lighttpd/lighttpd-1.4.53/src/buffer.c new file mode 100644 index 000000000..261adf55c --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/buffer.c @@ -0,0 +1,1028 @@ +#include "first.h" + +#include "buffer.h" +#include "settings.h" /* BUFFER_MAX_REUSE_SIZE */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> /* strftime() */ + +static const char hex_chars_lc[] = "0123456789abcdef"; +static const char hex_chars_uc[] = "0123456789ABCDEF"; + +/** + * init the buffer + * + */ + +buffer* buffer_init(void) { + buffer *b; + + b = malloc(sizeof(*b)); + force_assert(b); + + b->ptr = NULL; + b->size = 0; + b->used = 0; + + return b; +} + +buffer *buffer_init_buffer(const buffer *src) { + buffer *b = buffer_init(); + buffer_copy_buffer(b, src); + return b; +} + +buffer *buffer_init_string(const char *str) { + buffer *b = buffer_init(); + buffer_copy_string(b, str); + return b; +} + +void buffer_free(buffer *b) { + if (NULL == b) return; + + free(b->ptr); + free(b); +} + +__attribute_cold__ +static void buffer_free_ptr(buffer *b) { + if (NULL == b) return; + free(b->ptr); + b->ptr = NULL; + b->used = 0; + b->size = 0; +} + +void buffer_reset(buffer *b) { + if (NULL != b && b->size > 0) { + b->used = 0; + /* release buffer larger than ... bytes */ + if (b->size > BUFFER_MAX_REUSE_SIZE) buffer_free_ptr(b); + } +} + +void buffer_move(buffer *b, buffer *src) { + buffer tmp; + force_assert(NULL != b); + force_assert(NULL != src); + + buffer_clear(b); + tmp = *src; *src = *b; *b = tmp; +} + +/* make sure buffer is at least "size" big + 1 for '\0'. keep old data */ +__attribute_cold__ +static void buffer_realloc(buffer *b, size_t len) { + #define BUFFER_PIECE_SIZE 64uL /*(must be power-of-2)*/ + const size_t sz = (len + 1 + BUFFER_PIECE_SIZE-1) & ~(BUFFER_PIECE_SIZE-1); + force_assert(sz > len); + + b->size = sz; + b->ptr = realloc(b->ptr, sz); + + force_assert(NULL != b->ptr); +} + +__attribute_cold__ +static void buffer_alloc_replace(buffer *b, size_t size) { + /*(discard old data so realloc() does not copy)*/ + if (NULL != b->ptr) { + free(b->ptr); + b->ptr = NULL; + } + buffer_realloc(b, size); +} + +char* buffer_string_prepare_copy(buffer *b, size_t size) { + force_assert(NULL != b); + + if (size >= b->size) buffer_alloc_replace(b, size); + + b->used = 0; + return b->ptr; +} + +char* buffer_string_prepare_append(buffer *b, size_t size) { + force_assert(NULL != b); + + if (b->used && size < b->size - b->used) + return b->ptr + b->used - 1; + + if (buffer_string_is_empty(b)) { + return buffer_string_prepare_copy(b, size); + } else { + /* not empty, b->used already includes a terminating 0 */ + size_t req_size = b->used + size; + + /* check for overflow: unsigned overflow is defined to wrap around */ + force_assert(req_size >= b->used); + + buffer_realloc(b, req_size); + + return b->ptr + b->used - 1; + } +} + +void buffer_string_set_length(buffer *b, size_t len) { + force_assert(NULL != b); + + if (len >= b->size) buffer_realloc(b, len); + + b->used = len + 1; + b->ptr[len] = '\0'; +} + +void buffer_commit(buffer *b, size_t size) +{ + force_assert(NULL != b); + force_assert(b->size > 0); + + if (0 == b->used) b->used = 1; + + if (size > 0) { + /* check for overflow: unsigned overflow is defined to wrap around */ + size_t sz = b->used + size; + force_assert(sz > b->used); + force_assert(sz <= b->size); + b->used = sz; + } + + b->ptr[b->used - 1] = '\0'; +} + +void buffer_copy_string(buffer *b, const char *s) { + buffer_copy_string_len(b, s, NULL != s ? strlen(s) : 0); +} + +void buffer_copy_string_len(buffer *b, const char *s, size_t s_len) { + force_assert(NULL != b); + force_assert(NULL != s || s_len == 0); + + if (s_len >= b->size) buffer_string_prepare_copy(b, s_len); + + if (0 != s_len) memcpy(b->ptr, s, s_len); /*(s might be NULL)*/ + b->ptr[s_len] = '\0'; + b->used = s_len + 1; +} + +void buffer_append_string(buffer *b, const char *s) { + buffer_append_string_len(b, s, NULL != s ? strlen(s) : 0); +} + +/** + * append a string to the end of the buffer + * + * the resulting buffer is terminated with a '\0' + * s is treated as a un-terminated string (a \0 is handled a normal character) + * + * @param b a buffer + * @param s the string + * @param s_len size of the string (without the terminating \0) + */ + +void buffer_append_string_len(buffer *b, const char *s, size_t s_len) { + char *target_buf; + + force_assert(NULL != b); + force_assert(NULL != s || s_len == 0); + + target_buf = buffer_string_prepare_append(b, s_len); + if (0 == b->used) ++b->used; /*(must include '\0' for append below)*/ + + /*(s might be NULL if 0 == s_len)*/ + if (s_len) memcpy(target_buf, s, s_len); + target_buf[s_len] = '\0'; + b->used += s_len; +} + +void buffer_append_path_len(buffer *b, const char *a, size_t alen) { + size_t blen = buffer_string_length(b); + int aslash = (alen && a[0] == '/'); + buffer_string_prepare_append(b, alen+2); /*(+ '/' and + '\0' if 0 == blen)*/ + if (blen && b->ptr[blen-1] == '/') { + if (aslash) --b->used; + } + else { + if (!b->used) ++b->used; + if (!aslash) b->ptr[++b->used - 2] = '/'; + } + memcpy(b->ptr+b->used-1, a, alen); + b->ptr[(b->used += alen)-1] = '\0'; +} + +void buffer_append_uint_hex_lc(buffer *b, uintmax_t value) { + char *buf; + unsigned int shift = 0; + + { + uintmax_t copy = value; + do { + copy >>= 8; + shift += 8; /* counting bits */ + } while (0 != copy); + } + + buf = buffer_string_prepare_append(b, shift >> 2); /*nibbles (4 bits)*/ + buffer_commit(b, shift >> 2); /* will fill below */ + + while (shift > 0) { + shift -= 4; + *(buf++) = hex_chars_lc[(value >> shift) & 0x0F]; + } +} + +static char* utostr(char * const buf_end, uintmax_t val) { + char *cur = buf_end; + do { + int mod = val % 10; + val /= 10; + /* prepend digit mod */ + *(--cur) = (char) ('0' + mod); + } while (0 != val); + return cur; +} + +static char* itostr(char * const buf_end, intmax_t val) { + /* absolute value not defined for INTMAX_MIN, but can take absolute + * value of any negative number via twos complement cast to unsigned. + * negative sign is prepended after (now unsigned) value is converted + * to string */ + uintmax_t uval = val >= 0 ? (uintmax_t)val : ((uintmax_t)~val) + 1; + char *cur = utostr(buf_end, uval); + if (val < 0) *(--cur) = '-'; + + return cur; +} + +void buffer_append_int(buffer *b, intmax_t val) { + char buf[LI_ITOSTRING_LENGTH]; + char* const buf_end = buf + sizeof(buf); + char *str; + + force_assert(NULL != b); + + str = itostr(buf_end, val); + force_assert(buf_end > str && str >= buf); + + buffer_append_string_len(b, str, buf_end - str); +} + +void buffer_copy_int(buffer *b, intmax_t val) { + force_assert(NULL != b); + + b->used = 0; + buffer_append_int(b, val); +} + +void buffer_append_strftime(buffer *b, const char *format, const struct tm *tm) { + size_t r; + char* buf; + force_assert(NULL != b); + force_assert(NULL != format); + force_assert(NULL != tm); + + buf = buffer_string_prepare_append(b, 255); + r = strftime(buf, buffer_string_space(b), format, tm); + + /* 0 (in some apis buffer_string_space(b)) signals the string may have + * been too small; but the format could also just have lead to an empty + * string + */ + if (0 == r || r >= buffer_string_space(b)) { + /* give it a second try with a larger string */ + buf = buffer_string_prepare_append(b, 4095); + r = strftime(buf, buffer_string_space(b), format, tm); + } + + if (r >= buffer_string_space(b)) r = 0; + + buffer_commit(b, r); +} + + +void li_itostrn(char *buf, size_t buf_len, intmax_t val) { + char p_buf[LI_ITOSTRING_LENGTH]; + char* const p_buf_end = p_buf + sizeof(p_buf); + char* str = p_buf_end - 1; + *str = '\0'; + + str = itostr(str, val); + force_assert(p_buf_end > str && str >= p_buf); + + force_assert(buf_len >= (size_t) (p_buf_end - str)); + memcpy(buf, str, p_buf_end - str); +} + +void li_utostrn(char *buf, size_t buf_len, uintmax_t val) { + char p_buf[LI_ITOSTRING_LENGTH]; + char* const p_buf_end = p_buf + sizeof(p_buf); + char* str = p_buf_end - 1; + *str = '\0'; + + str = utostr(str, val); + force_assert(p_buf_end > str && str >= p_buf); + + force_assert(buf_len >= (size_t) (p_buf_end - str)); + memcpy(buf, str, p_buf_end - str); +} + +#define li_ntox_lc(n) ((n) <= 9 ? (n) + '0' : (n) + 'a' - 10) + +char int2hex(char c) { + /*return li_ntox_lc(c & 0xF);*/ + return hex_chars_lc[(c & 0x0F)]; +} + +/* c (char) and n (nibble) MUST be unsigned integer types */ +#define li_cton(c,n) \ + (((n) = (c) - '0') <= 9 || (((n) = ((c)&0xdf) - 'A') <= 5 ? ((n) += 10) : 0)) + +/* converts hex char (0-9, A-Z, a-z) to decimal. + * returns 0xFF on invalid input. + */ +char hex2int(unsigned char hex) { + unsigned char n; + return li_cton(hex,n) ? (char)n : 0xFF; +} + +/** + * check if two buffer contain the same data + */ + +int buffer_is_equal(const buffer *a, const buffer *b) { + force_assert(NULL != a && NULL != b); + + /* 1 = equal; 0 = not equal */ + return (a->used == b->used && 0 == memcmp(a->ptr, b->ptr, a->used)); +} + +int buffer_is_equal_string(const buffer *a, const char *s, size_t b_len) { + force_assert(NULL != a && NULL != s); + force_assert(b_len + 1 > b_len); + + /* 1 = equal; 0 = not equal */ + return (a->used == b_len + 1 && 0 == memcmp(a->ptr, s, b_len)); +} + +/* buffer_is_equal_caseless_string(b, CONST_STR_LEN("value")) */ +int buffer_is_equal_caseless_string(const buffer *a, const char *s, size_t b_len) { + force_assert(NULL != a && NULL != s); + force_assert(b_len + 1 > b_len); + + /* 1 = equal; 0 = not equal */ + return (a->used == b_len + 1 && 0 == strncasecmp(a->ptr, s, b_len)); +} + +int buffer_caseless_compare(const char *a, size_t a_len, const char *b, size_t b_len) { + size_t const len = (a_len < b_len) ? a_len : b_len; + size_t i; + + for (i = 0; i < len; ++i) { + unsigned char ca = a[i], cb = b[i]; + if (ca == cb) continue; + + /* always lowercase for transitive results */ + if (ca >= 'A' && ca <= 'Z') ca |= 32; + if (cb >= 'A' && cb <= 'Z') cb |= 32; + + if (ca == cb) continue; + return ((int)ca) - ((int)cb); + } + if (a_len == b_len) return 0; + return a_len < b_len ? -1 : 1; +} + +int buffer_is_equal_right_len(const buffer *b1, const buffer *b2, size_t len) { + /* no len -> equal */ + if (len == 0) return 1; + + /* len > 0, but empty buffers -> not equal */ + if (b1->used == 0 || b2->used == 0) return 0; + + /* buffers too small -> not equal */ + if (b1->used - 1 < len || b2->used - 1 < len) return 0; + + return 0 == memcmp(b1->ptr + b1->used - 1 - len, b2->ptr + b2->used - 1 - len, len); +} + + +void li_tohex_lc(char *buf, size_t buf_len, const char *s, size_t s_len) { + force_assert(2 * s_len > s_len); + force_assert(2 * s_len < buf_len); + + for (size_t i = 0; i < s_len; ++i) { + buf[2*i] = hex_chars_lc[(s[i] >> 4) & 0x0F]; + buf[2*i+1] = hex_chars_lc[s[i] & 0x0F]; + } + buf[2*s_len] = '\0'; +} + +void li_tohex_uc(char *buf, size_t buf_len, const char *s, size_t s_len) { + force_assert(2 * s_len > s_len); + force_assert(2 * s_len < buf_len); + + for (size_t i = 0; i < s_len; ++i) { + buf[2*i] = hex_chars_uc[(s[i] >> 4) & 0x0F]; + buf[2*i+1] = hex_chars_uc[s[i] & 0x0F]; + } + buf[2*s_len] = '\0'; +} + + +void buffer_substr_replace (buffer * const b, const size_t offset, + const size_t len, const buffer * const replace) +{ + const size_t blen = buffer_string_length(b); + const size_t rlen = buffer_string_length(replace); + + if (rlen > len) { + buffer_string_set_length(b, blen-len+rlen); + memmove(b->ptr+offset+rlen, b->ptr+offset+len, blen-offset-len); + } + + memcpy(b->ptr+offset, replace->ptr, rlen); + + if (rlen < len) { + memmove(b->ptr+offset+rlen, b->ptr+offset+len, blen-offset-len); + buffer_string_set_length(b, blen-len+rlen); + } +} + + +void buffer_append_string_encoded_hex_lc(buffer *b, const char *s, size_t len) { + unsigned char * const p = + (unsigned char*) buffer_string_prepare_append(b, len*2); + buffer_commit(b, len*2); /* fill below */ + for (size_t i = 0; i < len; ++i) { + p[(i<<1)] = hex_chars_lc[(s[i] >> 4) & 0x0F]; + p[(i<<1)+1] = hex_chars_lc[(s[i]) & 0x0F]; + } +} + +void buffer_append_string_encoded_hex_uc(buffer *b, const char *s, size_t len) { + unsigned char * const p = + (unsigned char*) buffer_string_prepare_append(b, len*2); + buffer_commit(b, len*2); /* fill below */ + for (size_t i = 0; i < len; ++i) { + p[(i<<1)] = hex_chars_uc[(s[i] >> 4) & 0x0F]; + p[(i<<1)+1] = hex_chars_uc[(s[i]) & 0x0F]; + } +} + + +/* everything except: ! ( ) * - . 0-9 A-Z _ a-z */ +static const char encoded_chars_rel_uri_part[] = { + /* + 0 1 2 3 4 5 6 7 8 9 A B C D E F + */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ + 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, /* 20 - 2F space " # $ % & ' + , / */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 30 - 3F : ; < = > ? */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F @ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 70 - 7F { | } DEL */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* B0 - BF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0 - CF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* D0 - DF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0 - EF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ +}; + +/* everything except: ! ( ) * - . / 0-9 A-Z _ a-z */ +static const char encoded_chars_rel_uri[] = { + /* + 0 1 2 3 4 5 6 7 8 9 A B C D E F + */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ + 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, /* 20 - 2F space " # $ % & ' + , */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 30 - 3F : ; < = > ? */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F @ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 70 - 7F { | } DEL */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* B0 - BF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0 - CF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* D0 - DF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0 - EF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ +}; + +static const char encoded_chars_html[] = { + /* + 0 1 2 3 4 5 6 7 8 9 A B C D E F + */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ + 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F " & ' */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* B0 - BF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0 - CF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* D0 - DF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0 - EF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ +}; + +static const char encoded_chars_minimal_xml[] = { + /* + 0 1 2 3 4 5 6 7 8 9 A B C D E F + */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ + 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F " & ' */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 5F */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* 70 - 7F DEL */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ +}; + + + +void buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_encoding_t encoding) { + unsigned char *ds, *d; + size_t d_len, ndx; + const char *map = NULL; + + force_assert(NULL != b); + force_assert(NULL != s || 0 == s_len); + + if (0 == s_len) return; + + switch(encoding) { + case ENCODING_REL_URI: + map = encoded_chars_rel_uri; + break; + case ENCODING_REL_URI_PART: + map = encoded_chars_rel_uri_part; + break; + case ENCODING_HTML: + map = encoded_chars_html; + break; + case ENCODING_MINIMAL_XML: + map = encoded_chars_minimal_xml; + break; + } + + force_assert(NULL != map); + + /* count to-be-encoded-characters */ + for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { + if (map[*ds]) { + switch(encoding) { + case ENCODING_REL_URI: + case ENCODING_REL_URI_PART: + d_len += 3; + break; + case ENCODING_HTML: + case ENCODING_MINIMAL_XML: + d_len += 6; + break; + } + } else { + d_len++; + } + } + + d = (unsigned char*) buffer_string_prepare_append(b, d_len); + buffer_commit(b, d_len); /* fill below */ + + for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { + if (map[*ds]) { + switch(encoding) { + case ENCODING_REL_URI: + case ENCODING_REL_URI_PART: + d[d_len++] = '%'; + d[d_len++] = hex_chars_uc[((*ds) >> 4) & 0x0F]; + d[d_len++] = hex_chars_uc[(*ds) & 0x0F]; + break; + case ENCODING_HTML: + case ENCODING_MINIMAL_XML: + d[d_len++] = '&'; + d[d_len++] = '#'; + d[d_len++] = 'x'; + d[d_len++] = hex_chars_uc[((*ds) >> 4) & 0x0F]; + d[d_len++] = hex_chars_uc[(*ds) & 0x0F]; + d[d_len++] = ';'; + break; + } + } else { + d[d_len++] = *ds; + } + } +} + +void buffer_append_string_c_escaped(buffer *b, const char *s, size_t s_len) { + unsigned char *ds, *d; + size_t d_len, ndx; + + force_assert(NULL != b); + force_assert(NULL != s || 0 == s_len); + + if (0 == s_len) return; + + /* count to-be-encoded-characters */ + for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { + if ((*ds < 0x20) /* control character */ + || (*ds >= 0x7f)) { /* DEL + non-ASCII characters */ + switch (*ds) { + case '\t': + case '\r': + case '\n': + d_len += 2; + break; + default: + d_len += 4; /* \xCC */ + break; + } + } else { + d_len++; + } + } + + d = (unsigned char*) buffer_string_prepare_append(b, d_len); + buffer_commit(b, d_len); /* fill below */ + + for (ds = (unsigned char *)s, d_len = 0, ndx = 0; ndx < s_len; ds++, ndx++) { + if ((*ds < 0x20) /* control character */ + || (*ds >= 0x7f)) { /* DEL + non-ASCII characters */ + d[d_len++] = '\\'; + switch (*ds) { + case '\t': + d[d_len++] = 't'; + break; + case '\r': + d[d_len++] = 'r'; + break; + case '\n': + d[d_len++] = 'n'; + break; + default: + d[d_len++] = 'x'; + d[d_len++] = hex_chars_lc[((*ds) >> 4) & 0x0F]; + d[d_len++] = hex_chars_lc[(*ds) & 0x0F]; + break; + } + } else { + d[d_len++] = *ds; + } + } +} + + +void buffer_copy_string_encoded_cgi_varnames(buffer *b, const char *s, size_t s_len, int is_http_header) { + size_t i, j = 0; + + force_assert(NULL != b); + force_assert(NULL != s || 0 == s_len); + + buffer_string_prepare_copy(b, s_len + 5); + + if (is_http_header && NULL != s && 0 != strcasecmp(s, "CONTENT-TYPE")) { + buffer_copy_string_len(b, CONST_STR_LEN("HTTP_")); + j = 5; /* "HTTP_" */ + } + + for (i = 0; i < s_len; ++i) { + unsigned char cr = s[i]; + if (light_isalpha(cr)) { + /* upper-case */ + cr &= ~32; + } else if (!light_isdigit(cr)) { + cr = '_'; + } + b->ptr[j++] = cr; + } + b->used = j; + b->ptr[b->used++] = '\0'; +} + +/* decodes url-special-chars inplace. + * replaces non-printable characters with '_' + */ + +static void buffer_urldecode_internal(buffer *url, int is_query) { + unsigned char high, low; + char *src; + char *dst; + + force_assert(NULL != url); + if (buffer_string_is_empty(url)) return; + + force_assert('\0' == url->ptr[url->used-1]); + + src = (char*) url->ptr; + + while ('\0' != *src) { + if ('%' == *src) break; + if (is_query && '+' == *src) *src = ' '; + src++; + } + dst = src; + + while ('\0' != *src) { + if (is_query && *src == '+') { + *dst = ' '; + } else if (*src == '%') { + *dst = '%'; + + high = hex2int(*(src + 1)); + if (0xFF != high) { + low = hex2int(*(src + 2)); + if (0xFF != low) { + high = (high << 4) | low; + + /* map control-characters out */ + if (high < 32 || high == 127) high = '_'; + + *dst = high; + src += 2; + } + } + } else { + *dst = *src; + } + + dst++; + src++; + } + + *dst = '\0'; + url->used = (dst - url->ptr) + 1; +} + +void buffer_urldecode_path(buffer *url) { + buffer_urldecode_internal(url, 0); +} + +void buffer_urldecode_query(buffer *url) { + buffer_urldecode_internal(url, 1); +} + +int buffer_is_valid_UTF8(const buffer *b) { + /* https://www.w3.org/International/questions/qa-forms-utf-8 */ + const unsigned char *c = (unsigned char *)b->ptr; + while (*c) { + + /*(note: includes ctrls)*/ + if ( c[0] < 0x80 ) { ++c; continue; } + + if ( 0xc2 <= c[0] && c[0] <= 0xdf + && 0x80 <= c[1] && c[1] <= 0xbf ) { c+=2; continue; } + + if ( ( ( 0xe0 == c[0] + && 0xa0 <= c[1] && c[1] <= 0xbf) + || ( 0xe1 <= c[0] && c[0] <= 0xef && c[0] != 0xed + && 0x80 <= c[1] && c[1] <= 0xbf) + || ( 0xed == c[0] + && 0x80 <= c[1] && c[1] <= 0x9f) ) + && 0x80 <= c[2] && c[2] <= 0xbf ) { c+=3; continue; } + + if ( ( ( 0xf0 == c[0] + && 0x90 <= c[1] && c[1] <= 0xbf) + || ( 0xf1 <= c[0] && c[0] <= 0xf3 + && 0x80 <= c[1] && c[1] <= 0xbf) + || ( 0xf4 == c[0] + && 0x80 <= c[1] && c[1] <= 0x8f) ) + && 0x80 <= c[2] && c[2] <= 0xbf + && 0x80 <= c[3] && c[3] <= 0xbf ) { c+=4; continue; } + + return 0; /* invalid */ + } + return 1; /* valid */ +} + +/* - special case: empty string returns empty string + * - on windows or cygwin: replace \ with / + * - strip leading spaces + * - prepends "/" if not present already + * - resolve "/../", "//" and "/./" the usual way: + * the first one removes a preceding component, the other two + * get compressed to "/". + * - "/." and "/.." at the end are similar, but always leave a trailing + * "/" + * + * /blah/.. gets / + * /blah/../foo gets /foo + * /abc/./xyz gets /abc/xyz + * /abc//xyz gets /abc/xyz + * + * NOTE: src and dest can point to the same buffer, in which case, + * the operation is performed in-place. + */ + +void buffer_path_simplify(buffer *dest, buffer *src) +{ + /* current character, the one before, and the one before that from input */ + char c, pre1, pre2; + char *start, *slash, *walk, *out; + + force_assert(NULL != dest && NULL != src); + + if (buffer_string_is_empty(src)) { + buffer_copy_string_len(dest, CONST_STR_LEN("")); + return; + } + + force_assert('\0' == src->ptr[src->used-1]); + +#if defined(__WIN32) || defined(__CYGWIN__) + /* cygwin is treating \ and / the same, so we have to that too */ + { + char *p; + for (p = src->ptr; *p; p++) { + if (*p == '\\') *p = '/'; + } + } +#endif + + walk = src->ptr; + start = dest->ptr; + out = dest->ptr; + slash = dest->ptr; + + /* skip leading spaces */ + while (*walk == ' ') { + walk++; + } + if (*walk == '.') { + if (walk[1] == '/' || walk[1] == '\0') + ++walk; + else if (walk[1] == '.' && (walk[2] == '/' || walk[2] == '\0')) + walk+=2; + } + + pre1 = 0; + c = *(walk++); + + while (c != '\0') { + /* assert((src != dest || out <= walk) && slash <= out); */ + /* the following comments about out and walk are only interesting if + * src == dest; otherwise the memory areas don't overlap anyway. + */ + pre2 = pre1; + pre1 = c; + + /* possibly: out == walk - need to read first */ + c = *walk; + *out = pre1; + + out++; + walk++; + /* (out <= walk) still true; also now (slash < out) */ + + if (c == '/' || c == '\0') { + const size_t toklen = out - slash; + if (toklen == 3 && pre2 == '.' && pre1 == '.' && *slash == '/') { + /* "/../" or ("/.." at end of string) */ + out = slash; + /* if there is something before "/..", there is at least one + * component, which needs to be removed */ + if (out > start) { + out--; + while (out > start && *out != '/') out--; + } + + /* don't kill trailing '/' at end of path */ + if (c == '\0') out++; + /* slash < out before, so out_new <= slash + 1 <= out_before <= walk */ + } else if (toklen == 1 || (pre2 == '/' && pre1 == '.')) { + /* "//" or "/./" or (("/" or "/.") at end of string) */ + out = slash; + /* don't kill trailing '/' at end of path */ + if (c == '\0') out++; + /* slash < out before, so out_new <= slash + 1 <= out_before <= walk */ + } + + slash = out; + } + } + + buffer_string_set_length(dest, out - start); +} + +void buffer_to_lower(buffer *b) { + size_t i; + + for (i = 0; i < b->used; ++i) { + char c = b->ptr[i]; + if (c >= 'A' && c <= 'Z') b->ptr[i] |= 0x20; + } +} + + +void buffer_to_upper(buffer *b) { + size_t i; + + for (i = 0; i < b->used; ++i) { + char c = b->ptr[i]; + if (c >= 'a' && c <= 'z') b->ptr[i] &= ~0x20; + } +} + + +#include <stdio.h> + +#ifdef HAVE_LIBUNWIND +# define UNW_LOCAL_ONLY +# include <libunwind.h> + +static void print_backtrace(FILE *file) { + unw_cursor_t cursor; + unw_context_t context; + int ret; + unsigned int frame = 0; + + if (0 != (ret = unw_getcontext(&context))) goto error; + if (0 != (ret = unw_init_local(&cursor, &context))) goto error; + + fprintf(file, "Backtrace:\n"); + + while (0 < (ret = unw_step(&cursor))) { + unw_word_t proc_ip = 0; + unw_proc_info_t procinfo; + char procname[256]; + unw_word_t proc_offset = 0; + + if (0 != (ret = unw_get_reg(&cursor, UNW_REG_IP, &proc_ip))) goto error; + + if (0 == proc_ip) { + /* without an IP the other functions are useless; unw_get_proc_name would return UNW_EUNSPEC */ + ++frame; + fprintf(file, "%u: (nil)\n", frame); + continue; + } + + if (0 != (ret = unw_get_proc_info(&cursor, &procinfo))) goto error; + + if (0 != (ret = unw_get_proc_name(&cursor, procname, sizeof(procname), &proc_offset))) { + switch (-ret) { + case UNW_ENOMEM: + memset(procname + sizeof(procname) - 4, '.', 3); + procname[sizeof(procname) - 1] = '\0'; + break; + case UNW_ENOINFO: + procname[0] = '?'; + procname[1] = '\0'; + proc_offset = 0; + break; + default: + snprintf(procname, sizeof(procname), "?? (unw_get_proc_name error %d)", -ret); + break; + } + } + + ++frame; + fprintf(file, "%u: %s (+0x%x) [%p]\n", + frame, + procname, + (unsigned int) proc_offset, + (void*)(uintptr_t)proc_ip); + } + + if (0 != ret) goto error; + + return; + +error: + fprintf(file, "Error while generating backtrace: unwind error %i\n", (int) -ret); +} +#else +static void print_backtrace(FILE *file) { + UNUSED(file); +} +#endif + +void log_failed_assert(const char *filename, unsigned int line, const char *msg) { + /* can't use buffer here; could lead to recursive assertions */ + fprintf(stderr, "%s.%u: %s\n", filename, line, msg); + print_backtrace(stderr); + fflush(stderr); + abort(); +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/buffer.h b/data/lighttpd/lighttpd-1.4.53/src/buffer.h new file mode 100644 index 000000000..df461e1a8 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/buffer.h @@ -0,0 +1,226 @@ +#ifndef _BUFFER_H_ +#define _BUFFER_H_ +#include "first.h" + +struct tm; /* declaration */ + +/* generic string + binary data container; contains a terminating 0 in both + * cases + * + * used == 0 indicates a special "empty" state (unset config values); ptr + * might be NULL too then. otherwise an empty string has used == 1 (and ptr[0] + * == 0); + * + * copy/append functions will ensure used >= 1 (i.e. never leave it in the + * special empty state); only buffer_copy_buffer will copy the special empty + * state. + */ +typedef struct { + char *ptr; + + /* "used" includes a terminating 0 */ + size_t used; + /* size of allocated buffer at *ptr */ + size_t size; +} buffer; + +/* create new buffer; either empty or copy given data */ +buffer* buffer_init(void); +buffer* buffer_init_buffer(const buffer *src); /* src can be NULL */ +buffer* buffer_init_string(const char *str); /* str can be NULL */ + +void buffer_free(buffer *b); /* b can be NULL */ +/* truncates to used == 0; frees large buffers, might keep smaller ones for reuse */ +void buffer_reset(buffer *b); /* b can be NULL */ + +/* reset b. if NULL != b && NULL != src, move src content to b. reset src. */ +void buffer_move(buffer *b, buffer *src); + +/* make sure buffer is large enough to store a string of given size + * and a terminating zero. + * sets b to an empty string, and may drop old content. + * @return b->ptr + */ +char* buffer_string_prepare_copy(buffer *b, size_t size); + +/* allocate buffer large enough to be able to append a string of given size + * if b was empty (used == 0) it will contain an empty string (used == 1) + * afterwards + * "used" data is preserved; if not empty buffer must contain a + * zero terminated string. + */ +char* buffer_string_prepare_append(buffer *b, size_t size); + +/* use after prepare_(copy,append) when you have written data to the buffer + * to increase the buffer length by size. also sets the terminating zero. + * requires enough space is present for the terminating zero (prepare with the + * same size to be sure). + */ +void buffer_commit(buffer *b, size_t size); + +/* sets string length: + * - always stores a terminating zero to terminate the "new" string + * - does not modify the string data apart from terminating zero + * - reallocates the buffer iff needed + */ +void buffer_string_set_length(buffer *b, size_t len); + +/* clear buffer + * - invalidate buffer contents + * - unsets used chars but does not modify existing ptr contents + * (b->ptr *is not* set to an empty, '\0'-terminated string "") + */ +static inline void buffer_clear(buffer *b); + +void buffer_copy_string(buffer *b, const char *s); +void buffer_copy_string_len(buffer *b, const char *s, size_t s_len); +static inline void buffer_copy_buffer(buffer *b, const buffer *src); + +void buffer_append_string(buffer *b, const char *s); +void buffer_append_string_len(buffer *b, const char *s, size_t s_len); +static inline void buffer_append_string_buffer(buffer *b, const buffer *src); + +#define buffer_append_uint_hex(b,len) buffer_append_uint_hex_lc((b),(len)) +void buffer_append_uint_hex_lc(buffer *b, uintmax_t len); +void buffer_append_int(buffer *b, intmax_t val); +void buffer_copy_int(buffer *b, intmax_t val); + +void buffer_append_strftime(buffer *b, const char *format, const struct tm *tm); + +/* '-', log_10 (2^bits) = bits * log 2 / log 10 < bits * 0.31, terminating 0 */ +#define LI_ITOSTRING_LENGTH (2 + (8 * sizeof(intmax_t) * 31 + 99) / 100) + +void li_itostrn(char *buf, size_t buf_len, intmax_t val); +void li_utostrn(char *buf, size_t buf_len, uintmax_t val); + +/* buf must be (at least) 2*s_len + 1 big. uses lower-case hex letters. */ +#define li_tohex(buf,buf_len,s,s_len) li_tohex_lc((buf),(buf_len),(s),(s_len)) +void li_tohex_lc(char *buf, size_t buf_len, const char *s, size_t s_len); +void li_tohex_uc(char *buf, size_t buf_len, const char *s, size_t s_len); + +/* NULL buffer or empty buffer (used == 0); + * unset "string" (buffer) config options are initialized to used == 0, + * while setting an empty string leads to used == 1 + */ +static inline int buffer_is_empty(const buffer *b); +/* NULL buffer, empty buffer (used == 0) or empty string (used == 1) */ +static inline int buffer_string_is_empty(const buffer *b); + +int buffer_is_equal(const buffer *a, const buffer *b); +int buffer_is_equal_right_len(const buffer *a, const buffer *b, size_t len); +int buffer_is_equal_string(const buffer *a, const char *s, size_t b_len); +int buffer_is_equal_caseless_string(const buffer *a, const char *s, size_t b_len); +int buffer_caseless_compare(const char *a, size_t a_len, const char *b, size_t b_len); + +void buffer_substr_replace (buffer *b, size_t offset, size_t len, const buffer *replace); + +void buffer_append_string_encoded_hex_lc(buffer *b, const char *s, size_t len); +void buffer_append_string_encoded_hex_uc(buffer *b, const char *s, size_t len); + +typedef enum { + ENCODING_REL_URI, /* for coding a rel-uri (/with space/and%percent) nicely as part of a href */ + ENCODING_REL_URI_PART, /* same as ENC_REL_URL plus coding / too as %2F */ + ENCODING_HTML, /* & becomes & and so on */ + ENCODING_MINIMAL_XML /* minimal encoding for xml */ +} buffer_encoding_t; + +void buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_encoding_t encoding); + +/* escape non-printable characters; simple escapes for \t, \r, \n; fallback to \xCC */ +void buffer_append_string_c_escaped(buffer *b, const char *s, size_t s_len); + +/* to upper case, replace non alpha-numerics with '_'; if is_http_header prefix with "HTTP_" unless s is "content-type" */ +void buffer_copy_string_encoded_cgi_varnames(buffer *b, const char *s, size_t s_len, int is_http_header); + +void buffer_urldecode_path(buffer *url); +void buffer_urldecode_query(buffer *url); +int buffer_is_valid_UTF8(const buffer *b); +void buffer_path_simplify(buffer *dest, buffer *src); + +void buffer_to_lower(buffer *b); +void buffer_to_upper(buffer *b); + + +/** deprecated */ +char hex2int(unsigned char c); +char int2hex(char i); + +static inline int light_isdigit(int c); +static inline int light_isxdigit(int c); +static inline int light_isalpha(int c); +static inline int light_isalnum(int c); + +static inline int light_isdigit(int c) { + return (c >= '0' && c <= '9'); +} + +static inline int light_isxdigit(int c) { + return light_isdigit(c) || (c |= 32, c >= 'a' && c <= 'f'); +} + +static inline int light_isalpha(int c) { + return (c |= 32, c >= 'a' && c <= 'z'); +} + +static inline int light_isalnum(int c) { + return light_isdigit(c) || light_isalpha(c); +} + + +static inline size_t buffer_string_length(const buffer *b); /* buffer string length without terminating 0 */ +static inline size_t buffer_string_space(const buffer *b); /* maximum length of string that can be stored without reallocating */ +static inline void buffer_append_slash(buffer *b); /* append '/' no non-empty strings not ending in '/' */ +void buffer_append_path_len(buffer *b, const char *a, size_t alen); /* join strings with '/', if '/' not present */ + +#define BUFFER_APPEND_STRING_CONST(x, y) \ + buffer_append_string_len(x, y, sizeof(y) - 1) + +#define BUFFER_COPY_STRING_CONST(x, y) \ + buffer_copy_string_len(x, y, sizeof(y) - 1) + +#define CONST_STR_LEN(x) x, (x) ? sizeof(x) - 1 : 0 +#define CONST_BUF_LEN(x) ((x) ? (x)->ptr : NULL), buffer_string_length(x) + + +#define LI_NORETURN __attribute_noreturn__ + +__attribute_cold__ +void log_failed_assert(const char *filename, unsigned int line, const char *msg) LI_NORETURN; +#define force_assert(x) do { if (!(x)) log_failed_assert(__FILE__, __LINE__, "assertion failed: " #x); } while(0) +#define SEGFAULT() log_failed_assert(__FILE__, __LINE__, "aborted"); + +/* inline implementations */ + +static inline int buffer_is_empty(const buffer *b) { + return NULL == b || 0 == b->used; +} +static inline int buffer_string_is_empty(const buffer *b) { + return NULL == b || b->used < 2; +} + +static inline size_t buffer_string_length(const buffer *b) { + return NULL != b && 0 != b->used ? b->used - 1 : 0; +} + +static inline size_t buffer_string_space(const buffer *b) { + return NULL != b && b->size ? b->size - (b->used | (0 == b->used)) : 0; +} + +static inline void buffer_copy_buffer(buffer *b, const buffer *src) { + buffer_copy_string_len(b, CONST_BUF_LEN(src)); +} + +static inline void buffer_append_string_buffer(buffer *b, const buffer *src) { + buffer_append_string_len(b, CONST_BUF_LEN(src)); +} + +static inline void buffer_append_slash(buffer *b) { + size_t len = buffer_string_length(b); + if (len > 0 && '/' != b->ptr[len-1]) BUFFER_APPEND_STRING_CONST(b, "/"); +} + +static inline void buffer_clear(buffer *b) { + b->used = 0; +} + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/burl.c b/data/lighttpd/lighttpd-1.4.53/src/burl.c new file mode 100644 index 000000000..511826286 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/burl.c @@ -0,0 +1,512 @@ +#include "first.h" +#include "burl.h" + +#include <string.h> + +#include "buffer.h" +#include "base64.h" + +static const char hex_chars_uc[] = "0123456789ABCDEF"; + +/* everything except: ! $ & ' ( ) * + , - . / 0-9 : ; = ? @ A-Z _ a-z ~ */ +static const char encoded_chars_http_uri_reqd[] = { + /* + 0 1 2 3 4 5 6 7 8 9 A B C D E F + */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 00 - 0F control chars */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 10 - 1F */ + 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 2F space " # % */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 30 - 3F < > */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 - 4F */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, /* 50 - 5F [ \ ] ^ */ + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F ` */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* 70 - 7F { | } DEL */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 80 - 8F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 90 - 9F */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* A0 - AF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* B0 - BF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* C0 - CF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* D0 - DF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* E0 - EF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* F0 - FF */ +}; + + +/* c (char) and n (nibble) MUST be unsigned integer types */ +#define li_cton(c,n) \ + (((n) = (c) - '0') <= 9 || (((n) = ((c)&0xdf) - 'A') <= 5 ? ((n) += 10) : 0)) + +/* b (byte) MUST be unsigned integer type + * https://en.wikipedia.org/wiki/UTF-8 + * reject overlong encodings of 7-byte ASCII and invalid UTF-8 + * (but does not detect other overlong multi-byte encodings) */ +#define li_utf8_invalid_byte(b) ((b) >= 0xF5 || ((b)|0x1) == 0xC1) + + +static int burl_is_unreserved (const int c) +{ + return (light_isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~'); +} + + +static int burl_normalize_basic_unreserved_fix (buffer *b, buffer *t, int i, int qs) +{ + int j = i; + const int used = (int)buffer_string_length(b); + const unsigned char * const s = (unsigned char *)b->ptr; + unsigned char * const p = + (unsigned char *)buffer_string_prepare_copy(t,i+(used-i)*3+1); + unsigned int n1, n2; + memcpy(p, s, (size_t)i); + for (; i < used; ++i, ++j) { + if (!encoded_chars_http_uri_reqd[s[i]]) { + if (s[i] == '?' && -1 == qs) qs = j; + p[j] = s[i]; + } + else if (s[i]=='%' && li_cton(s[i+1], n1) && li_cton(s[i+2], n2)) { + const unsigned int x = (n1 << 4) | n2; + if (burl_is_unreserved(x)) { + p[j] = x; + } + else { + p[j] = '%'; + p[++j] = hex_chars_uc[n1]; /*(s[i+1] & 0xdf)*/ + p[++j] = hex_chars_uc[n2]; /*(s[i+2] & 0xdf)*/ + if (li_utf8_invalid_byte(x)) qs = -2; + } + i+=2; + } + else if (s[i] == '#') break; /* ignore fragment */ + else { + p[j] = '%'; + p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF]; + p[++j] = hex_chars_uc[s[i] & 0xF]; + if (li_utf8_invalid_byte(s[i])) qs = -2; + } + } + buffer_commit(t, (size_t)j); + buffer_copy_buffer(b, t); + return qs; +} + + +static int burl_normalize_basic_unreserved (buffer *b, buffer *t) +{ + const unsigned char * const s = (unsigned char *)b->ptr; + const int used = (int)buffer_string_length(b); + unsigned int n1, n2, x; + int qs = -1; + + for (int i = 0; i < used; ++i) { + if (!encoded_chars_http_uri_reqd[s[i]]) { + if (s[i] == '?' && -1 == qs) qs = i; + } + else if (s[i]=='%' && li_cton(s[i+1], n1) && li_cton(s[i+2], n2) + && !burl_is_unreserved((x = (n1 << 4) | n2))) { + if (li_utf8_invalid_byte(x)) qs = -2; + if (s[i+1] >= 'a') b->ptr[i+1] &= 0xdf; /* uppercase hex */ + if (s[i+2] >= 'a') b->ptr[i+2] &= 0xdf; /* uppercase hex */ + i+=2; + } + else if (s[i] == '#') { /* ignore fragment */ + buffer_string_set_length(b, (size_t)i); + break; + } + else { + qs = burl_normalize_basic_unreserved_fix(b, t, i, qs); + break; + } + } + + return qs; +} + + +static int burl_normalize_basic_required_fix (buffer *b, buffer *t, int i, int qs) +{ + int j = i; + const int used = (int)buffer_string_length(b); + const unsigned char * const s = (unsigned char *)b->ptr; + unsigned char * const p = + (unsigned char *)buffer_string_prepare_copy(t,i+(used-i)*3+1); + unsigned int n1, n2; + memcpy(p, s, (size_t)i); + for (; i < used; ++i, ++j) { + if (!encoded_chars_http_uri_reqd[s[i]]) { + if (s[i] == '?' && -1 == qs) qs = j; + p[j] = s[i]; + } + else if (s[i]=='%' && li_cton(s[i+1], n1) && li_cton(s[i+2], n2)) { + const unsigned int x = (n1 << 4) | n2; + if (!encoded_chars_http_uri_reqd[x] + && (qs < 0 ? (x!='/'&&x!='?') : (x!='&'&&x!='='&&x!=';'))) { + p[j] = x; + } + else { + p[j] = '%'; + p[++j] = hex_chars_uc[n1]; /*(s[i+1] & 0xdf)*/ + p[++j] = hex_chars_uc[n2]; /*(s[i+2] & 0xdf)*/ + if (li_utf8_invalid_byte(x)) qs = -2; + } + i+=2; + } + else if (s[i] == '#') break; /* ignore fragment */ + else { + p[j] = '%'; + p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF]; + p[++j] = hex_chars_uc[s[i] & 0xF]; + if (li_utf8_invalid_byte(s[i])) qs = -2; + } + } + buffer_commit(t, (size_t)j); + buffer_copy_buffer(b, t); + return qs; +} + + +static int burl_normalize_basic_required (buffer *b, buffer *t) +{ + const unsigned char * const s = (unsigned char *)b->ptr; + const int used = (int)buffer_string_length(b); + unsigned int n1, n2, x; + int qs = -1; + + for (int i = 0; i < used; ++i) { + if (!encoded_chars_http_uri_reqd[s[i]]) { + if (s[i] == '?' && -1 == qs) qs = i; + } + else if (s[i]=='%' && li_cton(s[i+1], n1) && li_cton(s[i+2], n2) + && (encoded_chars_http_uri_reqd[(x = (n1 << 4) | n2)] + ||(qs < 0 ? (x=='/'||x=='?') : (x=='&'||x=='='||x==';')))){ + if (li_utf8_invalid_byte(x)) qs = -2; + if (s[i+1] >= 'a') b->ptr[i+1] &= 0xdf; /* uppercase hex */ + if (s[i+2] >= 'a') b->ptr[i+2] &= 0xdf; /* uppercase hex */ + i+=2; + } + else if (s[i] == '#') { /* ignore fragment */ + buffer_string_set_length(b, (size_t)i); + break; + } + else { + qs = burl_normalize_basic_required_fix(b, t, i, qs); + break; + } + } + + return qs; +} + + +static int burl_contains_ctrls (const buffer *b) +{ + const char * const s = b->ptr; + const int used = (int)buffer_string_length(b); + for (int i = 0; i < used; ++i) { + if (s[i] == '%' && (s[i+1] < '2' || (s[i+1] == '7' && s[i+2] == 'F'))) + return 1; + } + return 0; +} + + +static void burl_normalize_qs20_to_plus_fix (buffer *b, int i) +{ + char * const s = b->ptr; + const int used = (int)buffer_string_length(b); + int j = i; + for (; i < used; ++i, ++j) { + s[j] = s[i]; + if (s[i] == '%' && s[i+1] == '2' && s[i+2] == '0') { + s[j] = '+'; + i+=2; + } + } + buffer_string_set_length(b, j); +} + + +static void burl_normalize_qs20_to_plus (buffer *b, int qs) +{ + const char * const s = b->ptr; + const int used = qs < 0 ? 0 : (int)buffer_string_length(b); + int i; + if (qs < 0) return; + for (i = qs+1; i < used; ++i) { + if (s[i] == '%' && s[i+1] == '2' && s[i+2] == '0') break; + } + if (i != used) burl_normalize_qs20_to_plus_fix(b, i); +} + + +static int burl_normalize_2F_to_slash_fix (buffer *b, int qs, int i) +{ + char * const s = b->ptr; + const int blen = (int)buffer_string_length(b); + const int used = qs < 0 ? blen : qs; + int j = i; + for (; i < used; ++i, ++j) { + s[j] = s[i]; + if (s[i] == '%' && s[i+1] == '2' && s[i+2] == 'F') { + s[j] = '/'; + i+=2; + } + } + if (qs >= 0) { + memmove(s+j, s+qs, blen - qs); + j += blen - qs; + } + buffer_string_set_length(b, j); + return qs; +} + + +static int burl_normalize_2F_to_slash (buffer *b, int qs, int flags) +{ + /*("%2F" must already have been uppercased during normalization)*/ + const char * const s = b->ptr; + const int used = qs < 0 ? (int)buffer_string_length(b) : qs; + for (int i = 0; i < used; ++i) { + if (s[i] == '%' && s[i+1] == '2' && s[i+2] == 'F') { + return (flags & HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE) + ? burl_normalize_2F_to_slash_fix(b, qs, i) + : -2; /*(flags & HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)*/ + } + } + return qs; +} + + +static int burl_normalize_path (buffer *b, buffer *t, int qs, int flags) +{ + const unsigned char * const s = (unsigned char *)b->ptr; + const int used = (int)buffer_string_length(b); + int path_simplify = 0; + for (int i = 0, len = qs < 0 ? used : qs; i < len; ++i) { + if (s[i] == '.' && (s[i+1] != '.' || ++i) + && (s[i+1] == '/' || s[i+1] == '?' || s[i+1] == '\0')) { + path_simplify = 1; + break; + } + do { ++i; } while (i < len && s[i] != '/'); + if (s[i] == '/' && s[i+1] == '/') { /*(s[len] != '/')*/ + path_simplify = 1; + break; + } + } + + if (path_simplify) { + if (flags & HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT) return -2; + if (qs >= 0) { + buffer_copy_string_len(t, b->ptr+qs, used - qs); + buffer_string_set_length(b, qs); + } + + buffer_path_simplify(b, b); + + if (qs >= 0) { + qs = (int)buffer_string_length(b); + buffer_append_string_len(b, CONST_BUF_LEN(t)); + } + } + + return qs; +} + + +int burl_normalize (buffer *b, buffer *t, int flags) +{ + int qs; + + #if defined(__WIN32) || defined(__CYGWIN__) + /* Windows and Cygwin treat '\\' as '/' if '\\' is present in path; + * convert to '/' for consistency before percent-encoding + * normalization which will convert '\\' to "%5C" in the URL. + * (Clients still should not be sending '\\' unencoded in requests.) */ + if (flags & HTTP_PARSEOPT_URL_NORMALIZE_PATH_BACKSLASH_TRANS) { + for (char *p = b->ptr; *p != '?' && *p != '\0'; ++p) { + if (*p == '\\') *p = '/'; + } + } + #endif + + qs = (flags & HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED) + ? burl_normalize_basic_required(b, t) + : burl_normalize_basic_unreserved(b, t); + if (-2 == qs) return -2; + + if (flags & HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT) { + if (burl_contains_ctrls(b)) return -2; + } + + if (flags & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE + |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) { + qs = burl_normalize_2F_to_slash(b, qs, flags); + if (-2 == qs) return -2; + } + + if (flags & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE + |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT)) { + qs = burl_normalize_path(b, t, qs, flags); + if (-2 == qs) return -2; + } + + if (flags & HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS) { + if (qs >= 0) burl_normalize_qs20_to_plus(b, qs); + } + + return qs; +} + + +static void burl_append_encode_nde (buffer * const b, const char * const str, const size_t len) +{ + /* percent-encodes everything except unreserved - . 0-9 A-Z _ a-z ~ + * unless already percent-encoded (does not double-encode) */ + /* Note: not checking for invalid UTF-8 */ + char * const p = buffer_string_prepare_append(b, len*3); + unsigned int n1, n2; + int j = 0; + for (unsigned int i = 0; i < len; ++i, ++j) { + if (str[i]=='%' && li_cton(str[i+1], n1) && li_cton(str[i+2], n2)) { + const unsigned int x = (n1 << 4) | n2; + if (burl_is_unreserved((int)x)) { + p[j] = (char)x; + } + else { /* leave UTF-8, control chars, and required chars encoded */ + p[j] = '%'; + p[++j] = str[i+1]; + p[++j] = str[i+2]; + } + i+=2; + } + else if (burl_is_unreserved(str[i])) { + p[j] = str[i]; + } + else { + p[j] = '%'; + p[++j] = hex_chars_uc[(str[i] >> 4) & 0xF]; + p[++j] = hex_chars_uc[str[i] & 0xF]; + } + } + buffer_commit(b, j); +} + + +static void burl_append_encode_psnde (buffer * const b, const char * const str, const size_t len) +{ + /* percent-encodes everything except unreserved - . 0-9 A-Z _ a-z ~ plus / + * unless already percent-encoded (does not double-encode) */ + /* Note: not checking for invalid UTF-8 */ + char * const p = buffer_string_prepare_append(b, len*3); + unsigned int n1, n2; + int j = 0; + for (unsigned int i = 0; i < len; ++i, ++j) { + if (str[i]=='%' && li_cton(str[i+1], n1) && li_cton(str[i+2], n2)) { + const unsigned int x = (n1 << 4) | n2; + if (burl_is_unreserved((int)x)) { + p[j] = (char)x; + } + else { /* leave UTF-8, control chars, and required chars encoded */ + p[j] = '%'; + p[++j] = str[i+1]; + p[++j] = str[i+2]; + } + i+=2; + } + else if (burl_is_unreserved(str[i]) || str[i] == '/') { + p[j] = str[i]; + } + else { + p[j] = '%'; + p[++j] = hex_chars_uc[(str[i] >> 4) & 0xF]; + p[++j] = hex_chars_uc[str[i] & 0xF]; + } + } + buffer_commit(b, j); +} + + +static void burl_append_encode_all (buffer * const b, const char * const str, const size_t len) +{ + /* percent-encodes everything except unreserved - . 0-9 A-Z _ a-z ~ + * Note: double-encodes any existing '%') */ + /* Note: not checking for invalid UTF-8 */ + char * const p = buffer_string_prepare_append(b, len*3); + int j = 0; + for (unsigned int i = 0; i < len; ++i, ++j) { + if (burl_is_unreserved(str[i])) { + p[j] = str[i]; + } + else { + p[j] = '%'; + p[++j] = hex_chars_uc[(str[i] >> 4) & 0xF]; + p[++j] = hex_chars_uc[str[i] & 0xF]; + } + } + buffer_commit(b, j); +} + + +static void burl_offset_tolower (buffer * const b, const size_t off) +{ + /*(skips over all percent-encodings, including encoding of alpha chars)*/ + for (char *p = b->ptr+off; p[0]; ++p) { + if (p[0] >= 'A' && p[0] <= 'Z') p[0] |= 0x20; + else if (p[0]=='%' && light_isxdigit(p[1]) && light_isxdigit(p[2])) + p+=2; + } +} + + +static void burl_offset_toupper (buffer * const b, const size_t off) +{ + /*(skips over all percent-encodings, including encoding of alpha chars)*/ + for (char *p = b->ptr+off; p[0]; ++p) { + if (p[0] >= 'a' && p[0] <= 'z') p[0] &= 0xdf; + else if (p[0]=='%' && light_isxdigit(p[1]) && light_isxdigit(p[2])) + p+=2; + } +} + + +void burl_append (buffer * const b, const char * const str, const size_t len, const int flags) +{ + size_t off = 0; + + if (0 == len) return; + + if (0 == flags) { + buffer_append_string_len(b, str, len); + return; + } + + if (flags & (BURL_TOUPPER|BURL_TOLOWER)) off = buffer_string_length(b); + + if (flags & BURL_ENCODE_NONE) { + buffer_append_string_len(b, str, len); + } + else if (flags & BURL_ENCODE_ALL) { + burl_append_encode_all(b, str, len); + } + else if (flags & BURL_ENCODE_NDE) { + burl_append_encode_nde(b, str, len); + } + else if (flags & BURL_ENCODE_PSNDE) { + burl_append_encode_psnde(b, str, len); + } + else if (flags & BURL_ENCODE_B64U) { + const unsigned char *s = (const unsigned char *)str; + buffer_append_base64_encode_no_padding(b, s, len, BASE64_URL); + } + else if (flags & BURL_DECODE_B64U) { + buffer_append_base64_decode(b, str, len, BASE64_URL); + } + + /* note: not normalizing str, which could come from arbitrary header, + * so it is possible that alpha chars are percent-encoded upper/lowercase */ + if (flags & (BURL_TOLOWER|BURL_TOUPPER)) { + (flags & BURL_TOLOWER) + ? burl_offset_tolower(b, off) /*(flags & BURL_TOLOWER)*/ + : burl_offset_toupper(b, off); /*(flags & BURL_TOUPPER)*/ + } +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/burl.h b/data/lighttpd/lighttpd-1.4.53/src/burl.h new file mode 100644 index 000000000..4eb8f3ea1 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/burl.h @@ -0,0 +1,46 @@ +#ifndef INCLUDED_BURL_H +#define INCLUDED_BURL_H +#include "first.h" + +#include "buffer.h" + +struct burl_parts_t { + buffer *scheme; + buffer *authority; + unsigned short port; + buffer *path; + buffer *query; +}; + +enum burl_opts_e { + HTTP_PARSEOPT_HEADER_STRICT = 0x1 + ,HTTP_PARSEOPT_HOST_STRICT = 0x2 + ,HTTP_PARSEOPT_HOST_NORMALIZE = 0x4 + ,HTTP_PARSEOPT_URL_NORMALIZE = 0x8/*normalize chars %-encoded, uppercase hex*/ + ,HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED =0x10 /* decode unreserved */ + ,HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED =0x20 /* decode (un)reserved*/ + ,HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT =0x40 + ,HTTP_PARSEOPT_URL_NORMALIZE_PATH_BACKSLASH_TRANS=0x80 /* "\\" -> "/" Cygwin */ + ,HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE =0x100/* "%2F"-> "/" */ + ,HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT =0x200 + ,HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE =0x400/* "." ".." "//" */ + ,HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT =0x800 + ,HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS =0x1000 +}; + +int burl_normalize (buffer *b, buffer *t, int flags); + +enum burl_recoding_e { + BURL_TOLOWER = 0x0001 + ,BURL_TOUPPER = 0x0002 + ,BURL_ENCODE_NONE = 0x0004 + ,BURL_ENCODE_ALL = 0x0008 + ,BURL_ENCODE_NDE = 0x0010 /* encode delims, but no-double-encode (NDE) */ + ,BURL_ENCODE_PSNDE = 0x0020 /* similar to NDE, but preserve literal slash */ + ,BURL_ENCODE_B64U = 0x0040 + ,BURL_DECODE_B64U = 0x0080 +}; + +void burl_append (buffer * const b, const char * const str, const size_t len, const int flags); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/chunk.c b/data/lighttpd/lighttpd-1.4.53/src/chunk.c new file mode 100644 index 000000000..b7b692201 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/chunk.c @@ -0,0 +1,830 @@ +#include "first.h" + +/** + * the network chunk-API + * + * + */ + +#include "chunk.h" +#include "fdevent.h" +#include "log.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include "sys-mmap.h" + +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> + +#include <errno.h> +#include <string.h> + +/* default 1MB, upper limit 128MB */ +#define DEFAULT_TEMPFILE_SIZE (1 * 1024 * 1024) +#define MAX_TEMPFILE_SIZE (128 * 1024 * 1024) + +static size_t chunk_buf_sz = 4096; +static chunk *chunks; +static chunk *chunk_buffers; +static array *chunkqueue_default_tempdirs = NULL; +static unsigned int chunkqueue_default_tempfile_size = DEFAULT_TEMPFILE_SIZE; + +void chunkqueue_set_chunk_size (size_t sz) +{ + chunk_buf_sz = sz > 0 ? ((sz + 1023) & ~1023uL) : 4096; +} + +void chunkqueue_set_tempdirs_default_reset (void) +{ + chunkqueue_default_tempdirs = NULL; + chunkqueue_default_tempfile_size = DEFAULT_TEMPFILE_SIZE; +} + +chunkqueue *chunkqueue_init(void) { + chunkqueue *cq; + + cq = calloc(1, sizeof(*cq)); + force_assert(NULL != cq); + + cq->first = NULL; + cq->last = NULL; + + cq->tempdirs = chunkqueue_default_tempdirs; + cq->upload_temp_file_size = chunkqueue_default_tempfile_size; + + return cq; +} + +static chunk *chunk_init(size_t sz) { + chunk *c; + + c = calloc(1, sizeof(*c)); + force_assert(NULL != c); + + c->type = MEM_CHUNK; + c->mem = buffer_init(); + c->file.start = c->file.length = c->file.mmap.offset = 0; + c->file.fd = -1; + c->file.mmap.start = MAP_FAILED; + c->file.mmap.length = 0; + c->file.is_temp = 0; + c->offset = 0; + c->next = NULL; + + buffer_string_prepare_copy(c->mem, sz-1); + + return c; +} + +static void chunk_reset_file_chunk(chunk *c) { + if (c->file.is_temp && !buffer_string_is_empty(c->mem)) { + unlink(c->mem->ptr); + } + if (c->file.fd != -1) { + close(c->file.fd); + c->file.fd = -1; + } + if (MAP_FAILED != c->file.mmap.start) { + munmap(c->file.mmap.start, c->file.mmap.length); + c->file.mmap.start = MAP_FAILED; + } + c->file.start = c->file.length = c->file.mmap.offset = 0; + c->file.mmap.length = 0; + c->file.is_temp = 0; + c->type = MEM_CHUNK; +} + +static void chunk_reset(chunk *c) { + if (c->type == FILE_CHUNK) chunk_reset_file_chunk(c); + + buffer_clear(c->mem); + c->offset = 0; +} + +static void chunk_free(chunk *c) { + if (c->type == FILE_CHUNK) chunk_reset_file_chunk(c); + buffer_free(c->mem); + free(c); +} + +buffer * chunk_buffer_acquire(void) { + chunk *c; + buffer *b; + if (chunks) { + c = chunks; + chunks = c->next; + } + else { + c = chunk_init(chunk_buf_sz); + } + c->next = chunk_buffers; + chunk_buffers = c; + b = c->mem; + c->mem = NULL; + return b; +} + +void chunk_buffer_release(buffer *b) { + if (NULL == b) return; + if (b->size >= chunk_buf_sz && chunk_buffers) { + chunk *c = chunk_buffers; + chunk_buffers = c->next; + c->mem = b; + c->next = chunks; + chunks = c; + buffer_clear(b); + } + else { + buffer_free(b); + } +} + +static chunk * chunk_acquire(void) { + if (chunks) { + chunk *c = chunks; + chunks = c->next; + return c; + } + else { + return chunk_init(chunk_buf_sz); + } +} + +static void chunk_release(chunk *c) { + if (c->mem->size >= chunk_buf_sz) { + chunk_reset(c); + c->next = chunks; + chunks = c; + } + else { + chunk_free(c); + } +} + +void chunkqueue_chunk_pool_clear(void) +{ + for (chunk *next, *c = chunks; c; c = next) { + next = c->next; + chunk_free(c); + } + chunks = NULL; +} + +void chunkqueue_chunk_pool_free(void) +{ + chunkqueue_chunk_pool_clear(); + for (chunk *next, *c = chunk_buffers; c; c = next) { + next = c->next; + c->mem = buffer_init(); /*(chunk_reset() expects c->mem != NULL)*/ + chunk_free(c); + } + chunk_buffers = NULL; +} + +static off_t chunk_remaining_length(const chunk *c) { + off_t len = 0; + switch (c->type) { + case MEM_CHUNK: + len = buffer_string_length(c->mem); + break; + case FILE_CHUNK: + len = c->file.length; + break; + default: + force_assert(c->type == MEM_CHUNK || c->type == FILE_CHUNK); + break; + } + force_assert(c->offset <= len); + return len - c->offset; +} + +void chunkqueue_free(chunkqueue *cq) { + chunk *c, *pc; + + if (NULL == cq) return; + + for (c = cq->first; c; ) { + pc = c; + c = c->next; + chunk_release(pc); + } + + free(cq); +} + +static void chunkqueue_prepend_chunk(chunkqueue *cq, chunk *c) { + c->next = cq->first; + cq->first = c; + + if (NULL == cq->last) { + cq->last = c; + } +} + +static void chunkqueue_append_chunk(chunkqueue *cq, chunk *c) { + c->next = NULL; + if (cq->last) { + cq->last->next = c; + } + cq->last = c; + + if (NULL == cq->first) { + cq->first = c; + } +} + +static chunk * chunkqueue_prepend_mem_chunk(chunkqueue *cq) { + chunk *c = chunk_acquire(); + chunkqueue_prepend_chunk(cq, c); + return c; +} + +static chunk * chunkqueue_append_mem_chunk(chunkqueue *cq) { + chunk *c = chunk_acquire(); + chunkqueue_append_chunk(cq, c); + return c; +} + +static chunk * chunkqueue_append_file_chunk(chunkqueue *cq, buffer *fn, off_t offset, off_t len) { + chunk *c = chunk_acquire(); + chunkqueue_append_chunk(cq, c); + c->type = FILE_CHUNK; + c->file.start = offset; + c->file.length = len; + cq->bytes_in += len; + buffer_copy_buffer(c->mem, fn); + return c; +} + +void chunkqueue_reset(chunkqueue *cq) { + chunk *cur = cq->first; + + cq->first = cq->last = NULL; + + while (NULL != cur) { + chunk *next = cur->next; + chunk_release(cur); + cur = next; + } + + cq->bytes_in = 0; + cq->bytes_out = 0; + cq->tempdir_idx = 0; +} + +void chunkqueue_append_file_fd(chunkqueue *cq, buffer *fn, int fd, off_t offset, off_t len) { + if (len > 0) { + (chunkqueue_append_file_chunk(cq, fn, offset, len))->file.fd = fd; + } + else { + close(fd); + } +} + +void chunkqueue_append_file(chunkqueue *cq, buffer *fn, off_t offset, off_t len) { + if (len > 0) { + chunkqueue_append_file_chunk(cq, fn, offset, len); + } +} + + +static int chunkqueue_append_mem_extend_chunk(chunkqueue *cq, const char *mem, size_t len) { + chunk *c = cq->last; + if (0 == len) return 1; + if (c != NULL && c->type == MEM_CHUNK + && buffer_string_space(c->mem) >= len) { + buffer_append_string_len(c->mem, mem, len); + cq->bytes_in += len; + return 1; + } + return 0; +} + + +void chunkqueue_append_buffer(chunkqueue *cq, buffer *mem) { + chunk *c; + size_t len = buffer_string_length(mem); + if (len < 256 && chunkqueue_append_mem_extend_chunk(cq, mem->ptr, len)) return; + + c = chunkqueue_append_mem_chunk(cq); + cq->bytes_in += len; + buffer_move(c->mem, mem); +} + + +void chunkqueue_append_mem(chunkqueue *cq, const char * mem, size_t len) { + chunk *c; + if (len < chunk_buf_sz && chunkqueue_append_mem_extend_chunk(cq, mem, len)) + return; + + c = chunkqueue_append_mem_chunk(cq); + cq->bytes_in += len; + buffer_copy_string_len(c->mem, mem, len); +} + + +void chunkqueue_append_mem_min(chunkqueue *cq, const char * mem, size_t len) { + chunk *c; + if (len < chunk_buf_sz && chunkqueue_append_mem_extend_chunk(cq, mem, len)) + return; + + c = chunk_init(len+1); + chunkqueue_append_chunk(cq, c); + cq->bytes_in += len; + buffer_copy_string_len(c->mem, mem, len); +} + + +void chunkqueue_append_chunkqueue(chunkqueue *cq, chunkqueue *src) { + if (src == NULL || NULL == src->first) return; + + if (NULL == cq->first) { + cq->first = src->first; + } else { + cq->last->next = src->first; + } + cq->last = src->last; + cq->bytes_in += (src->bytes_in - src->bytes_out); + + src->first = NULL; + src->last = NULL; + src->bytes_out = src->bytes_in; +} + + +__attribute_cold__ +static void chunkqueue_buffer_open_resize(chunk *c, size_t sz) { + chunk * const n = chunk_init((sz + 4095) & ~4095uL); + buffer * const b = c->mem; + c->mem = n->mem; + n->mem = b; + chunk_release(n); +} + + +buffer * chunkqueue_prepend_buffer_open_sz(chunkqueue *cq, size_t sz) { + chunk * const c = chunkqueue_prepend_mem_chunk(cq); + if (buffer_string_space(c->mem) < sz) { + chunkqueue_buffer_open_resize(c, sz); + } + return c->mem; +} + + +buffer * chunkqueue_prepend_buffer_open(chunkqueue *cq) { + chunk *c = chunkqueue_prepend_mem_chunk(cq); + return c->mem; +} + + +void chunkqueue_prepend_buffer_commit(chunkqueue *cq) { + cq->bytes_in += buffer_string_length(cq->first->mem); +} + + +buffer * chunkqueue_append_buffer_open_sz(chunkqueue *cq, size_t sz) { + chunk * const c = chunkqueue_append_mem_chunk(cq); + if (buffer_string_space(c->mem) < sz) { + chunkqueue_buffer_open_resize(c, sz); + } + return c->mem; +} + + +buffer * chunkqueue_append_buffer_open(chunkqueue *cq) { + chunk *c = chunkqueue_append_mem_chunk(cq); + return c->mem; +} + + +void chunkqueue_append_buffer_commit(chunkqueue *cq) { + cq->bytes_in += buffer_string_length(cq->last->mem); +} + + +char * chunkqueue_get_memory(chunkqueue *cq, size_t *len) { + size_t sz = *len ? *len : (chunk_buf_sz >> 1); + buffer *b; + chunk *c = cq->last; + if (NULL != c && MEM_CHUNK == c->type) { + /* return pointer into existing buffer if large enough */ + size_t avail = buffer_string_space(c->mem); + if (avail >= sz) { + *len = avail; + b = c->mem; + return b->ptr + buffer_string_length(b); + } + } + + /* allocate new chunk */ + b = chunkqueue_append_buffer_open_sz(cq, sz); + *len = buffer_string_space(b); + return b->ptr; +} + +void chunkqueue_use_memory(chunkqueue *cq, size_t len) { + buffer *b; + + force_assert(NULL != cq); + force_assert(NULL != cq->last && MEM_CHUNK == cq->last->type); + b = cq->last->mem; + + if (len > 0) { + buffer_commit(b, len); + cq->bytes_in += len; + } else if (buffer_string_is_empty(b)) { + /* unused buffer: can't remove chunk easily from + * end of list, so just reset the buffer + */ + buffer_clear(b); + } +} + +void chunkqueue_set_tempdirs_default (array *tempdirs, unsigned int upload_temp_file_size) { + chunkqueue_default_tempdirs = tempdirs; + chunkqueue_default_tempfile_size + = (0 == upload_temp_file_size) ? DEFAULT_TEMPFILE_SIZE + : (upload_temp_file_size > MAX_TEMPFILE_SIZE) ? MAX_TEMPFILE_SIZE + : upload_temp_file_size; +} + +#if 0 +void chunkqueue_set_tempdirs(chunkqueue *cq, array *tempdirs, unsigned int upload_temp_file_size) { + force_assert(NULL != cq); + cq->tempdirs = tempdirs; + cq->upload_temp_file_size + = (0 == upload_temp_file_size) ? DEFAULT_TEMPFILE_SIZE + : (upload_temp_file_size > MAX_TEMPFILE_SIZE) ? MAX_TEMPFILE_SIZE + : upload_temp_file_size; + cq->tempdir_idx = 0; +} +#endif + +void chunkqueue_steal(chunkqueue *dest, chunkqueue *src, off_t len) { + while (len > 0) { + chunk *c = src->first; + off_t clen = 0, use; + + if (NULL == c) break; + + clen = chunk_remaining_length(c); + if (0 == clen) { + /* drop empty chunk */ + src->first = c->next; + if (c == src->last) src->last = NULL; + chunk_release(c); + continue; + } + + use = len >= clen ? clen : len; + len -= use; + + if (use == clen) { + /* move complete chunk */ + src->first = c->next; + if (c == src->last) src->last = NULL; + + chunkqueue_append_chunk(dest, c); + dest->bytes_in += use; + } else { + /* partial chunk with length "use" */ + + switch (c->type) { + case MEM_CHUNK: + chunkqueue_append_mem(dest, c->mem->ptr + c->offset, use); + break; + case FILE_CHUNK: + /* tempfile flag is in "last" chunk after the split */ + chunkqueue_append_file(dest, c->mem, c->file.start + c->offset, use); + break; + } + + c->offset += use; + force_assert(0 == len); + } + + src->bytes_out += use; + } +} + +static chunk *chunkqueue_get_append_tempfile(server *srv, chunkqueue *cq) { + chunk *c; + buffer *template = buffer_init_string("/var/tmp/lighttpd-upload-XXXXXX"); + int fd = -1; + + if (cq->tempdirs && cq->tempdirs->used) { + /* we have several tempdirs, only if all of them fail we jump out */ + + for (errno = EIO; cq->tempdir_idx < cq->tempdirs->used; ++cq->tempdir_idx) { + data_string *ds = (data_string *)cq->tempdirs->data[cq->tempdir_idx]; + + buffer_copy_buffer(template, ds->value); + buffer_append_path_len(template, CONST_STR_LEN("lighttpd-upload-XXXXXX")); + + #ifdef __COVERITY__ + /* POSIX-2008 requires mkstemp create file with 0600 perms */ + umask(0600); + #endif + /* coverity[secure_temp : FALSE] */ + if (-1 != (fd = mkstemp(template->ptr))) break; + } + } else { + #ifdef __COVERITY__ + /* POSIX-2008 requires mkstemp create file with 0600 perms */ + umask(0600); + #endif + /* coverity[secure_temp : FALSE] */ + fd = mkstemp(template->ptr); + } + + if (fd < 0) { + /* (report only the last error to mkstemp() + * if multiple temp dirs attempted) */ + log_error_write(srv, __FILE__, __LINE__, "sbs", + "opening temp-file failed:", + template, strerror(errno)); + buffer_free(template); + return NULL; + } + + if (0 != fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_APPEND)) { + /* (should not happen; fd is regular file) */ + log_error_write(srv, __FILE__, __LINE__, "sbs", + "fcntl():", template, strerror(errno)); + close(fd); + buffer_free(template); + return NULL; + } + fdevent_setfd_cloexec(fd); + + c = chunkqueue_append_file_chunk(cq, template, 0, 0); + c->file.fd = fd; + c->file.is_temp = 1; + + buffer_free(template); + + return c; +} + +static void chunkqueue_remove_empty_chunks(chunkqueue *cq); + +int chunkqueue_append_mem_to_tempfile(server *srv, chunkqueue *dest, const char *mem, size_t len) { + chunk *dst_c; + ssize_t written; + + do { + /* + * if the last chunk is + * - smaller than dest->upload_temp_file_size + * - not read yet (offset == 0) + * -> append to it (so it might actually become larger than dest->upload_temp_file_size) + * otherwise + * -> create a new chunk + * + * */ + + dst_c = dest->last; + if (NULL != dst_c + && FILE_CHUNK == dst_c->type + && dst_c->file.is_temp + && dst_c->file.fd >= 0 + && 0 == dst_c->offset) { + /* ok, take the last chunk for our job */ + + if (dst_c->file.length >= (off_t)dest->upload_temp_file_size) { + /* the chunk is too large now, close it */ + int rc = close(dst_c->file.fd); + dst_c->file.fd = -1; + if (0 != rc) { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "close() temp-file", dst_c->mem, "failed:", + strerror(errno)); + return -1; + } + dst_c = NULL; + } + } else { + dst_c = NULL; + } + + if (NULL == dst_c && NULL == (dst_c = chunkqueue_get_append_tempfile(srv, dest))) { + return -1; + } + #ifdef __COVERITY__ + if (dst_c->file.fd < 0) return -1; + #endif + + /* (dst_c->file.fd >= 0) */ + /* coverity[negative_returns : FALSE] */ + written = write(dst_c->file.fd, mem, len); + + if ((size_t) written == len) { + dst_c->file.length += len; + dest->bytes_in += len; + + return 0; + } else if (written >= 0) { + /*(assume EINTR if partial write and retry write(); + * retry write() might fail with ENOSPC if no more space on volume)*/ + dest->bytes_in += written; + mem += written; + len -= (size_t)written; + dst_c->file.length += (size_t)written; + /* continue; retry */ + } else if (errno == EINTR) { + /* continue; retry */ + } else { + int retry = (errno == ENOSPC && dest->tempdirs && ++dest->tempdir_idx < dest->tempdirs->used); + if (!retry) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "write() temp-file", dst_c->mem, "failed:", + strerror(errno)); + } + + if (0 == chunk_remaining_length(dst_c)) { + /*(remove empty chunk and unlink tempfile)*/ + chunkqueue_remove_empty_chunks(dest); + } else {/*(close tempfile; avoid later attempts to append)*/ + int rc = close(dst_c->file.fd); + dst_c->file.fd = -1; + if (0 != rc) { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "close() temp-file", dst_c->mem, "failed:", + strerror(errno)); + return -1; + } + } + if (!retry) break; /* return -1; */ + + /* continue; retry */ + } + + } while (dst_c); + + return -1; +} + +int chunkqueue_steal_with_tempfiles(server *srv, chunkqueue *dest, chunkqueue *src, off_t len) { + while (len > 0) { + chunk *c = src->first; + off_t clen = 0, use; + + if (NULL == c) break; + + clen = chunk_remaining_length(c); + if (0 == clen) { + /* drop empty chunk */ + src->first = c->next; + if (c == src->last) src->last = NULL; + chunk_release(c); + continue; + } + + use = (len >= clen) ? clen : len; + len -= use; + + switch (c->type) { + case FILE_CHUNK: + if (use == clen) { + /* move complete chunk */ + src->first = c->next; + if (c == src->last) src->last = NULL; + chunkqueue_append_chunk(dest, c); + dest->bytes_in += use; + } else { + /* partial chunk with length "use" */ + /* tempfile flag is in "last" chunk after the split */ + chunkqueue_append_file(dest, c->mem, c->file.start + c->offset, use); + + c->offset += use; + force_assert(0 == len); + } + break; + + case MEM_CHUNK: + /* store "use" bytes from memory chunk in tempfile */ + if (0 != chunkqueue_append_mem_to_tempfile(srv, dest, c->mem->ptr + c->offset, use)) { + return -1; + } + + if (use == clen) { + /* finished chunk */ + src->first = c->next; + if (c == src->last) src->last = NULL; + chunk_release(c); + } else { + /* partial chunk */ + c->offset += use; + force_assert(0 == len); + } + break; + } + + src->bytes_out += use; + } + + return 0; +} + +off_t chunkqueue_length(chunkqueue *cq) { + off_t len = 0; + chunk *c; + + for (c = cq->first; c; c = c->next) { + len += chunk_remaining_length(c); + } + + return len; +} + +void chunkqueue_mark_written(chunkqueue *cq, off_t len) { + off_t written = len; + chunk *c; + force_assert(len >= 0); + + for (c = cq->first; NULL != c; c = cq->first) { + off_t c_len = chunk_remaining_length(c); + + if (0 == written && 0 != c_len) break; /* no more finished chunks */ + + if (written >= c_len) { /* chunk got finished */ + c->offset += c_len; + written -= c_len; + + cq->first = c->next; + if (c == cq->last) cq->last = NULL; + chunk_release(c); + } else { /* partial chunk */ + c->offset += written; + written = 0; + break; /* chunk not finished */ + } + } + + force_assert(0 == written); + cq->bytes_out += len; +} + +void chunkqueue_remove_finished_chunks(chunkqueue *cq) { + chunk *c; + + for (c = cq->first; c; c = cq->first) { + if (0 != chunk_remaining_length(c)) break; /* not finished yet */ + + cq->first = c->next; + if (c == cq->last) cq->last = NULL; + chunk_release(c); + } +} + +static void chunkqueue_remove_empty_chunks(chunkqueue *cq) { + chunk *c; + chunkqueue_remove_finished_chunks(cq); + if (chunkqueue_is_empty(cq)) return; + + for (c = cq->first; c && c->next; c = c->next) { + if (0 == chunk_remaining_length(c->next)) { + chunk *empty = c->next; + c->next = empty->next; + if (empty == cq->last) cq->last = c; + chunk_release(empty); + } + } +} + +int chunkqueue_open_file_chunk(server *srv, chunkqueue *cq) { + chunk* const c = cq->first; + off_t offset, toSend; + struct stat st; + + force_assert(NULL != c); + force_assert(FILE_CHUNK == c->type); + force_assert(c->offset >= 0 && c->offset <= c->file.length); + + offset = c->file.start + c->offset; + toSend = c->file.length - c->offset; + + if (-1 == c->file.fd) { + if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, O_RDONLY, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "open failed:", strerror(errno), c->mem); + return -1; + } + } + + /*(skip file size checks if file is temp file created by lighttpd)*/ + if (c->file.is_temp) return 0; + + if (-1 == fstat(c->file.fd, &st)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "fstat failed:", strerror(errno)); + return -1; + } + + if (offset > st.st_size || toSend > st.st_size || offset > st.st_size - toSend) { + log_error_write(srv, __FILE__, __LINE__, "sb", "file shrunk:", c->mem); + return -1; + } + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/chunk.h b/data/lighttpd/lighttpd-1.4.53/src/chunk.h new file mode 100644 index 000000000..e31177ffc --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/chunk.h @@ -0,0 +1,111 @@ +#ifndef _CHUNK_H_ +#define _CHUNK_H_ +#include "first.h" + +#ifdef _AIX /*(AIX might #define mmap mmap64)*/ +#include "sys-mmap.h" +#endif + +#include "buffer.h" +#include "array.h" + +typedef struct chunk { + struct chunk *next; + enum { MEM_CHUNK, FILE_CHUNK } type; + + buffer *mem; /* either the storage of the mem-chunk or the name of the file */ + + /* the size of the chunk is either: + * - mem-chunk: buffer_string_length(chunk::mem) + * - file-chunk: chunk::file.length + */ + off_t offset; /* octets sent from this chunk */ + + struct { + /* filechunk */ + off_t start; /* starting offset in the file */ + off_t length; /* octets to send from the starting offset */ + + int fd; + int is_temp; /* file is temporary and will be deleted if on cleanup */ + struct { + char *start; /* the start pointer of the mmap'ed area */ + size_t length; /* size of the mmap'ed area */ + off_t offset; /* start is <n> octet away from the start of the file */ + } mmap; + } file; +} chunk; + +typedef struct { + chunk *first; + chunk *last; + + off_t bytes_in, bytes_out; + + array *tempdirs; + unsigned int upload_temp_file_size; + unsigned int tempdir_idx; +} chunkqueue; + +buffer * chunk_buffer_acquire(void); +void chunk_buffer_release(buffer *b); + +void chunkqueue_chunk_pool_clear(void); +void chunkqueue_chunk_pool_free(void); + +chunkqueue *chunkqueue_init(void); +void chunkqueue_set_chunk_size (size_t sz); +void chunkqueue_set_tempdirs_default_reset (void); +void chunkqueue_set_tempdirs_default (array *tempdirs, unsigned int upload_temp_file_size); +void chunkqueue_append_file(chunkqueue *cq, buffer *fn, off_t offset, off_t len); /* copies "fn" */ +void chunkqueue_append_file_fd(chunkqueue *cq, buffer *fn, int fd, off_t offset, off_t len); /* copies "fn" */ +void chunkqueue_append_mem(chunkqueue *cq, const char *mem, size_t len); /* copies memory */ +void chunkqueue_append_mem_min(chunkqueue *cq, const char * mem, size_t len); /* copies memory */ +void chunkqueue_append_buffer(chunkqueue *cq, buffer *mem); /* may reset "mem" */ +void chunkqueue_append_chunkqueue(chunkqueue *cq, chunkqueue *src); + +buffer * chunkqueue_prepend_buffer_open_sz(chunkqueue *cq, size_t sz); +buffer * chunkqueue_prepend_buffer_open(chunkqueue *cq); +void chunkqueue_prepend_buffer_commit(chunkqueue *cq); +buffer * chunkqueue_append_buffer_open_sz(chunkqueue *cq, size_t sz); +buffer * chunkqueue_append_buffer_open(chunkqueue *cq); +void chunkqueue_append_buffer_commit(chunkqueue *cq); + +struct server; /*(declaration)*/ +int chunkqueue_append_mem_to_tempfile(struct server *srv, chunkqueue *cq, const char *mem, size_t len); + +/* functions to handle buffers to read into: */ +/* obtain/reserve memory in chunkqueue at least len (input) size, + * return pointer to memory with len (output) available for use + * modifying the chunkqueue invalidates the memory area. + * should always be followed by chunkqueue_get_memory(), + * even if nothing was read. + * pass 0 in len for mem at least half of chunk_buf_sz + */ +char * chunkqueue_get_memory(chunkqueue *cq, size_t *len); +/* commit len bytes of mem obtained from chunkqueue_get_memory() */ +void chunkqueue_use_memory(chunkqueue *cq, size_t len); + +/* mark first "len" bytes as written (incrementing chunk offsets) + * and remove finished chunks + */ +void chunkqueue_mark_written(chunkqueue *cq, off_t len); + +void chunkqueue_remove_finished_chunks(chunkqueue *cq); + +void chunkqueue_steal(chunkqueue *dest, chunkqueue *src, off_t len); +struct server; +int chunkqueue_steal_with_tempfiles(struct server *srv, chunkqueue *dest, chunkqueue *src, off_t len); + +int chunkqueue_open_file_chunk(struct server *srv, chunkqueue *cq); + +off_t chunkqueue_length(chunkqueue *cq); +void chunkqueue_free(chunkqueue *cq); +void chunkqueue_reset(chunkqueue *cq); + +static inline int chunkqueue_is_empty(const chunkqueue *cq); +static inline int chunkqueue_is_empty(const chunkqueue *cq) { + return NULL == cq->first; +} + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/config.h.cmake b/data/lighttpd/lighttpd-1.4.53/src/config.h.cmake new file mode 100644 index 000000000..4afc01035 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/config.h.cmake @@ -0,0 +1,169 @@ +/* + CMake autogenerated config.h file. Do not edit! +*/ + +/* Package details */ +#define LIGHTTPD_VERSION_ID ${LIGHTTPD_VERSION_ID} +#define PACKAGE_NAME "${PACKAGE_NAME}" +#define PACKAGE_VERSION "${PACKAGE_VERSION}" +#define LIBRARY_DIR "${LIGHTTPD_LIBRARY_DIR}" + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +/* System */ +#cmakedefine HAVE_SYS_DEVPOLL_H +#cmakedefine HAVE_SYS_EPOLL_H +#cmakedefine HAVE_SYS_EVENT_H +#cmakedefine HAVE_SYS_MMAN_H +#cmakedefine HAVE_SYS_POLL_H +#cmakedefine HAVE_SYS_PORT_H +#cmakedefine HAVE_SYS_PRCTL_H +#cmakedefine HAVE_SYS_RESOURCE_H +#cmakedefine HAVE_SYS_SENDFILE_H +#cmakedefine HAVE_SYS_SELECT_H +#cmakedefine HAVE_SYS_TYPES_H +#cmakedefine HAVE_SYS_UIO_H +#cmakedefine HAVE_SYS_UN_H +#cmakedefine HAVE_SYS_WAIT_H +#cmakedefine HAVE_SYS_TIME_H +#cmakedefine HAVE_UNISTD_H +#cmakedefine HAVE_PTHREAD_H +#cmakedefine HAVE_IPV6 +#cmakedefine HAVE_WEAK_SYMBOLS + +/* XATTR */ +#cmakedefine HAVE_ATTR_ATTRIBUTES_H +#cmakedefine HAVE_XATTR + +/* mySQL */ +#cmakedefine HAVE_MYSQL_H +#cmakedefine HAVE_MYSQL + +/* OpenSSL */ +#cmakedefine HAVE_OPENSSL_SSL_H +#cmakedefine HAVE_LIBCRYPTO +#cmakedefine HAVE_LIBSSL + +/* BZip */ +#cmakedefine HAVE_BZLIB_H +#cmakedefine HAVE_LIBBZ2 + +/* FAM */ +#cmakedefine HAVE_FAM_H +#cmakedefine HAVE_FAMNOEXISTS + +/* getopt */ +#cmakedefine HAVE_GETOPT_H + +#cmakedefine HAVE_INTTYPES_H + +/* LDAP */ +#cmakedefine HAVE_LDAP_H +#cmakedefine HAVE_LIBLDAP +#cmakedefine HAVE_LBER_H +#cmakedefine HAVE_LIBLBER + +/* XML */ +#cmakedefine HAVE_LIBXML_H +#cmakedefine HAVE_LIBXML + +/* PCRE */ +#cmakedefine HAVE_PCRE_H +#cmakedefine HAVE_LIBPCRE + +#cmakedefine HAVE_POLL_H +#cmakedefine HAVE_PWD_H + +/* sqlite3 */ +#cmakedefine HAVE_SQLITE3_H +#cmakedefine HAVE_LIBPCRE + +#cmakedefine HAVE_STDDEF_H +#cmakedefine HAVE_STDINT_H +#cmakedefine HAVE_SYSLOG_H + +/* UUID */ +#cmakedefine HAVE_UUID_UUID_H +#cmakedefine HAVE_LIBUUID + +/* ZLIB */ +#cmakedefine HAVE_ZLIB_H +#cmakedefine HAVE_LIBZ + +/* lua */ +#cmakedefine HAVE_LUA_H +#cmakedefine HAVE_LIBLUA + +/* gdbm */ +#cmakedefine HAVE_GDBM_H +#cmakedefine HAVE_GDBM + +/* memcache */ +#cmakedefine USE_MEMCACHED + +/* inotify */ +#cmakedefine HAVE_INOTIFY_INIT +#cmakedefine HAVE_SYS_INOTIFY_H + +/* Types */ +#cmakedefine HAVE_SOCKLEN_T +#cmakedefine SIZEOF_LONG ${SIZEOF_LONG} +#cmakedefine SIZEOF_OFF_T ${SIZEOF_OFF_T} + +/* Functions */ +#cmakedefine HAVE_CHROOT +#cmakedefine HAVE_EPOLL_CTL +#cmakedefine HAVE_FORK +#cmakedefine HAVE_GETRLIMIT +#cmakedefine HAVE_GETUID +#cmakedefine HAVE_GMTIME_R +#cmakedefine HAVE_INET_NTOP +#cmakedefine HAVE_KQUEUE +#cmakedefine HAVE_LOCALTIME_R +#cmakedefine HAVE_LSTAT +#cmakedefine HAVE_MADVISE +#cmakedefine HAVE_MEMCPY +#cmakedefine HAVE_MEMSET +#cmakedefine HAVE_MMAP +#cmakedefine HAVE_PATHCONF +#cmakedefine HAVE_POLL +#cmakedefine HAVE_PORT_CREATE +#cmakedefine HAVE_PRCTL +#cmakedefine HAVE_PREAD +#cmakedefine HAVE_POSIX_FADVISE +#cmakedefine HAVE_SELECT +#cmakedefine HAVE_SENDFILE +#cmakedefine HAVE_SEND_FILE +#cmakedefine HAVE_SENDFILE64 +#cmakedefine HAVE_SENDFILEV +#cmakedefine HAVE_SIGACTION +#cmakedefine HAVE_SIGNAL +#cmakedefine HAVE_SIGTIMEDWAIT +#cmakedefine HAVE_STRPTIME +#cmakedefine HAVE_SYSLOG +#cmakedefine HAVE_WRITEV +#cmakedefine HAVE_INET_ATON +#cmakedefine HAVE_ISSETUGID +#cmakedefine HAVE_INET_PTON +#cmakedefine HAVE_MEMSET_S +#cmakedefine HAVE_EXPLICIT_BZERO + +/* libcrypt */ +#cmakedefine HAVE_CRYPT_H +#cmakedefine HAVE_LIBCRYPT +#cmakedefine HAVE_CRYPT +#cmakedefine HAVE_CRYPT_R + +/* fastcgi */ +#cmakedefine HAVE_FASTCGI_H +#cmakedefine HAVE_FASTCGI_FASTCGI_H + +/* libev */ +#cmakedefine HAVE_LIBEV + +/* libunwind */ +#cmakedefine HAVE_LIBUNWIND + +#cmakedefine LIGHTTPD_STATIC diff --git a/data/lighttpd/lighttpd-1.4.53/src/configfile-glue.c b/data/lighttpd/lighttpd-1.4.53/src/configfile-glue.c new file mode 100644 index 000000000..94c8909ab --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/configfile-glue.c @@ -0,0 +1,584 @@ +#include "first.h" + +#include "base.h" +#include "buffer.h" +#include "array.h" +#include "log.h" +#include "fdevent.h" +#include "http_header.h" +#include "sock_addr.h" + +#include "configfile.h" + +#include <string.h> +#include <stdlib.h> + +/** + * like all glue code this file contains functions which + * are the external interface of lighttpd. The functions + * are used by the server itself and the plugins. + * + * The main-goal is to have a small library in the end + * which is linked against both and which will define + * the interface itself in the end. + * + */ + + +/* handle global options */ + +/* parse config array */ +int config_insert_values_internal(server *srv, array *ca, const config_values_t cv[], config_scope_type_t scope) { + size_t i; + data_unset *du; + + for (i = 0; cv[i].key; i++) { + + if (NULL == (du = array_get_element_klen(ca, cv[i].key, strlen(cv[i].key)))) { + /* no found */ + + continue; + } + + if ((T_CONFIG_SCOPE_SERVER == cv[i].scope) + && (T_CONFIG_SCOPE_SERVER != scope)) { + /* server scope options should only be set in server scope, not in conditionals */ + log_error_write(srv, __FILE__, __LINE__, "ss", + "DEPRECATED: don't set server options in conditionals, variable:", + cv[i].key); + } + + switch (cv[i].type) { + case T_CONFIG_ARRAY: + if (du->type == TYPE_ARRAY) { + size_t j; + data_array *da = (data_array *)du; + + for (j = 0; j < da->value->used; j++) { + data_unset *ds = da->value->data[j]; + if (ds->type == TYPE_STRING || ds->type == TYPE_INTEGER || ds->type == TYPE_ARRAY) { + array_insert_unique(cv[i].destination, ds->fn->copy(ds)); + } else { + log_error_write(srv, __FILE__, __LINE__, "sssbsd", + "the value of an array can only be a string, variable:", + cv[i].key, "[", ds->key, "], type:", ds->type); + + return -1; + } + } + } else { + log_error_write(srv, __FILE__, __LINE__, "ss", cv[i].key, "should have been a array of strings like ... = ( \"...\" )"); + + return -1; + } + break; + case T_CONFIG_STRING: + if (du->type == TYPE_STRING) { + data_string *ds = (data_string *)du; + + buffer_copy_buffer(cv[i].destination, ds->value); + } else { + log_error_write(srv, __FILE__, __LINE__, "ss", cv[i].key, "should have been a string like ... = \"...\""); + + return -1; + } + break; + case T_CONFIG_SHORT: + switch(du->type) { + case TYPE_INTEGER: { + data_integer *di = (data_integer *)du; + + *((unsigned short *)(cv[i].destination)) = di->value; + break; + } + case TYPE_STRING: { + data_string *ds = (data_string *)du; + + /* If the value came from an environment variable, then it is a + * data_string, although it may contain a number in ASCII + * decimal format. We try to interpret the string as a decimal + * short before giving up, in order to support setting numeric + * values with environment variables (eg, port number). + */ + if (ds->value->ptr && *ds->value->ptr) { + char *e; + long l = strtol(ds->value->ptr, &e, 10); + if (e != ds->value->ptr && !*e && l >=0 && l <= 65535) { + *((unsigned short *)(cv[i].destination)) = l; + break; + } + } + + log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected a short:", cv[i].key, ds->value); + + return -1; + } + default: + log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected a short integer, range 0 ... 65535"); + return -1; + } + break; + case T_CONFIG_INT: + switch(du->type) { + case TYPE_INTEGER: { + data_integer *di = (data_integer *)du; + + *((unsigned int *)(cv[i].destination)) = di->value; + break; + } + case TYPE_STRING: { + data_string *ds = (data_string *)du; + + if (ds->value->ptr && *ds->value->ptr) { + char *e; + long l = strtol(ds->value->ptr, &e, 10); + if (e != ds->value->ptr && !*e && l >= 0) { + *((unsigned int *)(cv[i].destination)) = l; + break; + } + } + + log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected an integer:", cv[i].key, ds->value); + + return -1; + } + default: + log_error_write(srv, __FILE__, __LINE__, "ssds", "unexpected type for key:", cv[i].key, du->type, "expected an integer, range 0 ... 4294967295"); + return -1; + } + break; + case T_CONFIG_BOOLEAN: + if (du->type == TYPE_STRING) { + data_string *ds = (data_string *)du; + + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("enable"))) { + *((unsigned short *)(cv[i].destination)) = 1; + } else if (buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))) { + *((unsigned short *)(cv[i].destination)) = 0; + } else { + log_error_write(srv, __FILE__, __LINE__, "ssbs", "ERROR: unexpected value for key:", cv[i].key, ds->value, "(enable|disable)"); + + return -1; + } + } else { + log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: unexpected type for key:", cv[i].key, "(string)", "\"(enable|disable)\""); + + return -1; + } + break; + case T_CONFIG_LOCAL: + case T_CONFIG_UNSET: + break; + case T_CONFIG_UNSUPPORTED: + log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found unsupported key:", cv[i].key, "-", (char *)(cv[i].destination)); + + srv->config_unsupported = 1; + + break; + case T_CONFIG_DEPRECATED: + log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found deprecated key:", cv[i].key, "-", (char *)(cv[i].destination)); + + srv->config_deprecated = 1; + + break; + } + } + + return 0; +} + +int config_insert_values_global(server *srv, array *ca, const config_values_t cv[], config_scope_type_t scope) { + size_t i; + data_unset *du; + + for (i = 0; cv[i].key; i++) { + if (NULL == (du = array_get_element_klen(ca, cv[i].key, strlen(cv[i].key)))) { + /* no found */ + + continue; + } + array_set_key_value(srv->config_touched, CONST_BUF_LEN(du->key), CONST_STR_LEN("")); + } + + return config_insert_values_internal(srv, ca, cv, scope); +} + +static const char* cond_result_to_string(cond_result_t cond_result) { + switch (cond_result) { + case COND_RESULT_UNSET: return "unset"; + case COND_RESULT_SKIP: return "skipped"; + case COND_RESULT_FALSE: return "false"; + case COND_RESULT_TRUE: return "true"; + default: return "invalid cond_result_t"; + } +} + +static int config_addrstr_eq_remote_ip_mask(server *srv, const char *addrstr, int nm_bits, sock_addr *rmt) { + /* special-case 0 == nm_bits to mean "all bits of the address" in addrstr */ + sock_addr addr; + if (1 == sock_addr_inet_pton(&addr, addrstr, AF_INET, 0)) { + if (nm_bits > 32) { + log_error_write(srv, __FILE__, __LINE__, "sd", "ERROR: ipv4 netmask too large:", nm_bits); + return -1; + } + } else if (1 == sock_addr_inet_pton(&addr, addrstr, AF_INET6, 0)) { + if (nm_bits > 128) { + log_error_write(srv, __FILE__, __LINE__, "sd", "ERROR: ipv6 netmask too large:", nm_bits); + return -1; + } + } else { + log_error_write(srv, __FILE__, __LINE__, "ss", "ERROR: ip addr is invalid:", addrstr); + return -1; + } + return sock_addr_is_addr_eq_bits(&addr, rmt, nm_bits); +} + +static int config_addrbuf_eq_remote_ip_mask(server *srv, buffer *string, char *nm_slash, sock_addr *rmt) { + char *err; + int nm_bits = strtol(nm_slash + 1, &err, 10); + size_t addrstrlen = (size_t)(nm_slash - string->ptr); + char addrstr[64]; /*(larger than INET_ADDRSTRLEN and INET6_ADDRSTRLEN)*/ + + if (*err) { + log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", string, err); + return -1; + } + + if (nm_bits <= 0) { + if (*(nm_slash+1) == '\0') { + log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", string); + } else { + log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: invalid netmask <= 0:", string, err); + } + return -1; + } + + if (addrstrlen >= sizeof(addrstr)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: address string too long:", string); + return -1; + } + + memcpy(addrstr, string->ptr, addrstrlen); + addrstr[addrstrlen] = '\0'; + + return config_addrstr_eq_remote_ip_mask(srv, addrstr, nm_bits, rmt); +} + +static cond_result_t config_check_cond_cached(server *srv, connection *con, data_config *dc); + +static cond_result_t config_check_cond_nocache(server *srv, connection *con, data_config *dc) { + buffer *l; + server_socket *srv_sock = con->srv_socket; + cond_cache_t *cache = &con->cond_cache[dc->context_ndx]; + + /* check parent first */ + if (dc->parent && dc->parent->context_ndx) { + /** + * a nested conditional + * + * if the parent is not decided yet or false, we can't be true either + */ + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "sb", "go parent", dc->parent->key); + } + + switch (config_check_cond_cached(srv, con, dc->parent)) { + case COND_RESULT_UNSET: + /* decide later */ + return COND_RESULT_UNSET; + case COND_RESULT_SKIP: + case COND_RESULT_FALSE: + /* failed precondition */ + return COND_RESULT_SKIP; + case COND_RESULT_TRUE: + /* proceed */ + break; + } + } + + if (dc->prev) { + /** + * a else branch; can only be executed if the previous branch + * was evaluated as "false" (not unset/skipped/true) + */ + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "sb", "go prev", dc->prev->key); + } + + /* make sure prev is checked first */ + switch (config_check_cond_cached(srv, con, dc->prev)) { + case COND_RESULT_UNSET: + /* decide later */ + return COND_RESULT_UNSET; + case COND_RESULT_SKIP: + case COND_RESULT_TRUE: + /* failed precondition */ + return COND_RESULT_SKIP; + case COND_RESULT_FALSE: + /* proceed */ + break; + } + } + + if (!con->conditional_is_valid[dc->comp]) { + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "dss", + dc->comp, + dc->key->ptr, + "not available yet"); + } + + return COND_RESULT_UNSET; + } + + /* if we had a real result before and weren't cleared just return it */ + switch (cache->local_result) { + case COND_RESULT_TRUE: + case COND_RESULT_FALSE: + return cache->local_result; + default: + break; + } + + if (CONFIG_COND_ELSE == dc->cond) return COND_RESULT_TRUE; + + /* pass the rules */ + + switch (dc->comp) { + case COMP_HTTP_HOST: { + char *ck_colon = NULL, *val_colon = NULL; + unsigned short port; + + if (!buffer_string_is_empty(con->uri.authority)) { + + /* + * append server-port to the HTTP_POST if necessary + */ + + l = con->uri.authority; + + switch(dc->cond) { + case CONFIG_COND_NE: + case CONFIG_COND_EQ: + port = sock_addr_get_port(&srv_sock->addr); + if (0 == port) break; + ck_colon = strchr(dc->string->ptr, ':'); + val_colon = strchr(l->ptr, ':'); + + if (NULL != ck_colon && NULL == val_colon) { + /* condition "host:port" but client send "host" */ + buffer_copy_buffer(srv->cond_check_buf, l); + buffer_append_string_len(srv->cond_check_buf, CONST_STR_LEN(":")); + buffer_append_int(srv->cond_check_buf, port); + l = srv->cond_check_buf; + } else if (NULL != val_colon && NULL == ck_colon) { + /* condition "host" but client send "host:port" */ + buffer_copy_string_len(srv->cond_check_buf, l->ptr, val_colon - l->ptr); + l = srv->cond_check_buf; + } + break; + default: + break; + } + } else { + l = srv->empty_string; + } + break; + } + case COMP_HTTP_REMOTE_IP: { + char *nm_slash; + /* handle remoteip limitations + * + * "10.0.0.1" is provided for all comparisions + * + * only for == and != we support + * + * "10.0.0.1/24" + */ + + if ((dc->cond == CONFIG_COND_EQ || + dc->cond == CONFIG_COND_NE) && + (NULL != (nm_slash = strchr(dc->string->ptr, '/')))) { + switch (config_addrbuf_eq_remote_ip_mask(srv, dc->string, nm_slash, &con->dst_addr)) { + case 1: return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE; + case 0: return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE; + case -1: return COND_RESULT_FALSE; /*(error parsing configfile entry)*/ + } + } + l = con->dst_addr_buf; + break; + } + case COMP_HTTP_SCHEME: + l = con->uri.scheme; + break; + + case COMP_HTTP_URL: + l = con->uri.path; + break; + + case COMP_HTTP_QUERY_STRING: + l = con->uri.query; + break; + + case COMP_SERVER_SOCKET: + l = srv_sock->srv_token; + break; + + case COMP_HTTP_REQUEST_HEADER: + l = http_header_request_get(con, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(dc->comp_tag)); + if (NULL == l) l = srv->empty_string; + break; + case COMP_HTTP_REQUEST_METHOD: + l = srv->tmp_buf; + buffer_clear(l); + http_method_append(l, con->request.http_method); + break; + default: + return COND_RESULT_FALSE; + } + + if (NULL == l) { + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "bsbs", dc->comp_key, + "(", l, ") compare to NULL"); + } + return COND_RESULT_FALSE; + } + + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "bsbsb", dc->comp_key, + "(", l, ") compare to ", dc->string); + } + switch(dc->cond) { + case CONFIG_COND_NE: + case CONFIG_COND_EQ: + if (buffer_is_equal(l, dc->string)) { + return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE; + } else { + return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE; + } + case CONFIG_COND_NOMATCH: + case CONFIG_COND_MATCH: { + if (data_config_pcre_exec(dc, cache, l) > 0) { + return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_TRUE : COND_RESULT_FALSE; + } else { + /* cache is already cleared */ + return (dc->cond == CONFIG_COND_MATCH) ? COND_RESULT_FALSE : COND_RESULT_TRUE; + } + } + default: + /* no way */ + break; + } + + return COND_RESULT_FALSE; +} + +static cond_result_t config_check_cond_cached(server *srv, connection *con, data_config *dc) { + cond_cache_t *caches = con->cond_cache; + + if (COND_RESULT_UNSET == caches[dc->context_ndx].result) { + caches[dc->context_ndx].result = config_check_cond_nocache(srv, con, dc); + switch (caches[dc->context_ndx].result) { + case COND_RESULT_FALSE: + case COND_RESULT_TRUE: + /* remember result of local condition for a partial reset */ + caches[dc->context_ndx].local_result = caches[dc->context_ndx].result; + break; + default: + break; + } + + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "dss", + dc->context_ndx, + "(uncached) result:", + cond_result_to_string(caches[dc->context_ndx].result)); + } + } else { + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "dss", + dc->context_ndx, + "(cached) result:", + cond_result_to_string(caches[dc->context_ndx].result)); + } + } + return caches[dc->context_ndx].result; +} + +/* if we reset the cache result for a node, we also need to clear all + * child nodes and else-branches*/ +static void config_cond_clear_node(server *srv, connection *con, data_config *dc) { + /* if a node is "unset" all children are unset too */ + if (con->cond_cache[dc->context_ndx].result != COND_RESULT_UNSET) { + size_t i; + + #if 0 + /* (redundant; matches not relevant unless COND_RESULT_TRUE) */ + switch (con->cond_cache[dc->context_ndx].local_result) { + case COND_RESULT_TRUE: + case COND_RESULT_FALSE: + break; + default: + con->cond_cache[dc->context_ndx].patterncount = 0; + con->cond_cache[dc->context_ndx].comp_value = NULL; + } + #endif + con->cond_cache[dc->context_ndx].result = COND_RESULT_UNSET; + + for (i = 0; i < dc->children.used; ++i) { + data_config *dc_child = dc->children.data[i]; + if (NULL == dc_child->prev) { + /* only call for first node in if-else chain */ + config_cond_clear_node(srv, con, dc_child); + } + } + if (NULL != dc->next) config_cond_clear_node(srv, con, dc->next); + } +} + +/** + * reset the config-cache for a named item + * + * if the item is COND_LAST_ELEMENT we reset all items + */ +void config_cond_cache_reset_item(server *srv, connection *con, comp_key_t item) { + size_t i; + + for (i = 0; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + + if (item == dc->comp) { + /* clear local_result */ + con->cond_cache[i].local_result = COND_RESULT_UNSET; + /* clear result in subtree (including the node itself) */ + config_cond_clear_node(srv, con, dc); + } + } +} + +/** + * reset the config cache to its initial state at connection start + */ +void config_cond_cache_reset(server *srv, connection *con) { + size_t i; + + /* resetting all entries; no need to follow children as in config_cond_cache_reset_item */ + for (i = 0; i < srv->config_context->used; i++) { + con->cond_cache[i].result = COND_RESULT_UNSET; + con->cond_cache[i].local_result = COND_RESULT_UNSET; + con->cond_cache[i].patterncount = 0; + con->cond_cache[i].comp_value = NULL; + } + + for (i = 0; i < COMP_LAST_ELEMENT; i++) { + con->conditional_is_valid[i] = 0; + } +} + +int config_check_cond(server *srv, connection *con, data_config *dc) { + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "=== start of condition block ==="); + } + return (config_check_cond_cached(srv, con, dc) == COND_RESULT_TRUE); +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/configfile.c b/data/lighttpd/lighttpd-1.4.53/src/configfile.c new file mode 100644 index 000000000..b359dcbeb --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/configfile.c @@ -0,0 +1,1665 @@ +#include "first.h" + +#include "base.h" +#include "burl.h" +#include "fdevent.h" +#include "keyvalue.h" +#include "log.h" +#include "stream.h" + +#include "configparser.h" +#include "configfile.h" +#include "stat_cache.h" +#include "sys-crypto.h" + +#include <sys/stat.h> +#include <sys/wait.h> + +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include <glob.h> + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +#if defined(HAVE_MYSQL) || (defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER)) +static void config_warn_authn_module (server *srv, const char *module, size_t len) { + for (size_t i = 0; i < srv->config_context->used; ++i) { + const data_config *config = (data_config const*)srv->config_context->data[i]; + const data_unset *du = array_get_element(config->value, "auth.backend"); + if (NULL != du && du->type == TYPE_STRING) { + data_string *ds = (data_string *)du; + if (buffer_is_equal_string(ds->value, module, len)) { + buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("mod_authn_")); + buffer_append_string_len(srv->tmp_buf, module, len); + array_insert_value(srv->srvconf.modules, CONST_BUF_LEN(srv->tmp_buf)); + log_error_write(srv, __FILE__, __LINE__, "SSSsSSS", "Warning: please add \"mod_authn_", module, "\" to server.modules list in lighttpd.conf. A future release of lighttpd 1.4.x will not automatically load mod_authn_", module, "and lighttpd will fail to start up since your lighttpd.conf uses auth.backend = \"", module, "\"."); + return; + } + } + } +} +#endif + +#ifdef USE_OPENSSL_CRYPTO +static void config_warn_openssl_module (server *srv) { + for (size_t i = 0; i < srv->config_context->used; ++i) { + const data_config *config = (data_config const*)srv->config_context->data[i]; + for (size_t j = 0; j < config->value->used; ++j) { + data_unset *du = config->value->data[j]; + if (0 == strncmp(du->key->ptr, "ssl.", sizeof("ssl.")-1)) { + /* mod_openssl should be loaded after mod_extforward */ + array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_openssl")); + log_error_write(srv, __FILE__, __LINE__, "S", "Warning: please add \"mod_openssl\" to server.modules list in lighttpd.conf. A future release of lighttpd 1.4.x *will not* automatically load mod_openssl and lighttpd *will not* use SSL/TLS where your lighttpd.conf contains ssl.* directives"); + return; + } + } + } +} +#endif + +static int config_http_parseopts (server *srv, array *a) { + unsigned short int opts = srv->srvconf.http_url_normalize; + unsigned short int decode_2f = 1; + int rc = 1; + if (!array_is_kvstring(a)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for server.http-parseopts; " + "expected list of \"key\" => \"[enable|disable]\""); + return 0; + } + for (size_t i = 0; i < a->used; ++i) { + const data_string * const ds = (data_string *)a->data[i]; + unsigned short int opt; + int val = 0; + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("enable"))) + val = 1; + else if (buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))) + val = 0; + else { + log_error_write(srv, __FILE__, __LINE__, "sbsbs", + "unrecognized value for server.http-parseopts:", + ds->key, "=>", ds->value, + "(expect \"[enable|disable]\")"); + rc = 0; + } + if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-normalize"))) + opt = HTTP_PARSEOPT_URL_NORMALIZE; + else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-normalize-unreserved"))) + opt = HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED; + else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-normalize-required"))) + opt = HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED; + else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-ctrls-reject"))) + opt = HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT; + else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-path-backslash-trans"))) + opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_BACKSLASH_TRANS; + else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-path-2f-decode"))) + opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE; + else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-path-2f-reject"))) + opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT; + else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-path-dotseg-remove"))) + opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE; + else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-path-dotseg-reject"))) + opt = HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT; + else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("url-query-20-plus"))) + opt = HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS; + else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("header-strict"))) { + srv->srvconf.http_header_strict = val; + continue; + } + else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("host-strict"))) { + srv->srvconf.http_host_strict = val; + continue; + } + else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("host-normalize"))) { + srv->srvconf.http_host_normalize = val; + continue; + } + else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "unrecognized key for server.http-parseopts:", + ds->key); + rc = 0; + continue; + } + if (val) + opts |= opt; + else { + opts &= ~opt; + if (opt == HTTP_PARSEOPT_URL_NORMALIZE) { + opts = 0; + break; + } + if (opt == HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE) { + decode_2f = 0; + } + } + } + if (opts != 0) { + opts |= HTTP_PARSEOPT_URL_NORMALIZE; + if ((opts & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE + |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) + == (HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE + |HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "conflicting options in server.http-parseopts:" + "url-path-2f-decode, url-path-2f-reject"); + rc = 0; + } + if ((opts & (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE + |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT)) + == (HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE + |HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "conflicting options in server.http-parseopts:" + "url-path-dotseg-remove, url-path-dotseg-reject"); + rc = 0; + } + if (!(opts & (HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED + |HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED))) { + opts |= HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED; + if (decode_2f + && !(opts & HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT)) + opts |= HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE; + } + } + srv->srvconf.http_url_normalize = opts; + return rc; +} + +static int config_insert(server *srv) { + size_t i; + int ret = 0; + buffer *stat_cache_string; + array *http_parseopts; + unsigned int chunk_sz = 0; + + config_values_t cv[] = { + { "server.bind", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 0 */ + { "server.errorlog", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 1 */ + { "server.errorfile-prefix", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "server.chroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 3 */ + { "server.username", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 4 */ + { "server.groupname", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 5 */ + { "server.port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 6 */ + { "server.tag", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ + { "server.use-ipv6", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ + { "server.modules", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_SERVER }, /* 9 */ + + { "server.event-handler", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 10 */ + { "server.pid-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 11 */ + { "server.max-request-size", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ + { "server.max-worker", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 13 */ + { "server.document-root", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ + { "server.force-lowercase-filenames", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */ + { "debug.log-condition-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 16 */ + { "server.max-keep-alive-requests", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 17 */ + { "server.name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 18 */ + { "server.max-keep-alive-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 19 */ + + { "server.max-read-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 20 */ + { "server.max-write-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 21 */ + { "server.error-handler", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 22 */ + { "server.max-fds", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 23 */ +#ifdef HAVE_LSTAT + { "server.follow-symlink", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 24 */ +#else + { "server.follow-symlink", + "Your system lacks lstat(). We can not differ symlinks from files." + "Please remove server.follow-symlinks from your config.", + T_CONFIG_UNSUPPORTED, T_CONFIG_SCOPE_UNSET }, +#endif + { "server.kbytes-per-second", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 25 */ + { "connection.kbytes-per-second", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 26 */ + { "mimetype.use-xattr", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 27 */ + { "mimetype.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 28 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 29 */ + + { "ssl.engine", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 30 */ + { "debug.log-file-not-found", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 31 */ + { "debug.log-request-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 32 */ + { "debug.log-response-header", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 33 */ + { "debug.log-request-header", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 34 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 35 */ + { "server.protocol-http11", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 36 */ + { "debug.log-request-header-on-error", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 37 */ + { "debug.log-state-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 38 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 39 */ + + { "server.errorlog-use-syslog", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 40 */ + { "server.range-requests", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 41 */ + { "server.stat-cache-engine", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 42 */ + { "server.max-connections", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 43 */ + { "server.network-backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 44 */ + { "server.upload-dirs", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_SERVER }, /* 45 */ + { "server.core-files", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 46 */ + { "server.compat-module-load", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 47 */ + { "server.chunkqueue-chunk-sz", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_SERVER }, /* 48 */ + { "etag.use-inode", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 49 */ + + { "etag.use-mtime", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 50 */ + { "etag.use-size", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 51 */ + { "server.reject-expect-100-with-417", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 52 */ + { "debug.log-timeouts", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 53 */ + { "server.defer-accept", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 54 */ + { "server.breakagelog", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 55 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 56 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 57 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 58 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 59 */ + + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 60 */ + { "server.set-v6only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 61 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 62 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 63 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 64 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 65 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 66 */ + { "unused-slot-moved-to-mod-openssl", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 67 */ + { "server.upload-temp-file-size", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_SERVER }, /* 68 */ + { "mimetype.xattr-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 69 */ + { "server.listen-backlog", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 70 */ + { "server.error-handler-404", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 71 */ + { "server.http-parseopt-header-strict",NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 72 */ + { "server.http-parseopt-host-strict", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 73 */ + { "server.http-parseopt-host-normalize",NULL,T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 74 */ + { "server.bsd-accept-filter", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 75 */ + { "server.stream-request-body", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 76 */ + { "server.stream-response-body", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 77 */ + { "server.max-request-field-size", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_SERVER }, /* 78 */ + { "server.error-intercept", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 79 */ + { "server.syslog-facility", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 80 */ + { "server.socket-perms", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 81 */ + { "server.http-parseopts", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_SERVER }, /* 82 */ + { "server.systemd-socket-activation", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 83 */ + + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + /* all T_CONFIG_SCOPE_SERVER options */ + cv[0].destination = srv->srvconf.bindhost; + cv[1].destination = srv->srvconf.errorlog_file; + cv[3].destination = srv->srvconf.changeroot; + cv[4].destination = srv->srvconf.username; + cv[5].destination = srv->srvconf.groupname; + cv[6].destination = &(srv->srvconf.port); + cv[9].destination = srv->srvconf.modules; + + cv[10].destination = srv->srvconf.event_handler; + cv[11].destination = srv->srvconf.pid_file; + cv[13].destination = &(srv->srvconf.max_worker); + + cv[23].destination = &(srv->srvconf.max_fds); + + cv[37].destination = &(srv->srvconf.log_request_header_on_error); + cv[38].destination = &(srv->srvconf.log_state_handling); + + cv[40].destination = &(srv->srvconf.errorlog_use_syslog); + stat_cache_string = buffer_init(); + cv[42].destination = stat_cache_string; + cv[43].destination = &(srv->srvconf.max_conns); + cv[44].destination = srv->srvconf.network_backend; + cv[45].destination = srv->srvconf.upload_tempdirs; + cv[46].destination = &(srv->srvconf.enable_cores); + cv[47].destination = &(srv->srvconf.compat_module_load); + cv[48].destination = &chunk_sz; + + cv[52].destination = &(srv->srvconf.reject_expect_100_with_417); + cv[55].destination = srv->srvconf.breakagelog_file; + + cv[68].destination = &(srv->srvconf.upload_temp_file_size); + cv[69].destination = srv->srvconf.xattr_name; + cv[72].destination = &(srv->srvconf.http_header_strict); + cv[73].destination = &(srv->srvconf.http_host_strict); + cv[74].destination = &(srv->srvconf.http_host_normalize); + cv[78].destination = &(srv->srvconf.max_request_field_size); + cv[80].destination = srv->srvconf.syslog_facility; + http_parseopts = array_init(); + cv[82].destination = http_parseopts; + cv[83].destination = &(srv->srvconf.systemd_socket_activation); + + srv->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); + + force_assert(srv->config_storage); + force_assert(srv->config_context->used); /* static analysis hint for ccc +-analyzer */ + + for (i = 0; i < srv->config_context->used; i++) { + data_config * const config = (data_config *)srv->config_context->data[i]; + specific_config *s; + + s = calloc(1, sizeof(specific_config)); + force_assert(s); + s->document_root = buffer_init(); + s->mimetypes = array_init(); + s->server_name = buffer_init(); + s->error_handler = buffer_init(); + s->error_handler_404 = buffer_init(); + s->server_tag = buffer_init(); + s->errorfile_prefix = buffer_init(); + #if defined(__FreeBSD__) || defined(__NetBSD__) \ + || defined(__OpenBSD__) || defined(__DragonFly__) + s->bsd_accept_filter = (i == 0) + ? buffer_init() + : buffer_init_buffer(srv->config_storage[0]->bsd_accept_filter); + #endif + s->socket_perms = (i == 0 || buffer_string_is_empty(srv->config_storage[0]->socket_perms)) + ? buffer_init() + : buffer_init_buffer(srv->config_storage[0]->socket_perms); + s->max_keep_alive_requests = 100; + s->max_keep_alive_idle = 5; + s->max_read_idle = 60; + s->max_write_idle = 360; + s->max_request_size = 0; + s->use_xattr = 0; + s->ssl_enabled = 0; + s->use_ipv6 = (i == 0) ? 0 : srv->config_storage[0]->use_ipv6; + s->set_v6only = (i == 0) ? 1 : srv->config_storage[0]->set_v6only; + s->defer_accept = (i == 0) ? 0 : srv->config_storage[0]->defer_accept; +#ifdef HAVE_LSTAT + s->follow_symlink = 1; +#endif + s->kbytes_per_second = 0; + s->allow_http11 = 1; + s->etag_use_inode = 1; + s->etag_use_mtime = 1; + s->etag_use_size = 1; + s->range_requests = 1; + s->force_lowercase_filenames = (i == 0) ? 2 : 0; /* we wan't to detect later if user changed this for global section */ + s->global_kbytes_per_second = 0; + s->global_bytes_per_second_cnt = 0; + s->global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt; + s->listen_backlog = (0 == i ? 1024 : srv->config_storage[0]->listen_backlog); + s->stream_request_body = 0; + s->stream_response_body = 0; + s->error_intercept = 0; + + /* all T_CONFIG_SCOPE_CONNECTION options */ + cv[2].destination = s->errorfile_prefix; + cv[7].destination = s->server_tag; + cv[8].destination = &(s->use_ipv6); + + cv[12].destination = &(s->max_request_size); + cv[14].destination = s->document_root; + cv[15].destination = &(s->force_lowercase_filenames); + cv[16].destination = &(s->log_condition_handling); + cv[17].destination = &(s->max_keep_alive_requests); + cv[18].destination = s->server_name; + cv[19].destination = &(s->max_keep_alive_idle); + + cv[20].destination = &(s->max_read_idle); + cv[21].destination = &(s->max_write_idle); + cv[22].destination = s->error_handler; +#ifdef HAVE_LSTAT + cv[24].destination = &(s->follow_symlink); +#endif + cv[25].destination = &(s->global_kbytes_per_second); + cv[26].destination = &(s->kbytes_per_second); + cv[27].destination = &(s->use_xattr); + cv[28].destination = s->mimetypes; + /*cv[29].destination = s->unused;*/ + + cv[30].destination = &(s->ssl_enabled); + cv[31].destination = &(s->log_file_not_found); + cv[32].destination = &(s->log_request_handling); + cv[33].destination = &(s->log_response_header); + cv[34].destination = &(s->log_request_header); + /*cv[35].destination = &(s->unused);*/ + cv[36].destination = &(s->allow_http11); + /*cv[39].destination = s->unused;*/ + + cv[41].destination = &(s->range_requests); + /*cv[47].destination = s->unused;*/ + /*cv[48].destination = &(s->unused);*/ + cv[49].destination = &(s->etag_use_inode); + + cv[50].destination = &(s->etag_use_mtime); + cv[51].destination = &(s->etag_use_size); + cv[53].destination = &(s->log_timeouts); + cv[54].destination = &(s->defer_accept); + /*cv[56].destination = &(s->unused);*/ + /*cv[57].destination = &(s->unused);*/ + /*cv[58].destination = &(s->unused);*/ + /*cv[59].destination = s->unused;*/ + + /*cv[60].destination = &(s->unused);*/ + cv[61].destination = &(s->set_v6only); + /*cv[62].destination = &(s->unused);*/ + /*cv[63].destination = s->unused;*/ + /*cv[64].destination = s->unused;*/ + /*cv[65].destination = &(s->unused);*/ + /*cv[66].destination = &(s->unused);*/ + /*cv[67].destination = &(s->unused);*/ + cv[70].destination = &(s->listen_backlog); + cv[71].destination = s->error_handler_404; + #if defined(__FreeBSD__) || defined(__NetBSD__) \ + || defined(__OpenBSD__) || defined(__DragonFly__) + cv[75].destination = s->bsd_accept_filter; + #endif + cv[76].destination = &(s->stream_request_body); + cv[77].destination = &(s->stream_response_body); + cv[79].destination = &(s->error_intercept); + cv[81].destination = s->socket_perms; + + srv->config_storage[i] = s; + + if (0 != (ret = config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION))) { + break; + } + + if (s->stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN) { + s->stream_request_body |= FDEVENT_STREAM_REQUEST; + } + if (s->stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) { + s->stream_response_body |= FDEVENT_STREAM_RESPONSE; + } + + if (!array_is_kvstring(s->mimetypes)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for mimetype.assign; expected list of \"ext\" => \"mimetype\""); + } + + if (!buffer_string_is_empty(s->server_tag)) { + for (char *t = strchr(s->server_tag->ptr,'\n'); NULL != t; t = strchr(t+2,'\n')) { + /* not expecting admin to define multi-line server.tag, + * but ensure server_tag has proper header continuation, + * if needed */ + off_t off = t - s->server_tag->ptr; + size_t len; + if (t[1] == ' ' || t[1] == '\t') continue; + len = buffer_string_length(s->server_tag); + buffer_string_prepare_append(s->server_tag, 1); + t = s->server_tag->ptr+off; + memmove(t+2, t+1, len - off - 1); + t[1] = ' '; + buffer_commit(s->server_tag, 1); + } + } + + if (0 == i) { + if (!config_http_parseopts(srv, http_parseopts)) { + ret = HANDLER_ERROR; + break; + } + } + + if (srv->srvconf.http_url_normalize + && COMP_HTTP_QUERY_STRING == config->comp) { + switch(config->cond) { + case CONFIG_COND_NE: + case CONFIG_COND_EQ: + /* (can use this routine as long as it does not perform + * any regex-specific normalization of first arg) */ + pcre_keyvalue_burl_normalize_key(config->string, srv->tmp_buf); + break; + case CONFIG_COND_NOMATCH: + case CONFIG_COND_MATCH: + pcre_keyvalue_burl_normalize_key(config->string, srv->tmp_buf); + if (!data_config_pcre_compile(config)) { + ret = HANDLER_ERROR; + } + break; + default: + break; + } + if (HANDLER_ERROR == ret) break; + } + + #ifndef USE_OPENSSL_CRYPTO + if (s->ssl_enabled) { + log_error_write(srv, __FILE__, __LINE__, "s", + "ssl support is missing, recompile with e.g. --with-openssl"); + ret = HANDLER_ERROR; + break; + } + #endif + } + array_free(http_parseopts); + + { + specific_config *s = srv->config_storage[0]; + s->http_parseopts= /*(global, but stored in con->conf.http_parseopts)*/ + (srv->srvconf.http_header_strict ?(HTTP_PARSEOPT_HEADER_STRICT) :0) + |(srv->srvconf.http_host_strict ?(HTTP_PARSEOPT_HOST_STRICT + |HTTP_PARSEOPT_HOST_NORMALIZE):0) + |(srv->srvconf.http_host_normalize ?(HTTP_PARSEOPT_HOST_NORMALIZE):0); + s->http_parseopts |= srv->srvconf.http_url_normalize; + + if (s->log_request_handling || s->log_request_header) + srv->srvconf.log_request_header_on_error = 1; + } + + if (0 != chunk_sz) { + chunkqueue_set_chunk_size(chunk_sz); + } + + if (0 != stat_cache_choose_engine(srv, stat_cache_string)) { + ret = HANDLER_ERROR; + } + buffer_free(stat_cache_string); + + if (!array_is_vlist(srv->srvconf.upload_tempdirs)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for server.upload-dirs; expected list of \"path\" strings"); + ret = HANDLER_ERROR; + } + + if (!array_is_vlist(srv->srvconf.modules)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for server.modules; expected list of \"mod_xxxxxx\" strings"); + ret = HANDLER_ERROR; + } else if (srv->srvconf.compat_module_load) { + data_string *ds; + int prepend_mod_indexfile = 1; + int append_mod_dirlisting = 1; + int append_mod_staticfile = 1; + int append_mod_authn_file = 1; + int append_mod_authn_ldap = 1; + int append_mod_authn_mysql = 1; + int append_mod_openssl = 1; + int contains_mod_auth = 0; + + /* prepend default modules */ + for (i = 0; i < srv->srvconf.modules->used; i++) { + ds = (data_string *)srv->srvconf.modules->data[i]; + + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_indexfile"))) { + prepend_mod_indexfile = 0; + } + + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_staticfile"))) { + append_mod_staticfile = 0; + } + + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_dirlisting"))) { + append_mod_dirlisting = 0; + } + + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_openssl"))) { + append_mod_openssl = 0; + } + + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_authn_file"))) { + append_mod_authn_file = 0; + } + + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_authn_ldap"))) { + append_mod_authn_ldap = 0; + } + + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_authn_mysql"))) { + append_mod_authn_mysql = 0; + } + + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_auth"))) { + contains_mod_auth = 1; + } + + if (0 == prepend_mod_indexfile && + 0 == append_mod_dirlisting && + 0 == append_mod_staticfile && + 0 == append_mod_openssl && + 0 == append_mod_authn_file && + 0 == append_mod_authn_ldap && + 0 == append_mod_authn_mysql && + 1 == contains_mod_auth) { + break; + } + } + + if (prepend_mod_indexfile) { + /* mod_indexfile has to be loaded before mod_fastcgi and friends */ + array *modules = array_init(); + array_insert_value(modules, CONST_STR_LEN("mod_indexfile")); + + for (i = 0; i < srv->srvconf.modules->used; i++) { + ds = (data_string *)srv->srvconf.modules->data[i]; + array_insert_value(modules, CONST_BUF_LEN(ds->value)); + } + + array_free(srv->srvconf.modules); + srv->srvconf.modules = modules; + } + + /* append default modules */ + if (append_mod_dirlisting) { + array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_dirlisting")); + } + + if (append_mod_staticfile) { + array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_staticfile")); + } + + if (append_mod_openssl) { + #ifdef USE_OPENSSL_CRYPTO + config_warn_openssl_module(srv); + #endif + } + + /* mod_auth.c,http_auth.c auth backends were split into separate modules + * Automatically load auth backend modules for compatibility with + * existing lighttpd 1.4.x configs */ + if (contains_mod_auth) { + if (append_mod_authn_file) { + array_insert_value(srv->srvconf.modules, CONST_STR_LEN("mod_authn_file")); + } + if (append_mod_authn_ldap) { + #if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER) + config_warn_authn_module(srv, CONST_STR_LEN("ldap")); + #endif + } + if (append_mod_authn_mysql) { + #if defined(HAVE_MYSQL) + config_warn_authn_module(srv, CONST_STR_LEN("mysql")); + #endif + } + } + } + + return ret; + +} + + +#define PATCH(x) con->conf.x = s->x +int config_setup_connection(server *srv, connection *con) { + specific_config *s = srv->config_storage[0]; + + PATCH(http_parseopts); + + PATCH(allow_http11); + PATCH(mimetypes); + PATCH(document_root); + PATCH(high_precision_timestamps); + PATCH(max_keep_alive_requests); + PATCH(max_keep_alive_idle); + PATCH(max_read_idle); + PATCH(max_write_idle); + PATCH(max_request_size); + PATCH(use_xattr); + PATCH(error_handler); + PATCH(error_handler_404); + PATCH(error_intercept); + PATCH(errorfile_prefix); +#ifdef HAVE_LSTAT + PATCH(follow_symlink); +#endif + PATCH(server_tag); + PATCH(kbytes_per_second); + PATCH(global_kbytes_per_second); + PATCH(global_bytes_per_second_cnt); + + con->conf.global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt; + buffer_copy_buffer(con->server_name, s->server_name); + + PATCH(log_request_header); + PATCH(log_response_header); + PATCH(log_request_handling); + PATCH(log_condition_handling); + PATCH(log_file_not_found); + PATCH(log_timeouts); + + PATCH(range_requests); + PATCH(force_lowercase_filenames); + /*PATCH(listen_backlog);*//*(not necessary; used only at startup)*/ + PATCH(stream_request_body); + PATCH(stream_response_body); + PATCH(socket_perms); + + PATCH(etag_use_inode); + PATCH(etag_use_mtime); + PATCH(etag_use_size); + + return 0; +} + +int config_patch_connection(server *srv, connection *con) { + size_t i, j; + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + specific_config *s = srv->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.document-root"))) { + PATCH(document_root); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.range-requests"))) { + PATCH(range_requests); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.error-handler"))) { + PATCH(error_handler); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.error-handler-404"))) { + PATCH(error_handler_404); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.error-intercept"))) { + PATCH(error_intercept); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.errorfile-prefix"))) { + PATCH(errorfile_prefix); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("mimetype.assign"))) { + PATCH(mimetypes); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-keep-alive-requests"))) { + PATCH(max_keep_alive_requests); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-keep-alive-idle"))) { + PATCH(max_keep_alive_idle); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-write-idle"))) { + PATCH(max_write_idle); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-read-idle"))) { + PATCH(max_read_idle); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.max-request-size"))) { + PATCH(max_request_size); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("mimetype.use-xattr"))) { + PATCH(use_xattr); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("etag.use-inode"))) { + PATCH(etag_use_inode); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("etag.use-mtime"))) { + PATCH(etag_use_mtime); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("etag.use-size"))) { + PATCH(etag_use_size); +#ifdef HAVE_LSTAT + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.follow-symlink"))) { + PATCH(follow_symlink); +#endif + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.name"))) { + buffer_copy_buffer(con->server_name, s->server_name); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.tag"))) { + PATCH(server_tag); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.stream-request-body"))) { + PATCH(stream_request_body); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.stream-response-body"))) { + PATCH(stream_response_body); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("connection.kbytes-per-second"))) { + PATCH(kbytes_per_second); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-request-handling"))) { + PATCH(log_request_handling); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-request-header"))) { + PATCH(log_request_header); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-response-header"))) { + PATCH(log_response_header); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-condition-handling"))) { + PATCH(log_condition_handling); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-file-not-found"))) { + PATCH(log_file_not_found); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-timeouts"))) { + PATCH(log_timeouts); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.protocol-http11"))) { + PATCH(allow_http11); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.force-lowercase-filenames"))) { + PATCH(force_lowercase_filenames); + #if 0 /*(not necessary; used only at startup)*/ + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.listen-backlog"))) { + PATCH(listen_backlog); + #endif + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.kbytes-per-second"))) { + PATCH(global_kbytes_per_second); + PATCH(global_bytes_per_second_cnt); + con->conf.global_bytes_per_second_cnt_ptr = &s->global_bytes_per_second_cnt; + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.socket-perms"))) { + PATCH(socket_perms); + } + } + } + + con->etag_flags = (con->conf.etag_use_mtime ? ETAG_USE_MTIME : 0) | + (con->conf.etag_use_inode ? ETAG_USE_INODE : 0) | + (con->conf.etag_use_size ? ETAG_USE_SIZE : 0); + + return 0; +} +#undef PATCH + +typedef struct { + int foo; + int bar; + + const buffer *source; + const char *input; + size_t offset; + size_t size; + + int line_pos; + int line; + + int in_key; + int in_brace; + int in_cond; +} tokenizer_t; + +#if 0 +static int tokenizer_open(server *srv, tokenizer_t *t, buffer *basedir, const char *fn) { + if (buffer_string_is_empty(basedir) || + (fn[0] == '/' || fn[0] == '\\') || + (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\'))) { + t->file = buffer_init_string(fn); + } else { + t->file = buffer_init_buffer(basedir); + buffer_append_string(t->file, fn); + } + + if (0 != stream_open(&(t->s), t->file)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "opening configfile ", t->file, "failed:", strerror(errno)); + buffer_free(t->file); + return -1; + } + + t->input = t->s.start; + t->offset = 0; + t->size = t->s.size; + t->line = 1; + t->line_pos = 1; + + t->in_key = 1; + t->in_brace = 0; + t->in_cond = 0; + return 0; +} + +static int tokenizer_close(server *srv, tokenizer_t *t) { + UNUSED(srv); + + buffer_free(t->file); + return stream_close(&(t->s)); +} +#endif +static int config_skip_newline(tokenizer_t *t) { + int skipped = 1; + force_assert(t->input[t->offset] == '\r' || t->input[t->offset] == '\n'); + if (t->input[t->offset] == '\r' && t->input[t->offset + 1] == '\n') { + skipped ++; + t->offset ++; + } + t->offset ++; + return skipped; +} + +static int config_skip_comment(tokenizer_t *t) { + int i; + force_assert(t->input[t->offset] == '#'); + for (i = 1; t->input[t->offset + i] && + (t->input[t->offset + i] != '\n' && t->input[t->offset + i] != '\r'); + i++); + t->offset += i; + return i; +} + +static int config_tokenizer(server *srv, tokenizer_t *t, int *token_id, buffer *token) { + int tid = 0; + size_t i; + + for (tid = 0; tid == 0 && t->offset < t->size && t->input[t->offset] ; ) { + char c = t->input[t->offset]; + const char *start = NULL; + + switch (c) { + case '=': + if (t->in_brace) { + if (t->input[t->offset + 1] == '>') { + t->offset += 2; + + buffer_copy_string_len(token, CONST_STR_LEN("=>")); + + tid = TK_ARRAY_ASSIGN; + } else { + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + "source:", t->source, + "line:", t->line, "pos:", t->line_pos, + "use => for assignments in arrays"); + return -1; + } + } else if (t->in_cond) { + if (t->input[t->offset + 1] == '=') { + t->offset += 2; + + buffer_copy_string_len(token, CONST_STR_LEN("==")); + + tid = TK_EQ; + } else if (t->input[t->offset + 1] == '~') { + t->offset += 2; + + buffer_copy_string_len(token, CONST_STR_LEN("=~")); + + tid = TK_MATCH; + } else { + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + "source:", t->source, + "line:", t->line, "pos:", t->line_pos, + "only =~ and == are allowed in the condition"); + return -1; + } + t->in_key = 1; + t->in_cond = 0; + } else if (t->in_key) { + tid = TK_ASSIGN; + + buffer_copy_string_len(token, t->input + t->offset, 1); + + t->offset++; + t->line_pos++; + } else { + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + "source:", t->source, + "line:", t->line, "pos:", t->line_pos, + "unexpected equal-sign: ="); + return -1; + } + + break; + case '!': + if (t->in_cond) { + if (t->input[t->offset + 1] == '=') { + t->offset += 2; + + buffer_copy_string_len(token, CONST_STR_LEN("!=")); + + tid = TK_NE; + } else if (t->input[t->offset + 1] == '~') { + t->offset += 2; + + buffer_copy_string_len(token, CONST_STR_LEN("!~")); + + tid = TK_NOMATCH; + } else { + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + "source:", t->source, + "line:", t->line, "pos:", t->line_pos, + "only !~ and != are allowed in the condition"); + return -1; + } + t->in_key = 1; + t->in_cond = 0; + } else { + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + "source:", t->source, + "line:", t->line, "pos:", t->line_pos, + "unexpected exclamation-marks: !"); + return -1; + } + + break; + case '\t': + case ' ': + t->offset++; + t->line_pos++; + break; + case '\n': + case '\r': + if (t->in_brace == 0) { + int done = 0; + while (!done && t->offset < t->size) { + switch (t->input[t->offset]) { + case '\r': + case '\n': + config_skip_newline(t); + t->line_pos = 1; + t->line++; + break; + + case '#': + t->line_pos += config_skip_comment(t); + break; + + case '\t': + case ' ': + t->offset++; + t->line_pos++; + break; + + default: + done = 1; + } + } + t->in_key = 1; + tid = TK_EOL; + buffer_copy_string_len(token, CONST_STR_LEN("(EOL)")); + } else { + config_skip_newline(t); + t->line_pos = 1; + t->line++; + } + break; + case ',': + if (t->in_brace > 0) { + tid = TK_COMMA; + + buffer_copy_string_len(token, CONST_STR_LEN("(COMMA)")); + } + + t->offset++; + t->line_pos++; + break; + case '"': + /* search for the terminating " */ + start = t->input + t->offset + 1; + buffer_copy_string_len(token, CONST_STR_LEN("")); + + for (i = 1; t->input[t->offset + i]; i++) { + if (t->input[t->offset + i] == '\\' && + t->input[t->offset + i + 1] == '"') { + + buffer_append_string_len(token, start, t->input + t->offset + i - start); + + start = t->input + t->offset + i + 1; + + /* skip the " */ + i++; + continue; + } + + + if (t->input[t->offset + i] == '"') { + tid = TK_STRING; + + buffer_append_string_len(token, start, t->input + t->offset + i - start); + + break; + } + } + + if (t->input[t->offset + i] == '\0') { + /* ERROR */ + + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + "source:", t->source, + "line:", t->line, "pos:", t->line_pos, + "missing closing quote"); + + return -1; + } + + t->offset += i + 1; + t->line_pos += i + 1; + + break; + case '(': + t->offset++; + t->in_brace++; + + tid = TK_LPARAN; + + buffer_copy_string_len(token, CONST_STR_LEN("(")); + break; + case ')': + t->offset++; + t->in_brace--; + + tid = TK_RPARAN; + + buffer_copy_string_len(token, CONST_STR_LEN(")")); + break; + case '$': + t->offset++; + + tid = TK_DOLLAR; + t->in_cond = 1; + t->in_key = 0; + + buffer_copy_string_len(token, CONST_STR_LEN("$")); + + break; + + case '+': + if (t->input[t->offset + 1] == '=') { + t->offset += 2; + buffer_copy_string_len(token, CONST_STR_LEN("+=")); + tid = TK_APPEND; + } else { + t->offset++; + tid = TK_PLUS; + buffer_copy_string_len(token, CONST_STR_LEN("+")); + } + break; + + case ':': + if (t->input[t->offset+1] == '=') { + t->offset += 2; + tid = TK_FORCE_ASSIGN; + buffer_copy_string_len(token, CONST_STR_LEN(":=")); + } + break; + + case '{': + t->offset++; + + tid = TK_LCURLY; + + buffer_copy_string_len(token, CONST_STR_LEN("{")); + + break; + + case '}': + t->offset++; + + tid = TK_RCURLY; + + buffer_copy_string_len(token, CONST_STR_LEN("}")); + + break; + + case '[': + t->offset++; + + tid = TK_LBRACKET; + + buffer_copy_string_len(token, CONST_STR_LEN("[")); + + break; + + case ']': + t->offset++; + + tid = TK_RBRACKET; + + buffer_copy_string_len(token, CONST_STR_LEN("]")); + + break; + case '#': + t->line_pos += config_skip_comment(t); + + break; + default: + if (t->in_cond) { + for (i = 0; t->input[t->offset + i] && + (isalpha((unsigned char)t->input[t->offset + i]) + || t->input[t->offset + i] == '_'); ++i); + + if (i && t->input[t->offset + i]) { + tid = TK_SRVVARNAME; + buffer_copy_string_len(token, t->input + t->offset, i); + + t->offset += i; + t->line_pos += i; + } else { + /* ERROR */ + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + "source:", t->source, + "line:", t->line, "pos:", t->line_pos, + "invalid character in condition"); + return -1; + } + } else if (isdigit((unsigned char)c)) { + /* take all digits */ + for (i = 0; t->input[t->offset + i] && isdigit((unsigned char)t->input[t->offset + i]); i++); + + /* was there it least a digit ? */ + if (i) { + tid = TK_INTEGER; + + buffer_copy_string_len(token, t->input + t->offset, i); + + t->offset += i; + t->line_pos += i; + } + } else { + /* the key might consist of [-.0-9a-z] */ + for (i = 0; t->input[t->offset + i] && + (isalnum((unsigned char)t->input[t->offset + i]) || + t->input[t->offset + i] == '.' || + t->input[t->offset + i] == '_' || /* for env.* */ + t->input[t->offset + i] == '-' + ); i++); + + if (i && t->input[t->offset + i]) { + buffer_copy_string_len(token, t->input + t->offset, i); + + if (strcmp(token->ptr, "include") == 0) { + tid = TK_INCLUDE; + } else if (strcmp(token->ptr, "include_shell") == 0) { + tid = TK_INCLUDE_SHELL; + } else if (strcmp(token->ptr, "global") == 0) { + tid = TK_GLOBAL; + } else if (strcmp(token->ptr, "else") == 0) { + tid = TK_ELSE; + } else { + tid = TK_LKEY; + } + + t->offset += i; + t->line_pos += i; + } else { + /* ERROR */ + log_error_write(srv, __FILE__, __LINE__, "sbsdsds", + "source:", t->source, + "line:", t->line, "pos:", t->line_pos, + "invalid character in variable name"); + return -1; + } + } + break; + } + } + + if (tid) { + *token_id = tid; +#if 0 + log_error_write(srv, __FILE__, __LINE__, "sbsdsdbdd", + "source:", t->source, + "line:", t->line, "pos:", t->line_pos, + token, token->used - 1, tid); +#endif + + return 1; + } else if (t->offset < t->size) { + log_error_write(srv, __FILE__, __LINE__, "Dsb", tid, ",", token); + } + return 0; +} + +static int config_parse(server *srv, config_t *context, tokenizer_t *t) { + void *pParser; + int token_id; + buffer *token, *lasttoken; + int ret; + + pParser = configparserAlloc( malloc ); + force_assert(pParser); + lasttoken = buffer_init(); + token = buffer_init(); + while((1 == (ret = config_tokenizer(srv, t, &token_id, token))) && context->ok) { + buffer_copy_buffer(lasttoken, token); + configparser(pParser, token_id, token, context); + + token = buffer_init(); + } + buffer_free(token); + + if (ret != -1 && context->ok) { + /* add an EOL at EOF, better than say sorry */ + configparser(pParser, TK_EOL, buffer_init_string("(EOL)"), context); + if (context->ok) { + configparser(pParser, 0, NULL, context); + } + } + configparserFree(pParser, free); + + if (ret == -1) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "configfile parser failed at:", lasttoken); + } else if (context->ok == 0) { + log_error_write(srv, __FILE__, __LINE__, "sbsdsdsb", + "source:", t->source, + "line:", t->line, "pos:", t->line_pos, + "parser failed somehow near here:", lasttoken); + ret = -1; + } + buffer_free(lasttoken); + + return ret == -1 ? -1 : 0; +} + +static int tokenizer_init(tokenizer_t *t, const buffer *source, const char *input, size_t size) { + + t->source = source; + t->input = input; + t->size = size; + t->offset = 0; + t->line = 1; + t->line_pos = 1; + + t->in_key = 1; + t->in_brace = 0; + t->in_cond = 0; + return 0; +} + +static int config_parse_file_stream(server *srv, config_t *context, const buffer *filename) { + tokenizer_t t; + stream s; + int ret; + + if (0 != stream_open(&s, filename)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "opening configfile ", filename, "failed:", strerror(errno)); + return -1; + } else { + tokenizer_init(&t, filename, s.start, s.size); + ret = config_parse(srv, context, &t); + } + + stream_close(&s); + return ret; +} + +int config_parse_file(server *srv, config_t *context, const char *fn) { + buffer *filename; + size_t i; + int ret = -1; + #ifdef GLOB_BRACE + int flags = GLOB_BRACE; + #else + int flags = 0; + #endif + glob_t gl; + + if ((fn[0] == '/' || fn[0] == '\\') || + (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\')) || + (fn[0] == '.' && fn[1] == '.' && (fn[2] == '/' || fn[2] == '\\'))) { + filename = buffer_init_string(fn); + } else { + filename = buffer_init_buffer(context->basedir); + buffer_append_string(filename, fn); + } + + switch (glob(filename->ptr, flags, NULL, &gl)) { + case 0: + for (i = 0; i < gl.gl_pathc; ++i) { + buffer_copy_string(filename, gl.gl_pathv[i]); + ret = config_parse_file_stream(srv, context, filename); + if (0 != ret) break; + } + globfree(&gl); + break; + case GLOB_NOMATCH: + if (filename->ptr[strcspn(filename->ptr, "*?[]{}")] != '\0') { /*(contains glob metachars)*/ + ret = 0; /* not an error if no files match glob pattern */ + } + else { + log_error_write(srv, __FILE__, __LINE__, "sb", "include file not found: ", filename); + } + break; + case GLOB_ABORTED: + case GLOB_NOSPACE: + log_error_write(srv, __FILE__, __LINE__, "sbss", "glob()", filename, "failed:", strerror(errno)); + break; + } + + buffer_free(filename); + return ret; +} + +#ifdef __CYGWIN__ + +static char* getCWD(char *buf, size_t sz) { + if (NULL == getcwd(buf, sz)) { + return NULL; + } + for (size_t i = 0; buf[i]; ++i) { + if (buf[i] == '\\') buf[i] = '/'; + } + return buf; +} + +#define getcwd(buf, sz) getCWD((buf),(sz)) + +#endif /* __CYGWIN__ */ + +int config_parse_cmd(server *srv, config_t *context, const char *cmd) { + int ret = 0; + int fds[2]; + char oldpwd[PATH_MAX]; + + if (NULL == getcwd(oldpwd, sizeof(oldpwd))) { + log_error_write(srv, __FILE__, __LINE__, "s", + "cannot get cwd", strerror(errno)); + return -1; + } + + if (!buffer_string_is_empty(context->basedir)) { + if (0 != chdir(context->basedir->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "cannot change directory to", context->basedir, strerror(errno)); + return -1; + } + } + + if (pipe(fds)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "pipe failed: ", strerror(errno)); + ret = -1; + } + else { + char *shell = getenv("SHELL"); + char *args[4]; + pid_t pid; + *(const char **)&args[0] = shell ? shell : "/bin/sh"; + *(const char **)&args[1] = "-c"; + *(const char **)&args[2] = cmd; + args[3] = NULL; + + fdevent_setfd_cloexec(fds[0]); + pid = fdevent_fork_execve(args[0], args, NULL, -1, fds[1], -1, -1); + if (-1 == pid) { + log_error_write(srv, __FILE__, __LINE__, "SSss", + "fork/exec(", cmd, "):", strerror(errno)); + ret = -1; + } + else { + ssize_t rd; + pid_t wpid; + int wstatus; + buffer *out = buffer_init(); + close(fds[1]); + fds[1] = -1; + do { + rd = read(fds[0], buffer_string_prepare_append(out, 1023), 1023); + if (rd >= 0) buffer_commit(out, (size_t)rd); + } while (rd > 0 || (-1 == rd && errno == EINTR)); + if (0 != rd) { + log_error_write(srv, __FILE__, __LINE__, "SSss", + "read \"", cmd, "\" failed:", strerror(errno)); + ret = -1; + } + close(fds[0]); + fds[0] = -1; + while (-1 == (wpid = waitpid(pid, &wstatus, 0)) && errno == EINTR) ; + if (wpid != pid) { + log_error_write(srv, __FILE__, __LINE__, "SSss", + "waitpid \"", cmd, "\" failed:", strerror(errno)); + ret = -1; + } + if (0 != wstatus) { + log_error_write(srv, __FILE__, __LINE__, "SSsd", + "command \"", cmd, "\" exited non-zero:", WEXITSTATUS(wstatus)); + ret = -1; + } + + if (-1 != ret) { + buffer *source = buffer_init_string(cmd); + tokenizer_t t; + tokenizer_init(&t, source, CONST_BUF_LEN(out)); + ret = config_parse(srv, context, &t); + buffer_free(source); + } + buffer_free(out); + } + if (-1 != fds[0]) close(fds[0]); + if (-1 != fds[1]) close(fds[1]); + } + + if (0 != chdir(oldpwd)) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "cannot change directory to", oldpwd, strerror(errno)); + ret = -1; + } + return ret; +} + +static void context_init(server *srv, config_t *context) { + context->srv = srv; + context->ok = 1; + vector_config_weak_init(&context->configs_stack); + context->basedir = buffer_init(); +} + +static void context_free(config_t *context) { + vector_config_weak_clear(&context->configs_stack); + buffer_free(context->basedir); +} + +int config_read(server *srv, const char *fn) { + config_t context; + data_config *dc; + buffer *dcwd; + int ret; + char *pos; + buffer *filename; + + context_init(srv, &context); + context.all_configs = srv->config_context; + +#ifdef __WIN32 + pos = strrchr(fn, '\\'); +#else + pos = strrchr(fn, '/'); +#endif + if (pos) { + buffer_copy_string_len(context.basedir, fn, pos - fn + 1); + } + + dc = data_config_init(); + buffer_copy_string_len(dc->key, CONST_STR_LEN("global")); + + force_assert(context.all_configs->used == 0); + dc->context_ndx = context.all_configs->used; + array_insert_unique(context.all_configs, (data_unset *)dc); + context.current = dc; + + /* default context */ + *array_get_int_ptr(dc->value, CONST_STR_LEN("var.PID")) = getpid(); + + dcwd = srv->tmp_buf; + buffer_string_prepare_copy(dcwd, PATH_MAX-1); + if (NULL != getcwd(dcwd->ptr, buffer_string_space(dcwd)+1)) { + buffer_commit(dcwd, strlen(dcwd->ptr)); + array_set_key_value(dc->value, CONST_STR_LEN("var.CWD"), CONST_BUF_LEN(dcwd)); + } + + filename = buffer_init_string(fn); + ret = config_parse_file_stream(srv, &context, filename); + buffer_free(filename); + + /* remains nothing if parser is ok */ + force_assert(!(0 == ret && context.ok && 0 != context.configs_stack.used)); + context_free(&context); + + if (0 != ret) { + return ret; + } + + if (0 != config_insert(srv)) { + return -1; + } + + return 0; +} + +int config_set_defaults(server *srv) { + size_t i; + specific_config *s = srv->config_storage[0]; + struct stat st1, st2; + + force_assert(sizeof(((connection *)0)->conditional_is_valid) >= COMP_LAST_ELEMENT); + + if (0 != fdevent_config(srv)) return -1; + + if (!buffer_string_is_empty(srv->srvconf.changeroot)) { + if (-1 == stat(srv->srvconf.changeroot->ptr, &st1)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "server.chroot doesn't exist:", srv->srvconf.changeroot); + return -1; + } + if (!S_ISDIR(st1.st_mode)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "server.chroot isn't a directory:", srv->srvconf.changeroot); + return -1; + } + } + + if (!srv->srvconf.upload_tempdirs->used) { + const char *tmpdir = getenv("TMPDIR"); + if (NULL == tmpdir) tmpdir = "/var/tmp"; + array_insert_value(srv->srvconf.upload_tempdirs, tmpdir, strlen(tmpdir)); + } + + if (srv->srvconf.upload_tempdirs->used) { + buffer * const b = srv->tmp_buf; + size_t len; + buffer_clear(b); + if (!buffer_string_is_empty(srv->srvconf.changeroot)) { + buffer_copy_buffer(b, srv->srvconf.changeroot); + buffer_append_slash(b); + } + len = buffer_string_length(b); + + for (i = 0; i < srv->srvconf.upload_tempdirs->used; ++i) { + const data_string * const ds = (data_string *)srv->srvconf.upload_tempdirs->data[i]; + buffer_string_set_length(b, len); /*(truncate)*/ + buffer_append_string_buffer(b, ds->value); + if (-1 == stat(b->ptr, &st1)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "server.upload-dirs doesn't exist:", b); + } else if (!S_ISDIR(st1.st_mode)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "server.upload-dirs isn't a directory:", b); + } + } + } + + chunkqueue_set_tempdirs_default( + srv->srvconf.upload_tempdirs, + srv->srvconf.upload_temp_file_size); + + if (buffer_string_is_empty(s->document_root)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "a default document-root has to be set"); + + return -1; + } + + buffer_copy_buffer(srv->tmp_buf, s->document_root); + + buffer_to_lower(srv->tmp_buf); + + if (2 == s->force_lowercase_filenames) { /* user didn't configure it in global section? */ + s->force_lowercase_filenames = 0; /* default to 0 */ + + if (0 == stat(srv->tmp_buf->ptr, &st1)) { + int is_lower = 0; + + is_lower = buffer_is_equal(srv->tmp_buf, s->document_root); + + /* lower-case existed, check upper-case */ + buffer_copy_buffer(srv->tmp_buf, s->document_root); + + buffer_to_upper(srv->tmp_buf); + + /* we have to handle the special case that upper and lower-casing results in the same filename + * as in server.document-root = "/" or "/12345/" */ + + if (is_lower && buffer_is_equal(srv->tmp_buf, s->document_root)) { + /* lower-casing and upper-casing didn't result in + * an other filename, no need to stat(), + * just assume it is case-sensitive. */ + + s->force_lowercase_filenames = 0; + } else if (0 == stat(srv->tmp_buf->ptr, &st2)) { + + /* upper case exists too, doesn't the FS handle this ? */ + + /* upper and lower have the same inode -> case-insensitve FS */ + + if (st1.st_ino == st2.st_ino) { + /* upper and lower have the same inode -> case-insensitve FS */ + + s->force_lowercase_filenames = 1; + } + } + } + } + + if (srv->srvconf.port == 0) { + srv->srvconf.port = s->ssl_enabled ? 443 : 80; + } + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/configfile.h b/data/lighttpd/lighttpd-1.4.53/src/configfile.h new file mode 100644 index 000000000..1cef1209f --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/configfile.h @@ -0,0 +1,140 @@ +#ifndef _CONFIG_PARSER_H_ +#define _CONFIG_PARSER_H_ +#include "first.h" + +#include "base_decls.h" +#include "array.h" +#include "buffer.h" +#include "vector.h" + +/** + * possible compare ops in the configfile parser + */ +typedef enum { + CONFIG_COND_UNSET, + CONFIG_COND_EQ, /** == */ + CONFIG_COND_MATCH, /** =~ */ + CONFIG_COND_NE, /** != */ + CONFIG_COND_NOMATCH, /** !~ */ + CONFIG_COND_ELSE /** (always true if reached) */ +} config_cond_t; + +/** + * possible fields to match against + */ +typedef enum { + COMP_UNSET, + COMP_SERVER_SOCKET, + COMP_HTTP_URL, + COMP_HTTP_HOST, + COMP_HTTP_REFERER, /*(subsumed by COMP_HTTP_REQUEST_HEADER)*/ + COMP_HTTP_USER_AGENT, /*(subsumed by COMP_HTTP_REQUEST_HEADER)*/ + COMP_HTTP_LANGUAGE, /*(subsumed by COMP_HTTP_REQUEST_HEADER)*/ + COMP_HTTP_COOKIE, /*(subsumed by COMP_HTTP_REQUEST_HEADER)*/ + COMP_HTTP_REMOTE_IP, + COMP_HTTP_QUERY_STRING, + COMP_HTTP_SCHEME, + COMP_HTTP_REQUEST_METHOD, + COMP_HTTP_REQUEST_HEADER, + + COMP_LAST_ELEMENT +} comp_key_t; + +/* $HTTP["host"] == "incremental.home.kneschke.de" { ... } + * for print: comp_key op string + * for compare: comp cond string/regex + */ + +#ifdef HAVE_PCRE_H +struct pcre_extra; /* declaration */ +#endif + +typedef struct data_config data_config; +DEFINE_TYPED_VECTOR_NO_RELEASE(config_weak, data_config*); + +struct data_config { + DATA_UNSET; + + array *value; + + buffer *comp_tag; + buffer *comp_key; + comp_key_t comp; + + config_cond_t cond; + buffer *op; + + int context_ndx; /* more or less like an id */ + vector_config_weak children; + /* nested */ + data_config *parent; + /* for chaining only */ + data_config *prev; + data_config *next; + + buffer *string; +#ifdef HAVE_PCRE_H + void *regex; + struct pcre_extra *regex_study; +#endif +}; + +struct cond_cache_t; /* declaration */ + +data_config *data_config_init(void); +int data_config_pcre_compile(data_config *dc); +int data_config_pcre_exec(data_config *dc, struct cond_cache_t *cache, buffer *b); + +typedef struct { + server *srv; + int ok; + array *all_configs; + vector_config_weak configs_stack; /* to parse nested block */ + data_config *current; /* current started with { */ + buffer *basedir; +} config_t; + +int config_read(server *srv, const char *fn); +int config_set_defaults(server *srv); +void *configparserAlloc(void *(*mallocProc)(size_t)); +void configparserFree(void *p, void (*freeProc)(void*)); +void configparser(void *yyp, int yymajor, buffer *yyminor, config_t *ctx); +int config_parse_file(server *srv, config_t *context, const char *fn); +int config_parse_cmd(server *srv, config_t *context, const char *cmd); +data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2); + +int config_setup_connection(server *srv, connection *con); +int config_patch_connection(server *srv, connection *con); + +void config_cond_cache_reset(server *srv, connection *con); +void config_cond_cache_reset_item(server *srv, connection *con, comp_key_t item); + +typedef enum { T_CONFIG_UNSET, + T_CONFIG_STRING, + T_CONFIG_SHORT, + T_CONFIG_INT, + T_CONFIG_BOOLEAN, + T_CONFIG_ARRAY, + T_CONFIG_LOCAL, + T_CONFIG_DEPRECATED, + T_CONFIG_UNSUPPORTED +} config_values_type_t; + +typedef enum { T_CONFIG_SCOPE_UNSET, + T_CONFIG_SCOPE_SERVER, + T_CONFIG_SCOPE_CONNECTION +} config_scope_type_t; + +typedef struct { + const char *key; + void *destination; + + config_values_type_t type; + config_scope_type_t scope; +} config_values_t; + +int config_insert_values_global(server *srv, array *ca, const config_values_t *cv, config_scope_type_t scope); +int config_insert_values_internal(server *srv, array *ca, const config_values_t *cv, config_scope_type_t scope); +int config_check_cond(server *srv, connection *con, data_config *dc); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/configparser.c b/data/lighttpd/lighttpd-1.4.53/src/configparser.c new file mode 100644 index 000000000..5c50db121 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/configparser.c @@ -0,0 +1,1870 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +*/ +/* First off, code is include which follows the "include" declaration +** in the input file. */ +#include "first.h" +#include <stdio.h> +#line 5 "../../src/configparser.y" + +#include "first.h" +#include "base.h" +#include "configfile.h" +#include "buffer.h" +#include "array.h" +#include "request.h" /* http_request_host_normalize() */ + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +static void configparser_push(config_t *ctx, data_config *dc, int isnew) { + if (isnew) { + dc->context_ndx = ctx->all_configs->used; + force_assert(dc->context_ndx > ctx->current->context_ndx); + array_insert_unique(ctx->all_configs, (data_unset *)dc); + dc->parent = ctx->current; + vector_config_weak_push(&dc->parent->children, dc); + } + if (ctx->configs_stack.used > 0 && ctx->current->context_ndx == 0) { + fprintf(stderr, "Cannot use conditionals inside a global { ... } block\n"); + exit(-1); + } + vector_config_weak_push(&ctx->configs_stack, ctx->current); + ctx->current = dc; +} + +static data_config *configparser_pop(config_t *ctx) { + data_config *old = ctx->current; + ctx->current = vector_config_weak_pop(&ctx->configs_stack); + return old; +} + +/* return a copied variable */ +static data_unset *configparser_get_variable(config_t *ctx, const buffer *key) { + data_unset *du; + data_config *dc; + +#if 0 + fprintf(stderr, "get var %s\n", key->ptr); +#endif + for (dc = ctx->current; dc; dc = dc->parent) { +#if 0 + fprintf(stderr, "get var on block: %s\n", dc->key->ptr); + array_print(dc->value, 0); +#endif + if (NULL != (du = array_get_element_klen(dc->value, CONST_BUF_LEN(key)))) { + du = du->fn->copy(du); + buffer_clear(du->key); + return du; + } + } + return NULL; +} + +/* op1 is to be eat/return by this function if success, op1->key is not cared + op2 is left untouch, unreferenced + */ +data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { + /* type mismatch */ + if (op1->type != op2->type) { + if (op1->type == TYPE_STRING && op2->type == TYPE_INTEGER) { + data_string *ds = (data_string *)op1; + buffer_append_int(ds->value, ((data_integer*)op2)->value); + return op1; + } else if (op1->type == TYPE_INTEGER && op2->type == TYPE_STRING) { + data_string *ds = data_string_init(); + buffer_append_int(ds->value, ((data_integer*)op1)->value); + buffer_append_string_buffer(ds->value, ((data_string*)op2)->value); + op1->fn->free(op1); + return (data_unset *)ds; + } else { + fprintf(stderr, "data type mismatch, cannot merge\n"); + op1->fn->free(op1); + return NULL; + } + } + + switch (op1->type) { + case TYPE_STRING: + buffer_append_string_buffer(((data_string *)op1)->value, ((data_string *)op2)->value); + break; + case TYPE_INTEGER: + ((data_integer *)op1)->value += ((data_integer *)op2)->value; + break; + case TYPE_ARRAY: { + array *dst = ((data_array *)op1)->value; + array *src = ((data_array *)op2)->value; + data_unset *du; + size_t i; + + for (i = 0; i < src->used; i ++) { + du = (data_unset *)src->data[i]; + if (du) { + if (du->is_index_key || buffer_is_empty(du->key) || !array_get_element_klen(dst, CONST_BUF_LEN(du->key))) { + array_insert_unique(dst, du->fn->copy(du)); + } else { + fprintf(stderr, "Duplicate array-key '%s'\n", du->key->ptr); + op1->fn->free(op1); + return NULL; + } + } + } + break; + default: + force_assert(0); + break; + } + } + return op1; +} + +static int configparser_remoteip_normalize_compat(buffer *rvalue) { + /* $HTTP["remoteip"] IPv6 accepted with or without '[]' for config compat + * http_request_host_normalize() expects IPv6 with '[]', + * and config processing at runtime expects COMP_HTTP_REMOTE_IP + * compared without '[]', so strip '[]' after normalization */ + buffer *b = buffer_init(); + int rc; + + if (rvalue->ptr[0] != '[') { + buffer_append_string_len(b, CONST_STR_LEN("[")); + buffer_append_string_buffer(b, rvalue); + buffer_append_string_len(b, CONST_STR_LEN("]")); + } else { + buffer_append_string_buffer(b, rvalue); + } + + rc = http_request_host_normalize(b, 0); + + if (0 == rc) { + /* remove surrounding '[]' */ + size_t blen = buffer_string_length(b); + if (blen > 1) buffer_copy_string_len(rvalue, b->ptr+1, blen-2); + } + + buffer_free(b); + return rc; +} + + +#line 154 "./configparser.c" +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** configparserTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is configparserTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. +** configparserARG_SDECL A static variable declaration for the %extra_argument +** configparserARG_PDECL A parameter declaration for the %extra_argument +** configparserARG_STORE Code to store %extra_argument into yypParser +** configparserARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +/* */ +#define YYCODETYPE unsigned char +#define YYNOCODE 51 +#define YYACTIONTYPE unsigned char +#define configparserTOKENTYPE buffer * +typedef union { + configparserTOKENTYPE yy0; + data_config * yy18; + buffer * yy29; + array * yy42; + config_cond_t yy53; + data_unset * yy91; + int yy101; +} YYMINORTYPE; +#define YYSTACKDEPTH 100 +#define configparserARG_SDECL config_t *ctx; +#define configparserARG_PDECL ,config_t *ctx +#define configparserARG_FETCH config_t *ctx = yypParser->ctx +#define configparserARG_STORE yypParser->ctx = ctx +#define YYNSTATE 70 +#define YYNRULE 44 +#define YYERRORSYMBOL 27 +#define YYERRSYMDT yy101 +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* Next are that tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +static YYACTIONTYPE yy_action[] = { + /* 0 */ 2, 3, 4, 5, 14, 15, 70, 16, 7, 48, + /* 10 */ 96, 21, 21, 17, 24, 25, 27, 44, 11, 43, + /* 20 */ 8, 16, 12, 49, 21, 21, 24, 25, 27, 30, + /* 30 */ 20, 61, 63, 43, 13, 65, 111, 49, 57, 58, + /* 40 */ 59, 60, 18, 39, 41, 61, 63, 16, 29, 28, + /* 50 */ 38, 21, 98, 31, 26, 23, 35, 9, 10, 43, + /* 60 */ 36, 47, 20, 49, 45, 16, 11, 66, 22, 21, + /* 70 */ 105, 61, 63, 6, 26, 23, 46, 43, 51, 69, + /* 80 */ 20, 49, 115, 1, 50, 29, 28, 34, 97, 61, + /* 90 */ 63, 26, 23, 35, 29, 19, 29, 52, 32, 33, + /* 100 */ 26, 23, 26, 23, 53, 106, 29, 52, 49, 29, + /* 110 */ 52, 67, 26, 23, 62, 26, 23, 64, 29, 37, + /* 120 */ 54, 55, 29, 40, 26, 23, 29, 42, 26, 23, + /* 130 */ 29, 56, 26, 23, 68, 96, 26, 23, +}; +static YYCODETYPE yy_lookahead[] = { + /* 0 */ 30, 31, 32, 33, 34, 35, 0, 1, 46, 39, + /* 10 */ 16, 5, 5, 43, 7, 8, 9, 47, 48, 13, + /* 20 */ 16, 1, 14, 17, 5, 5, 7, 8, 9, 10, + /* 30 */ 6, 25, 26, 13, 29, 15, 12, 17, 21, 22, + /* 40 */ 23, 24, 2, 3, 4, 25, 26, 1, 36, 37, + /* 50 */ 38, 5, 14, 41, 42, 43, 44, 39, 40, 13, + /* 60 */ 12, 15, 6, 17, 14, 1, 48, 49, 36, 5, + /* 70 */ 14, 25, 26, 1, 42, 43, 29, 13, 19, 15, + /* 80 */ 6, 17, 28, 29, 18, 36, 37, 38, 16, 25, + /* 90 */ 26, 42, 43, 44, 36, 37, 36, 37, 10, 11, + /* 100 */ 42, 43, 42, 43, 44, 14, 36, 37, 17, 36, + /* 110 */ 37, 14, 42, 43, 44, 42, 43, 44, 36, 37, + /* 120 */ 20, 45, 36, 37, 42, 43, 36, 37, 42, 43, + /* 130 */ 36, 37, 42, 43, 29, 50, 42, 43, +}; +#define YY_SHIFT_USE_DFLT (-7) +static signed char yy_shift_ofst[] = { + /* 0 */ -7, 6, -7, -7, -7, 72, -6, 4, 91, -7, + /* 10 */ -7, 8, -7, 20, -7, -7, -7, 40, 7, 74, + /* 20 */ 7, -7, -7, -7, -7, -7, -7, 19, 24, -7, + /* 30 */ -7, 88, -7, 7, -7, 48, 7, 74, -7, 7, + /* 40 */ 74, 7, 74, 38, 50, -7, 46, -7, -7, 66, + /* 50 */ 59, 7, 74, 100, 17, 7, 56, -7, -7, -7, + /* 60 */ -7, 7, -7, 7, -7, -7, 97, -7, 64, -7, +}; +#define YY_REDUCE_USE_DFLT (-39) +static signed char yy_reduce_ofst[] = { + /* 0 */ 54, -30, -39, -39, -39, -38, -39, -39, 18, -39, + /* 10 */ -39, -39, 5, -30, -39, -39, -39, -39, 58, -39, + /* 20 */ 32, -39, -39, -39, -39, -39, -39, 12, -39, -39, + /* 30 */ -39, -39, -39, 49, -39, -39, 82, -39, -39, 86, + /* 40 */ -39, 90, -39, -39, -39, 47, -30, -39, -39, -39, + /* 50 */ -39, 60, -39, -39, 76, 94, -39, -39, -39, -39, + /* 60 */ -39, 70, -39, 73, -39, -39, -39, 105, -30, -39, +}; +static YYACTIONTYPE yy_default[] = { + /* 0 */ 72, 114, 71, 73, 74, 114, 75, 114, 114, 100, + /* 10 */ 101, 114, 72, 114, 76, 77, 78, 114, 114, 79, + /* 20 */ 114, 82, 83, 85, 86, 87, 88, 114, 94, 84, + /* 30 */ 89, 114, 90, 92, 91, 114, 114, 95, 93, 114, + /* 40 */ 80, 114, 81, 114, 114, 72, 114, 99, 102, 114, + /* 50 */ 114, 114, 111, 114, 114, 114, 114, 107, 108, 109, + /* 60 */ 110, 114, 112, 114, 113, 103, 114, 72, 114, 104, +}; +#define YY_SZ_ACTTAB (sizeof(yy_action)/sizeof(yy_action[0])) + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammer, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + int stateno; /* The state-number */ + int major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ + int yyerrcnt; /* Shifts left before out of the error */ + configparserARG_SDECL /* A place to hold %extra_argument */ + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include <stdio.h> +static FILE *yyTraceFILE = NULL; +static char *yyTracePrompt = NULL; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +** <ul> +** <li> A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +** <li> A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +** </ul> +** +** Outputs: +** None. +*/ +#if 0 +void configparserTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *yyTokenName[] = { + "$", "EOL", "ASSIGN", "FORCE_ASSIGN", + "APPEND", "LKEY", "PLUS", "STRING", + "INTEGER", "LPARAN", "RPARAN", "COMMA", + "ARRAY_ASSIGN", "GLOBAL", "LCURLY", "RCURLY", + "ELSE", "DOLLAR", "SRVVARNAME", "LBRACKET", + "RBRACKET", "EQ", "MATCH", "NE", + "NOMATCH", "INCLUDE", "INCLUDE_SHELL", "error", + "input", "metalines", "metaline", "varline", + "global", "condlines", "include", "include_shell", + "value", "expression", "aelement", "condline", + "cond_else", "aelements", "array", "key", + "stringop", "cond", "eols", "globalstart", + "context", "context_else", +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *yyRuleName[] = { + /* 0 */ "input ::= metalines", + /* 1 */ "metalines ::= metalines metaline", + /* 2 */ "metalines ::=", + /* 3 */ "metaline ::= varline", + /* 4 */ "metaline ::= global", + /* 5 */ "metaline ::= condlines EOL", + /* 6 */ "metaline ::= include", + /* 7 */ "metaline ::= include_shell", + /* 8 */ "metaline ::= EOL", + /* 9 */ "varline ::= key ASSIGN expression", + /* 10 */ "varline ::= key FORCE_ASSIGN expression", + /* 11 */ "varline ::= key APPEND expression", + /* 12 */ "key ::= LKEY", + /* 13 */ "expression ::= expression PLUS value", + /* 14 */ "expression ::= value", + /* 15 */ "value ::= key", + /* 16 */ "value ::= STRING", + /* 17 */ "value ::= INTEGER", + /* 18 */ "value ::= array", + /* 19 */ "array ::= LPARAN RPARAN", + /* 20 */ "array ::= LPARAN aelements RPARAN", + /* 21 */ "aelements ::= aelements COMMA aelement", + /* 22 */ "aelements ::= aelements COMMA", + /* 23 */ "aelements ::= aelement", + /* 24 */ "aelement ::= expression", + /* 25 */ "aelement ::= stringop ARRAY_ASSIGN expression", + /* 26 */ "eols ::= EOL", + /* 27 */ "eols ::=", + /* 28 */ "globalstart ::= GLOBAL", + /* 29 */ "global ::= globalstart LCURLY metalines RCURLY", + /* 30 */ "condlines ::= condlines eols ELSE condline", + /* 31 */ "condlines ::= condlines eols ELSE cond_else", + /* 32 */ "condlines ::= condline", + /* 33 */ "condline ::= context LCURLY metalines RCURLY", + /* 34 */ "cond_else ::= context_else LCURLY metalines RCURLY", + /* 35 */ "context ::= DOLLAR SRVVARNAME LBRACKET stringop RBRACKET cond expression", + /* 36 */ "context_else ::=", + /* 37 */ "cond ::= EQ", + /* 38 */ "cond ::= MATCH", + /* 39 */ "cond ::= NE", + /* 40 */ "cond ::= NOMATCH", + /* 41 */ "stringop ::= expression", + /* 42 */ "include ::= INCLUDE stringop", + /* 43 */ "include_shell ::= INCLUDE_SHELL stringop", +}; +#endif /* NDEBUG */ + +/* +** This function returns the symbolic name associated with a token +** value. +*/ +#if 0 +const char *configparserTokenName(int tokenType){ +#ifndef NDEBUG + if( tokenType>0 && (size_t)tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ + return yyTokenName[tokenType]; + }else{ + return "Unknown"; + } +#else + return ""; +#endif +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to configparser and configparserFree. +*/ +void *configparserAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){ + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: +#line 186 "../../src/configparser.y" +{ buffer_free((yypminor->yy0)); } +#line 574 "./configparser.c" + break; + case 36: +#line 177 "../../src/configparser.y" +{ if ((yypminor->yy91)) (yypminor->yy91)->fn->free((yypminor->yy91)); } +#line 579 "./configparser.c" + break; + case 37: +#line 178 "../../src/configparser.y" +{ if ((yypminor->yy91)) (yypminor->yy91)->fn->free((yypminor->yy91)); } +#line 584 "./configparser.c" + break; + case 38: +#line 179 "../../src/configparser.y" +{ if ((yypminor->yy91)) (yypminor->yy91)->fn->free((yypminor->yy91)); } +#line 589 "./configparser.c" + break; + case 41: +#line 180 "../../src/configparser.y" +{ array_free((yypminor->yy42)); } +#line 594 "./configparser.c" + break; + case 42: +#line 181 "../../src/configparser.y" +{ array_free((yypminor->yy42)); } +#line 599 "./configparser.c" + break; + case 43: +#line 182 "../../src/configparser.y" +{ buffer_free((yypminor->yy29)); } +#line 604 "./configparser.c" + break; + case 44: +#line 183 "../../src/configparser.y" +{ buffer_free((yypminor->yy29)); } +#line 609 "./configparser.c" + break; + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos; + + if( pParser->yyidx<0 ) return 0; + yytos = &pParser->yystack[pParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor( yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +** <ul> +** <li> A pointer to the parser. This should be a pointer +** obtained from configparserAlloc. +** <li> A pointer to a function used to reclaim memory obtained +** from malloc. +** </ul> +*/ +void configparserFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + if( pParser==NULL ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); + (*freeProc)((void*)pParser); +} + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + int iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + /* if( pParser->yyidx<0 ) return YY_NO_ACTION; */ + i = yy_shift_ofst[stateno]; + if( i==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + if( iLookAhead==YYNOCODE ){ + return YY_NO_ACTION; + } + i += iLookAhead; + if( i<0 || (size_t)i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ +#ifdef YYFALLBACK + int iFallback; /* Fallback token */ + if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) + && (iFallback = yyFallback[iLookAhead])!=0 ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + yyParser *pParser, /* The parser */ + int iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + i = yy_reduce_ofst[stateno]; + if( i==YY_REDUCE_USE_DFLT ){ + return yy_default[stateno]; + } + if( iLookAhead==YYNOCODE ){ + return YY_NO_ACTION; + } + i += iLookAhead; + if( i<0 || (size_t)i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; + if( yypParser->yyidx>=YYSTACKDEPTH ){ + configparserARG_FETCH; + yypParser->yyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ + configparserARG_STORE; /* Suppress warning about unused %extra_argument var */ + return; + } + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = yyNewState; + yytos->major = yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { + { 28, 1 }, + { 29, 2 }, + { 29, 0 }, + { 30, 1 }, + { 30, 1 }, + { 30, 2 }, + { 30, 1 }, + { 30, 1 }, + { 30, 1 }, + { 31, 3 }, + { 31, 3 }, + { 31, 3 }, + { 43, 1 }, + { 37, 3 }, + { 37, 1 }, + { 36, 1 }, + { 36, 1 }, + { 36, 1 }, + { 36, 1 }, + { 42, 2 }, + { 42, 3 }, + { 41, 3 }, + { 41, 2 }, + { 41, 1 }, + { 38, 1 }, + { 38, 3 }, + { 46, 1 }, + { 46, 0 }, + { 47, 1 }, + { 32, 4 }, + { 33, 4 }, + { 33, 4 }, + { 33, 1 }, + { 39, 4 }, + { 40, 4 }, + { 48, 7 }, + { 49, 0 }, + { 45, 1 }, + { 45, 1 }, + { 45, 1 }, + { 45, 1 }, + { 44, 1 }, + { 34, 2 }, + { 35, 2 }, +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + configparserARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE ) { + if (yyruleno>=0 + && (size_t)yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } else { + return; /*(should not happen)*/ + } + } +#endif /* NDEBUG */ + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line <lineno> <grammarfile> + ** { ... } // User supplied code + ** #line <lineno> <thisfile> + ** break; + */ + case 0: + /* No destructor defined for metalines */ + break; + case 1: + /* No destructor defined for metalines */ + /* No destructor defined for metaline */ + break; + case 2: + break; + case 3: + /* No destructor defined for varline */ + break; + case 4: + /* No destructor defined for global */ + break; + case 5: +#line 159 "../../src/configparser.y" +{ yymsp[-1].minor.yy18 = NULL; } +#line 888 "./configparser.c" + yy_destructor(1,&yymsp[0].minor); + break; + case 6: + /* No destructor defined for include */ + break; + case 7: + /* No destructor defined for include_shell */ + break; + case 8: + yy_destructor(1,&yymsp[0].minor); + break; + case 9: +#line 188 "../../src/configparser.y" +{ + if (ctx->ok) { + buffer_copy_buffer(yymsp[0].minor.yy91->key, yymsp[-2].minor.yy29); + if (strncmp(yymsp[-2].minor.yy29->ptr, "env.", sizeof("env.") - 1) == 0) { + fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, yymsp[-2].minor.yy29->ptr); + ctx->ok = 0; + } else if (NULL == array_get_element_klen(ctx->current->value, CONST_BUF_LEN(yymsp[0].minor.yy91->key))) { + array_insert_unique(ctx->current->value, yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; + } else { + fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, yymsp[0].minor.yy91->key->ptr); + ctx->ok = 0; + } + } + buffer_free(yymsp[-2].minor.yy29); + yymsp[-2].minor.yy29 = NULL; + if (yymsp[0].minor.yy91) yymsp[0].minor.yy91->fn->free(yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; +} +#line 925 "./configparser.c" + yy_destructor(2,&yymsp[-1].minor); + break; + case 10: +#line 212 "../../src/configparser.y" +{ + if (ctx->ok) { + if (strncmp(yymsp[-2].minor.yy29->ptr, "env.", sizeof("env.") - 1) == 0) { + fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, yymsp[-2].minor.yy29->ptr); + ctx->ok = 0; + } else { + buffer_copy_buffer(yymsp[0].minor.yy91->key, yymsp[-2].minor.yy29); + array_replace(ctx->current->value, yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; + } + } + buffer_free(yymsp[-2].minor.yy29); + yymsp[-2].minor.yy29 = NULL; + if (yymsp[0].minor.yy91) yymsp[0].minor.yy91->fn->free(yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; +} +#line 948 "./configparser.c" + yy_destructor(3,&yymsp[-1].minor); + break; + case 11: +#line 231 "../../src/configparser.y" +{ + if (ctx->ok) { + array *vars = ctx->current->value; + data_unset *du; + + if (strncmp(yymsp[-2].minor.yy29->ptr, "env.", sizeof("env.") - 1) == 0) { + fprintf(stderr, "Appending env variable is not supported in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, yymsp[-2].minor.yy29->ptr); + ctx->ok = 0; + } else if (NULL != (du = array_extract_element_klen(vars, CONST_BUF_LEN(yymsp[-2].minor.yy29))) || NULL != (du = configparser_get_variable(ctx, yymsp[-2].minor.yy29))) { + du = configparser_merge_data(du, yymsp[0].minor.yy91); + if (NULL == du) { + ctx->ok = 0; + } + else { + buffer_copy_buffer(du->key, yymsp[-2].minor.yy29); + array_insert_unique(ctx->current->value, du); + } + } else { + buffer_copy_buffer(yymsp[0].minor.yy91->key, yymsp[-2].minor.yy29); + array_insert_unique(ctx->current->value, yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; + } + } + buffer_free(yymsp[-2].minor.yy29); + yymsp[-2].minor.yy29 = NULL; + if (yymsp[0].minor.yy91) yymsp[0].minor.yy91->fn->free(yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; +} +#line 983 "./configparser.c" + yy_destructor(4,&yymsp[-1].minor); + break; + case 12: +#line 262 "../../src/configparser.y" +{ + if (strchr(yymsp[0].minor.yy0->ptr, '.') == NULL) { + yygotominor.yy29 = buffer_init_string("var."); + buffer_append_string_buffer(yygotominor.yy29, yymsp[0].minor.yy0); + } else { + yygotominor.yy29 = yymsp[0].minor.yy0; + yymsp[0].minor.yy0 = NULL; + } + buffer_free(yymsp[0].minor.yy0); + yymsp[0].minor.yy0 = NULL; +} +#line 999 "./configparser.c" + break; + case 13: +#line 274 "../../src/configparser.y" +{ + yygotominor.yy91 = NULL; + if (ctx->ok) { + yygotominor.yy91 = configparser_merge_data(yymsp[-2].minor.yy91, yymsp[0].minor.yy91); + yymsp[-2].minor.yy91 = NULL; + if (NULL == yygotominor.yy91) { + ctx->ok = 0; + } + } + if (yymsp[-2].minor.yy91) yymsp[-2].minor.yy91->fn->free(yymsp[-2].minor.yy91); + yymsp[-2].minor.yy91 = NULL; + if (yymsp[0].minor.yy91) yymsp[0].minor.yy91->fn->free(yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; +} +#line 1017 "./configparser.c" + yy_destructor(6,&yymsp[-1].minor); + break; + case 14: +#line 289 "../../src/configparser.y" +{ + yygotominor.yy91 = yymsp[0].minor.yy91; + yymsp[0].minor.yy91 = NULL; +} +#line 1026 "./configparser.c" + break; + case 15: +#line 294 "../../src/configparser.y" +{ + yygotominor.yy91 = NULL; + if (ctx->ok) { + if (strncmp(yymsp[0].minor.yy29->ptr, "env.", sizeof("env.") - 1) == 0) { + char *env; + + if (NULL != (env = getenv(yymsp[0].minor.yy29->ptr + 4))) { + data_string *ds; + ds = data_string_init(); + buffer_append_string(ds->value, env); + yygotominor.yy91 = (data_unset *)ds; + } + else { + fprintf(stderr, "Undefined env variable: %s\n", yymsp[0].minor.yy29->ptr + 4); + ctx->ok = 0; + } + } else if (NULL == (yygotominor.yy91 = configparser_get_variable(ctx, yymsp[0].minor.yy29))) { + fprintf(stderr, "Undefined config variable: %s\n", yymsp[0].minor.yy29->ptr); + ctx->ok = 0; + } + } + buffer_free(yymsp[0].minor.yy29); + yymsp[0].minor.yy29 = NULL; +} +#line 1054 "./configparser.c" + break; + case 16: +#line 319 "../../src/configparser.y" +{ + buffer *b; + yygotominor.yy91 = (data_unset *)data_string_init(); + b = ((data_string *)(yygotominor.yy91))->value; + buffer_free(b); + ((data_string *)(yygotominor.yy91))->value = yymsp[0].minor.yy0; + yymsp[0].minor.yy0 = NULL; +} +#line 1066 "./configparser.c" + break; + case 17: +#line 328 "../../src/configparser.y" +{ + char *endptr; + yygotominor.yy91 = (data_unset *)data_integer_init(); + errno = 0; + ((data_integer *)(yygotominor.yy91))->value = strtol(yymsp[0].minor.yy0->ptr, &endptr, 10); + /* skip trailing whitespace */ + if (endptr != yymsp[0].minor.yy0->ptr) while (isspace(*endptr)) endptr++; + if (0 != errno || *endptr != '\0') { + fprintf(stderr, "error parsing number: '%s'\n", yymsp[0].minor.yy0->ptr); + ctx->ok = 0; + } + buffer_free(yymsp[0].minor.yy0); + yymsp[0].minor.yy0 = NULL; +} +#line 1084 "./configparser.c" + break; + case 18: +#line 342 "../../src/configparser.y" +{ + yygotominor.yy91 = (data_unset *)data_array_init(); + array_free(((data_array *)(yygotominor.yy91))->value); + ((data_array *)(yygotominor.yy91))->value = yymsp[0].minor.yy42; + yymsp[0].minor.yy42 = NULL; +} +#line 1094 "./configparser.c" + break; + case 19: +#line 348 "../../src/configparser.y" +{ + yygotominor.yy42 = array_init(); +} +#line 1101 "./configparser.c" + yy_destructor(9,&yymsp[-1].minor); + yy_destructor(10,&yymsp[0].minor); + break; + case 20: +#line 351 "../../src/configparser.y" +{ + yygotominor.yy42 = yymsp[-1].minor.yy42; + yymsp[-1].minor.yy42 = NULL; +} +#line 1111 "./configparser.c" + yy_destructor(9,&yymsp[-2].minor); + yy_destructor(10,&yymsp[0].minor); + break; + case 21: +#line 356 "../../src/configparser.y" +{ + yygotominor.yy42 = NULL; + if (ctx->ok) { + if (buffer_is_empty(yymsp[0].minor.yy91->key) || + NULL == array_get_element_klen(yymsp[-2].minor.yy42, CONST_BUF_LEN(yymsp[0].minor.yy91->key))) { + array_insert_unique(yymsp[-2].minor.yy42, yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; + } else { + fprintf(stderr, "Error: duplicate array-key: %s. Please get rid of the duplicate entry.\n", + yymsp[0].minor.yy91->key->ptr); + ctx->ok = 0; + } + + yygotominor.yy42 = yymsp[-2].minor.yy42; + yymsp[-2].minor.yy42 = NULL; + } + array_free(yymsp[-2].minor.yy42); + yymsp[-2].minor.yy42 = NULL; + if (yymsp[0].minor.yy91) yymsp[0].minor.yy91->fn->free(yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; +} +#line 1138 "./configparser.c" + yy_destructor(11,&yymsp[-1].minor); + break; + case 22: +#line 378 "../../src/configparser.y" +{ + yygotominor.yy42 = yymsp[-1].minor.yy42; + yymsp[-1].minor.yy42 = NULL; +} +#line 1147 "./configparser.c" + yy_destructor(11,&yymsp[0].minor); + break; + case 23: +#line 383 "../../src/configparser.y" +{ + yygotominor.yy42 = NULL; + if (ctx->ok) { + yygotominor.yy42 = array_init(); + array_insert_unique(yygotominor.yy42, yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; + } + if (yymsp[0].minor.yy91) yymsp[0].minor.yy91->fn->free(yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; +} +#line 1162 "./configparser.c" + break; + case 24: +#line 394 "../../src/configparser.y" +{ + yygotominor.yy91 = yymsp[0].minor.yy91; + yymsp[0].minor.yy91 = NULL; +} +#line 1170 "./configparser.c" + break; + case 25: +#line 398 "../../src/configparser.y" +{ + yygotominor.yy91 = NULL; + if (ctx->ok) { + buffer_copy_buffer(yymsp[0].minor.yy91->key, yymsp[-2].minor.yy29); + + yygotominor.yy91 = yymsp[0].minor.yy91; + yymsp[0].minor.yy91 = NULL; + } + if (yymsp[0].minor.yy91) yymsp[0].minor.yy91->fn->free(yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; + buffer_free(yymsp[-2].minor.yy29); + yymsp[-2].minor.yy29 = NULL; +} +#line 1187 "./configparser.c" + yy_destructor(12,&yymsp[-1].minor); + break; + case 26: + yy_destructor(1,&yymsp[0].minor); + break; + case 27: + break; + case 28: +#line 415 "../../src/configparser.y" +{ + data_config *dc; + dc = (data_config *)array_get_element_klen(ctx->srv->config_context, CONST_STR_LEN("global")); + force_assert(dc); + configparser_push(ctx, dc, 0); +} +#line 1203 "./configparser.c" + yy_destructor(13,&yymsp[0].minor); + break; + case 29: +#line 422 "../../src/configparser.y" +{ + force_assert(ctx->current); + configparser_pop(ctx); + force_assert(ctx->current); +} +#line 1213 "./configparser.c" + /* No destructor defined for globalstart */ + yy_destructor(14,&yymsp[-2].minor); + /* No destructor defined for metalines */ + yy_destructor(15,&yymsp[0].minor); + break; + case 30: +#line 428 "../../src/configparser.y" +{ + yygotominor.yy18 = NULL; + if (ctx->ok) { + if (yymsp[-3].minor.yy18->context_ndx >= yymsp[0].minor.yy18->context_ndx) { + fprintf(stderr, "unreachable else condition\n"); + ctx->ok = 0; + } + if (yymsp[-3].minor.yy18->cond == CONFIG_COND_ELSE) { + fprintf(stderr, "unreachable condition following else catch-all\n"); + ctx->ok = 0; + } + yymsp[0].minor.yy18->prev = yymsp[-3].minor.yy18; + yymsp[-3].minor.yy18->next = yymsp[0].minor.yy18; + yygotominor.yy18 = yymsp[0].minor.yy18; + } + yymsp[-3].minor.yy18 = NULL; + yymsp[0].minor.yy18 = NULL; +} +#line 1239 "./configparser.c" + /* No destructor defined for eols */ + yy_destructor(16,&yymsp[-1].minor); + break; + case 31: +#line 447 "../../src/configparser.y" +{ + yygotominor.yy18 = NULL; + if (ctx->ok) { + if (yymsp[-3].minor.yy18->context_ndx >= yymsp[0].minor.yy18->context_ndx) { + fprintf(stderr, "unreachable else condition\n"); + ctx->ok = 0; + } + if (yymsp[-3].minor.yy18->cond == CONFIG_COND_ELSE) { + fprintf(stderr, "unreachable condition following else catch-all\n"); + ctx->ok = 0; + } + } + if (ctx->ok) { + size_t pos; + data_config *dc; + dc = (data_config *)array_extract_element_klen(ctx->all_configs, CONST_BUF_LEN(yymsp[0].minor.yy18->key)); + force_assert(yymsp[0].minor.yy18 == dc); + buffer_copy_buffer(yymsp[0].minor.yy18->key, yymsp[-3].minor.yy18->key); + yymsp[0].minor.yy18->comp = yymsp[-3].minor.yy18->comp; + /*buffer_copy_buffer(yymsp[0].minor.yy18->comp_key, yymsp[-3].minor.yy18->comp_key);*/ + /*yymsp[0].minor.yy18->string = buffer_init_buffer(yymsp[-3].minor.yy18->string);*/ + pos = buffer_string_length(yymsp[0].minor.yy18->key)-buffer_string_length(yymsp[-3].minor.yy18->string)-2; + switch(yymsp[-3].minor.yy18->cond) { + case CONFIG_COND_NE: + yymsp[0].minor.yy18->key->ptr[pos] = '='; /* opposite cond */ + /*buffer_copy_string_len(yymsp[0].minor.yy18->op, CONST_STR_LEN("=="));*/ + break; + case CONFIG_COND_EQ: + yymsp[0].minor.yy18->key->ptr[pos] = '!'; /* opposite cond */ + /*buffer_copy_string_len(yymsp[0].minor.yy18->op, CONST_STR_LEN("!="));*/ + break; + case CONFIG_COND_NOMATCH: + yymsp[0].minor.yy18->key->ptr[pos] = '='; /* opposite cond */ + /*buffer_copy_string_len(yymsp[0].minor.yy18->op, CONST_STR_LEN("=~"));*/ + break; + case CONFIG_COND_MATCH: + yymsp[0].minor.yy18->key->ptr[pos] = '!'; /* opposite cond */ + /*buffer_copy_string_len(yymsp[0].minor.yy18->op, CONST_STR_LEN("!~"));*/ + break; + default: /* should not happen; CONFIG_COND_ELSE checked further above */ + force_assert(0); + } + + if (NULL == (dc = (data_config *)array_get_element_klen(ctx->all_configs, CONST_BUF_LEN(yymsp[0].minor.yy18->key)))) { + /* re-insert into ctx->all_configs with new yymsp[0].minor.yy18->key */ + array_insert_unique(ctx->all_configs, (data_unset *)yymsp[0].minor.yy18); + yymsp[0].minor.yy18->prev = yymsp[-3].minor.yy18; + yymsp[-3].minor.yy18->next = yymsp[0].minor.yy18; + } else { + fprintf(stderr, "unreachable else condition\n"); + ctx->ok = 0; + yymsp[0].minor.yy18->fn->free((data_unset *)yymsp[0].minor.yy18); + yymsp[0].minor.yy18 = dc; + } + + yygotominor.yy18 = yymsp[0].minor.yy18; + } + yymsp[-3].minor.yy18 = NULL; + yymsp[0].minor.yy18 = NULL; +} +#line 1305 "./configparser.c" + /* No destructor defined for eols */ + yy_destructor(16,&yymsp[-1].minor); + break; + case 32: +#line 508 "../../src/configparser.y" +{ + yygotominor.yy18 = yymsp[0].minor.yy18; + yymsp[0].minor.yy18 = NULL; +} +#line 1315 "./configparser.c" + break; + case 33: +#line 513 "../../src/configparser.y" +{ + yygotominor.yy18 = NULL; + if (ctx->ok) { + data_config *cur; + + cur = ctx->current; + configparser_pop(ctx); + + force_assert(cur && ctx->current); + + yygotominor.yy18 = cur; + } +} +#line 1332 "./configparser.c" + /* No destructor defined for context */ + yy_destructor(14,&yymsp[-2].minor); + /* No destructor defined for metalines */ + yy_destructor(15,&yymsp[0].minor); + break; + case 34: +#line 527 "../../src/configparser.y" +{ + yygotominor.yy18 = NULL; + if (ctx->ok) { + data_config *cur; + + cur = ctx->current; + configparser_pop(ctx); + + force_assert(cur && ctx->current); + + yygotominor.yy18 = cur; + } +} +#line 1353 "./configparser.c" + /* No destructor defined for context_else */ + yy_destructor(14,&yymsp[-2].minor); + /* No destructor defined for metalines */ + yy_destructor(15,&yymsp[0].minor); + break; + case 35: +#line 541 "../../src/configparser.y" +{ + data_config *dc; + buffer *b = NULL, *rvalue, *op = NULL; + + if (ctx->ok && yymsp[0].minor.yy91->type != TYPE_STRING) { + fprintf(stderr, "rvalue must be string"); + ctx->ok = 0; + } + + if (ctx->ok) { + switch(yymsp[-1].minor.yy53) { + case CONFIG_COND_NE: + op = buffer_init_string("!="); + break; + case CONFIG_COND_EQ: + op = buffer_init_string("=="); + break; + case CONFIG_COND_NOMATCH: + op = buffer_init_string("!~"); + break; + case CONFIG_COND_MATCH: + op = buffer_init_string("=~"); + break; + default: + force_assert(0); + return; /* unreachable */ + } + + b = buffer_init(); + buffer_copy_buffer(b, ctx->current->key); + buffer_append_string_len(b, CONST_STR_LEN("/")); + buffer_append_string_buffer(b, yymsp[-5].minor.yy0); + buffer_append_string_buffer(b, yymsp[-3].minor.yy29); + buffer_append_string_buffer(b, op); + rvalue = ((data_string*)yymsp[0].minor.yy91)->value; + buffer_append_string_buffer(b, rvalue); + + if (NULL != (dc = (data_config *)array_get_element_klen(ctx->all_configs, CONST_BUF_LEN(b)))) { + configparser_push(ctx, dc, 0); + } else { + static const struct { + comp_key_t comp; + char *comp_key; + size_t len; + } comps[] = { + { COMP_SERVER_SOCKET, CONST_STR_LEN("SERVER[\"socket\"]" ) }, + { COMP_HTTP_URL, CONST_STR_LEN("HTTP[\"url\"]" ) }, + { COMP_HTTP_HOST, CONST_STR_LEN("HTTP[\"host\"]" ) }, + { COMP_HTTP_REQUEST_HEADER,CONST_STR_LEN("HTTP[\"referer\"]" ) }, + { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) }, + { COMP_HTTP_REQUEST_HEADER,CONST_STR_LEN("HTTP[\"user-agent\"]" ) }, + { COMP_HTTP_LANGUAGE, CONST_STR_LEN("HTTP[\"language\"]" ) }, + { COMP_HTTP_REQUEST_HEADER,CONST_STR_LEN("HTTP[\"cookie\"]" ) }, + { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) }, + { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remote-ip\"]" ) }, + { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"querystring\"]") }, + { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"query-string\"]") }, + { COMP_HTTP_REQUEST_METHOD, CONST_STR_LEN("HTTP[\"request-method\"]") }, + { COMP_HTTP_SCHEME, CONST_STR_LEN("HTTP[\"scheme\"]" ) }, + { COMP_UNSET, NULL, 0 }, + }; + size_t i; + + dc = data_config_init(); + + buffer_copy_buffer(dc->key, b); + buffer_copy_buffer(dc->op, op); + buffer_copy_buffer(dc->comp_tag, yymsp[-3].minor.yy29); + buffer_copy_buffer(dc->comp_key, yymsp[-5].minor.yy0); + buffer_append_string_len(dc->comp_key, CONST_STR_LEN("[\"")); + buffer_append_string_buffer(dc->comp_key, yymsp[-3].minor.yy29); + buffer_append_string_len(dc->comp_key, CONST_STR_LEN("\"]")); + dc->cond = yymsp[-1].minor.yy53; + + for (i = 0; comps[i].comp_key; i ++) { + if (buffer_is_equal_string( + dc->comp_key, comps[i].comp_key, comps[i].len)) { + dc->comp = comps[i].comp; + break; + } + } + if (COMP_UNSET == dc->comp) { + if (buffer_is_equal_string(yymsp[-5].minor.yy0, CONST_STR_LEN("REQUEST_HEADER"))) { + dc->comp = COMP_HTTP_REQUEST_HEADER; + } + else { + fprintf(stderr, "error comp_key %s", dc->comp_key->ptr); + ctx->ok = 0; + } + } + else if (COMP_HTTP_LANGUAGE == dc->comp) { + dc->comp = COMP_HTTP_REQUEST_HEADER; + buffer_copy_string_len(dc->comp_tag, CONST_STR_LEN("Accept-Language")); + } + else if (COMP_HTTP_USER_AGENT == dc->comp) { + dc->comp = COMP_HTTP_REQUEST_HEADER; + buffer_copy_string_len(dc->comp_tag, CONST_STR_LEN("User-Agent")); + } + else if (COMP_HTTP_REMOTE_IP == dc->comp + && (dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE)) { + char * const slash = strchr(rvalue->ptr, '/'); /* CIDR mask */ + char * const colon = strchr(rvalue->ptr, ':'); /* IPv6 */ + if (NULL != slash && slash == rvalue->ptr){/*(skip AF_UNIX /path/file)*/ + } + else if (NULL != slash) { + char *nptr; + const unsigned long nm_bits = strtoul(slash + 1, &nptr, 10); + if (*nptr || 0 == nm_bits || nm_bits > (NULL != colon ? 128 : 32)) { + /*(also rejects (slash+1 == nptr) which results in nm_bits = 0)*/ + fprintf(stderr, "invalid or missing netmask: %s\n", rvalue->ptr); + ctx->ok = 0; + } + else { + int rc; + buffer_string_set_length(rvalue, (size_t)(slash - rvalue->ptr)); /*(truncate)*/ + rc = (NULL == colon) + ? http_request_host_normalize(rvalue, 0) + : configparser_remoteip_normalize_compat(rvalue); + buffer_append_string_len(rvalue, CONST_STR_LEN("/")); + buffer_append_int(rvalue, (int)nm_bits); + if (0 != rc) { + fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr); + ctx->ok = 0; + } + } + } + else { + int rc = (NULL == colon) + ? http_request_host_normalize(rvalue, 0) + : configparser_remoteip_normalize_compat(rvalue); + if (0 != rc) { + fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr); + ctx->ok = 0; + } + } + } + else if (COMP_SERVER_SOCKET == dc->comp) { + /*(redundant with parsing in network.c; not actually required here)*/ + if (rvalue->ptr[0] != ':' /*(network.c special-cases ":" and "[]")*/ + && !(rvalue->ptr[0] == '[' && rvalue->ptr[1] == ']')) { + if (http_request_host_normalize(rvalue, 0)) { + fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr); + ctx->ok = 0; + } + } + } + else if (COMP_HTTP_HOST == dc->comp) { + if (dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE) { + if (http_request_host_normalize(rvalue, 0)) { + fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr); + ctx->ok = 0; + } + } + } + + dc->string = buffer_init_buffer(rvalue); + + if (ctx->ok) switch(yymsp[-1].minor.yy53) { + case CONFIG_COND_NE: + case CONFIG_COND_EQ: + break; + case CONFIG_COND_NOMATCH: + case CONFIG_COND_MATCH: { + if (!data_config_pcre_compile(dc)) { + ctx->ok = 0; + } + break; + } + + default: + fprintf(stderr, "unknown condition for $%s[%s]\n", + yymsp[-5].minor.yy0->ptr, yymsp[-3].minor.yy29->ptr); + ctx->ok = 0; + break; + } + + if (ctx->ok) { + configparser_push(ctx, dc, 1); + } else { + dc->fn->free((data_unset*) dc); + } + } + } + + buffer_free(b); + buffer_free(op); + buffer_free(yymsp[-5].minor.yy0); + yymsp[-5].minor.yy0 = NULL; + buffer_free(yymsp[-3].minor.yy29); + yymsp[-3].minor.yy29 = NULL; + if (yymsp[0].minor.yy91) yymsp[0].minor.yy91->fn->free(yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; +} +#line 1554 "./configparser.c" + yy_destructor(17,&yymsp[-6].minor); + yy_destructor(19,&yymsp[-4].minor); + yy_destructor(20,&yymsp[-2].minor); + break; + case 36: +#line 735 "../../src/configparser.y" +{ + if (ctx->ok) { + data_config *dc = data_config_init(); + buffer_copy_buffer(dc->key, ctx->current->key); + buffer_append_string_len(dc->key, CONST_STR_LEN("/")); + buffer_append_string_len(dc->key, CONST_STR_LEN("else_tmp_token")); + dc->cond = CONFIG_COND_ELSE; + configparser_push(ctx, dc, 1); + } +} +#line 1571 "./configparser.c" + break; + case 37: +#line 746 "../../src/configparser.y" +{ + yygotominor.yy53 = CONFIG_COND_EQ; +} +#line 1578 "./configparser.c" + yy_destructor(21,&yymsp[0].minor); + break; + case 38: +#line 749 "../../src/configparser.y" +{ + yygotominor.yy53 = CONFIG_COND_MATCH; +} +#line 1586 "./configparser.c" + yy_destructor(22,&yymsp[0].minor); + break; + case 39: +#line 752 "../../src/configparser.y" +{ + yygotominor.yy53 = CONFIG_COND_NE; +} +#line 1594 "./configparser.c" + yy_destructor(23,&yymsp[0].minor); + break; + case 40: +#line 755 "../../src/configparser.y" +{ + yygotominor.yy53 = CONFIG_COND_NOMATCH; +} +#line 1602 "./configparser.c" + yy_destructor(24,&yymsp[0].minor); + break; + case 41: +#line 759 "../../src/configparser.y" +{ + yygotominor.yy29 = NULL; + if (ctx->ok) { + if (yymsp[0].minor.yy91->type == TYPE_STRING) { + yygotominor.yy29 = ((data_string*)yymsp[0].minor.yy91)->value; + ((data_string*)yymsp[0].minor.yy91)->value = NULL; + } else if (yymsp[0].minor.yy91->type == TYPE_INTEGER) { + yygotominor.yy29 = buffer_init(); + buffer_copy_int(yygotominor.yy29, ((data_integer *)yymsp[0].minor.yy91)->value); + } else { + fprintf(stderr, "operand must be string"); + ctx->ok = 0; + } + } + if (yymsp[0].minor.yy91) yymsp[0].minor.yy91->fn->free(yymsp[0].minor.yy91); + yymsp[0].minor.yy91 = NULL; +} +#line 1624 "./configparser.c" + break; + case 42: +#line 777 "../../src/configparser.y" +{ + if (ctx->ok) { + if (0 != config_parse_file(ctx->srv, ctx, yymsp[0].minor.yy29->ptr)) { + ctx->ok = 0; + } + } + buffer_free(yymsp[0].minor.yy29); + yymsp[0].minor.yy29 = NULL; +} +#line 1637 "./configparser.c" + yy_destructor(25,&yymsp[-1].minor); + break; + case 43: +#line 787 "../../src/configparser.y" +{ + if (ctx->ok) { + if (0 != config_parse_cmd(ctx->srv, ctx, yymsp[0].minor.yy29->ptr)) { + ctx->ok = 0; + } + } + buffer_free(yymsp[0].minor.yy29); + yymsp[0].minor.yy29 = NULL; +} +#line 1651 "./configparser.c" + yy_destructor(26,&yymsp[-1].minor); + break; + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yypParser,yygoto); + if( yyact < YYNSTATE ){ + yy_shift(yypParser,yyact,yygoto,&yygotominor); + }else if( yyact == YYNSTATE + YYNRULE + 1 ){ + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + configparserARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ +#line 150 "../../src/configparser.y" + + ctx->ok = 0; + +#line 1685 "./configparser.c" + configparserARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + configparserARG_FETCH; + UNUSED(yymajor); + UNUSED(yyminor); +#define TOKEN (yyminor.yy0) + configparserARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + configparserARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ + configparserARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "configparserAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +** <ul> +** <li> A pointer to the parser (an opaque structure.) +** <li> The major token number. +** <li> The minor token number. +** <li> An option argument of a grammar-specified type. +** </ul> +** +** Outputs: +** None. +*/ +void configparser( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + configparserTOKENTYPE yyminor /* The value for the token */ + configparserARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ + int yyerrorhit = 0; /* True if yymajor has invoked an error */ + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ + if( yymajor==0 ) return; + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + configparserARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,yymajor); + if( yyact<YYNSTATE ){ + yy_shift(yypParser,yyact,yymajor,&yyminorunion); + yypParser->yyerrcnt--; + if( yyendofinput && yypParser->yyidx>=0 ){ + yymajor = 0; + }else{ + yymajor = YYNOCODE; + } + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else if( yyact == YY_ERROR_ACTION ){ + int yymx; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_shift_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + }else{ + yy_accept(yypParser); + yymajor = YYNOCODE; + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/configparser.h b/data/lighttpd/lighttpd-1.4.53/src/configparser.h new file mode 100644 index 000000000..b052c01fd --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/configparser.h @@ -0,0 +1,26 @@ +#define TK_EOL 1 +#define TK_ASSIGN 2 +#define TK_FORCE_ASSIGN 3 +#define TK_APPEND 4 +#define TK_LKEY 5 +#define TK_PLUS 6 +#define TK_STRING 7 +#define TK_INTEGER 8 +#define TK_LPARAN 9 +#define TK_RPARAN 10 +#define TK_COMMA 11 +#define TK_ARRAY_ASSIGN 12 +#define TK_GLOBAL 13 +#define TK_LCURLY 14 +#define TK_RCURLY 15 +#define TK_ELSE 16 +#define TK_DOLLAR 17 +#define TK_SRVVARNAME 18 +#define TK_LBRACKET 19 +#define TK_RBRACKET 20 +#define TK_EQ 21 +#define TK_MATCH 22 +#define TK_NE 23 +#define TK_NOMATCH 24 +#define TK_INCLUDE 25 +#define TK_INCLUDE_SHELL 26 diff --git a/data/lighttpd/lighttpd-1.4.53/src/configparser.y b/data/lighttpd/lighttpd-1.4.53/src/configparser.y new file mode 100644 index 000000000..9e49ad733 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/configparser.y @@ -0,0 +1,795 @@ +%token_prefix TK_ +%extra_argument {config_t *ctx} +%name configparser + +%include { +#include "first.h" +#include "base.h" +#include "configfile.h" +#include "buffer.h" +#include "array.h" +#include "request.h" /* http_request_host_normalize() */ + +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +static void configparser_push(config_t *ctx, data_config *dc, int isnew) { + if (isnew) { + dc->context_ndx = ctx->all_configs->used; + force_assert(dc->context_ndx > ctx->current->context_ndx); + array_insert_unique(ctx->all_configs, (data_unset *)dc); + dc->parent = ctx->current; + vector_config_weak_push(&dc->parent->children, dc); + } + if (ctx->configs_stack.used > 0 && ctx->current->context_ndx == 0) { + fprintf(stderr, "Cannot use conditionals inside a global { ... } block\n"); + exit(-1); + } + vector_config_weak_push(&ctx->configs_stack, ctx->current); + ctx->current = dc; +} + +static data_config *configparser_pop(config_t *ctx) { + data_config *old = ctx->current; + ctx->current = vector_config_weak_pop(&ctx->configs_stack); + return old; +} + +/* return a copied variable */ +static data_unset *configparser_get_variable(config_t *ctx, const buffer *key) { + data_unset *du; + data_config *dc; + +#if 0 + fprintf(stderr, "get var %s\n", key->ptr); +#endif + for (dc = ctx->current; dc; dc = dc->parent) { +#if 0 + fprintf(stderr, "get var on block: %s\n", dc->key->ptr); + array_print(dc->value, 0); +#endif + if (NULL != (du = array_get_element_klen(dc->value, CONST_BUF_LEN(key)))) { + du = du->fn->copy(du); + buffer_clear(du->key); + return du; + } + } + return NULL; +} + +/* op1 is to be eat/return by this function if success, op1->key is not cared + op2 is left untouch, unreferenced + */ +data_unset *configparser_merge_data(data_unset *op1, const data_unset *op2) { + /* type mismatch */ + if (op1->type != op2->type) { + if (op1->type == TYPE_STRING && op2->type == TYPE_INTEGER) { + data_string *ds = (data_string *)op1; + buffer_append_int(ds->value, ((data_integer*)op2)->value); + return op1; + } else if (op1->type == TYPE_INTEGER && op2->type == TYPE_STRING) { + data_string *ds = data_string_init(); + buffer_append_int(ds->value, ((data_integer*)op1)->value); + buffer_append_string_buffer(ds->value, ((data_string*)op2)->value); + op1->fn->free(op1); + return (data_unset *)ds; + } else { + fprintf(stderr, "data type mismatch, cannot merge\n"); + op1->fn->free(op1); + return NULL; + } + } + + switch (op1->type) { + case TYPE_STRING: + buffer_append_string_buffer(((data_string *)op1)->value, ((data_string *)op2)->value); + break; + case TYPE_INTEGER: + ((data_integer *)op1)->value += ((data_integer *)op2)->value; + break; + case TYPE_ARRAY: { + array *dst = ((data_array *)op1)->value; + array *src = ((data_array *)op2)->value; + data_unset *du; + size_t i; + + for (i = 0; i < src->used; i ++) { + du = (data_unset *)src->data[i]; + if (du) { + if (du->is_index_key || buffer_is_empty(du->key) || !array_get_element_klen(dst, CONST_BUF_LEN(du->key))) { + array_insert_unique(dst, du->fn->copy(du)); + } else { + fprintf(stderr, "Duplicate array-key '%s'\n", du->key->ptr); + op1->fn->free(op1); + return NULL; + } + } + } + break; + default: + force_assert(0); + break; + } + } + return op1; +} + +static int configparser_remoteip_normalize_compat(buffer *rvalue) { + /* $HTTP["remoteip"] IPv6 accepted with or without '[]' for config compat + * http_request_host_normalize() expects IPv6 with '[]', + * and config processing at runtime expects COMP_HTTP_REMOTE_IP + * compared without '[]', so strip '[]' after normalization */ + buffer *b = buffer_init(); + int rc; + + if (rvalue->ptr[0] != '[') { + buffer_append_string_len(b, CONST_STR_LEN("[")); + buffer_append_string_buffer(b, rvalue); + buffer_append_string_len(b, CONST_STR_LEN("]")); + } else { + buffer_append_string_buffer(b, rvalue); + } + + rc = http_request_host_normalize(b, 0); + + if (0 == rc) { + /* remove surrounding '[]' */ + size_t blen = buffer_string_length(b); + if (blen > 1) buffer_copy_string_len(rvalue, b->ptr+1, blen-2); + } + + buffer_free(b); + return rc; +} + +} + +%parse_failure { + ctx->ok = 0; +} + +input ::= metalines. +metalines ::= metalines metaline. +metalines ::= . +metaline ::= varline. +metaline ::= global. +metaline ::= condlines(A) EOL. { A = NULL; } +metaline ::= include. +metaline ::= include_shell. +metaline ::= EOL. + +%type value {data_unset *} +%type expression {data_unset *} +%type aelement {data_unset *} +%type condline {data_config *} +%type cond_else {data_config *} +%type condlines {data_config *} +%type aelements {array *} +%type array {array *} +%type key {buffer *} +%type stringop {buffer *} + +%type cond {config_cond_t } + +%destructor value { if ($$) $$->fn->free($$); } +%destructor expression { if ($$) $$->fn->free($$); } +%destructor aelement { if ($$) $$->fn->free($$); } +%destructor aelements { array_free($$); } +%destructor array { array_free($$); } +%destructor key { buffer_free($$); } +%destructor stringop { buffer_free($$); } + +%token_type {buffer *} +%token_destructor { buffer_free($$); } + +varline ::= key(A) ASSIGN expression(B). { + if (ctx->ok) { + buffer_copy_buffer(B->key, A); + if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) { + fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, A->ptr); + ctx->ok = 0; + } else if (NULL == array_get_element_klen(ctx->current->value, CONST_BUF_LEN(B->key))) { + array_insert_unique(ctx->current->value, B); + B = NULL; + } else { + fprintf(stderr, "Duplicate config variable in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, B->key->ptr); + ctx->ok = 0; + } + } + buffer_free(A); + A = NULL; + if (B) B->fn->free(B); + B = NULL; +} + +varline ::= key(A) FORCE_ASSIGN expression(B). { + if (ctx->ok) { + if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) { + fprintf(stderr, "Setting env variable is not supported in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, A->ptr); + ctx->ok = 0; + } else { + buffer_copy_buffer(B->key, A); + array_replace(ctx->current->value, B); + B = NULL; + } + } + buffer_free(A); + A = NULL; + if (B) B->fn->free(B); + B = NULL; +} + +varline ::= key(A) APPEND expression(B). { + if (ctx->ok) { + array *vars = ctx->current->value; + data_unset *du; + + if (strncmp(A->ptr, "env.", sizeof("env.") - 1) == 0) { + fprintf(stderr, "Appending env variable is not supported in conditional %d %s: %s\n", + ctx->current->context_ndx, + ctx->current->key->ptr, A->ptr); + ctx->ok = 0; + } else if (NULL != (du = array_extract_element_klen(vars, CONST_BUF_LEN(A))) || NULL != (du = configparser_get_variable(ctx, A))) { + du = configparser_merge_data(du, B); + if (NULL == du) { + ctx->ok = 0; + } + else { + buffer_copy_buffer(du->key, A); + array_insert_unique(ctx->current->value, du); + } + } else { + buffer_copy_buffer(B->key, A); + array_insert_unique(ctx->current->value, B); + B = NULL; + } + } + buffer_free(A); + A = NULL; + if (B) B->fn->free(B); + B = NULL; +} + +key(A) ::= LKEY(B). { + if (strchr(B->ptr, '.') == NULL) { + A = buffer_init_string("var."); + buffer_append_string_buffer(A, B); + } else { + A = B; + B = NULL; + } + buffer_free(B); + B = NULL; +} + +expression(A) ::= expression(B) PLUS value(C). { + A = NULL; + if (ctx->ok) { + A = configparser_merge_data(B, C); + B = NULL; + if (NULL == A) { + ctx->ok = 0; + } + } + if (B) B->fn->free(B); + B = NULL; + if (C) C->fn->free(C); + C = NULL; +} + +expression(A) ::= value(B). { + A = B; + B = NULL; +} + +value(A) ::= key(B). { + A = NULL; + if (ctx->ok) { + if (strncmp(B->ptr, "env.", sizeof("env.") - 1) == 0) { + char *env; + + if (NULL != (env = getenv(B->ptr + 4))) { + data_string *ds; + ds = data_string_init(); + buffer_append_string(ds->value, env); + A = (data_unset *)ds; + } + else { + fprintf(stderr, "Undefined env variable: %s\n", B->ptr + 4); + ctx->ok = 0; + } + } else if (NULL == (A = configparser_get_variable(ctx, B))) { + fprintf(stderr, "Undefined config variable: %s\n", B->ptr); + ctx->ok = 0; + } + } + buffer_free(B); + B = NULL; +} + +value(A) ::= STRING(B). { + buffer *b; + A = (data_unset *)data_string_init(); + b = ((data_string *)(A))->value; + buffer_free(b); + ((data_string *)(A))->value = B; + B = NULL; +} + +value(A) ::= INTEGER(B). { + char *endptr; + A = (data_unset *)data_integer_init(); + errno = 0; + ((data_integer *)(A))->value = strtol(B->ptr, &endptr, 10); + /* skip trailing whitespace */ + if (endptr != B->ptr) while (isspace(*endptr)) endptr++; + if (0 != errno || *endptr != '\0') { + fprintf(stderr, "error parsing number: '%s'\n", B->ptr); + ctx->ok = 0; + } + buffer_free(B); + B = NULL; +} +value(A) ::= array(B). { + A = (data_unset *)data_array_init(); + array_free(((data_array *)(A))->value); + ((data_array *)(A))->value = B; + B = NULL; +} +array(A) ::= LPARAN RPARAN. { + A = array_init(); +} +array(A) ::= LPARAN aelements(B) RPARAN. { + A = B; + B = NULL; +} + +aelements(A) ::= aelements(C) COMMA aelement(B). { + A = NULL; + if (ctx->ok) { + if (buffer_is_empty(B->key) || + NULL == array_get_element_klen(C, CONST_BUF_LEN(B->key))) { + array_insert_unique(C, B); + B = NULL; + } else { + fprintf(stderr, "Error: duplicate array-key: %s. Please get rid of the duplicate entry.\n", + B->key->ptr); + ctx->ok = 0; + } + + A = C; + C = NULL; + } + array_free(C); + C = NULL; + if (B) B->fn->free(B); + B = NULL; +} + +aelements(A) ::= aelements(C) COMMA. { + A = C; + C = NULL; +} + +aelements(A) ::= aelement(B). { + A = NULL; + if (ctx->ok) { + A = array_init(); + array_insert_unique(A, B); + B = NULL; + } + if (B) B->fn->free(B); + B = NULL; +} + +aelement(A) ::= expression(B). { + A = B; + B = NULL; +} +aelement(A) ::= stringop(B) ARRAY_ASSIGN expression(C). { + A = NULL; + if (ctx->ok) { + buffer_copy_buffer(C->key, B); + + A = C; + C = NULL; + } + if (C) C->fn->free(C); + C = NULL; + buffer_free(B); + B = NULL; +} + +eols ::= EOL. +eols ::= . + +globalstart ::= GLOBAL. { + data_config *dc; + dc = (data_config *)array_get_element_klen(ctx->srv->config_context, CONST_STR_LEN("global")); + force_assert(dc); + configparser_push(ctx, dc, 0); +} + +global ::= globalstart LCURLY metalines RCURLY. { + force_assert(ctx->current); + configparser_pop(ctx); + force_assert(ctx->current); +} + +condlines(A) ::= condlines(B) eols ELSE condline(C). { + A = NULL; + if (ctx->ok) { + if (B->context_ndx >= C->context_ndx) { + fprintf(stderr, "unreachable else condition\n"); + ctx->ok = 0; + } + if (B->cond == CONFIG_COND_ELSE) { + fprintf(stderr, "unreachable condition following else catch-all\n"); + ctx->ok = 0; + } + C->prev = B; + B->next = C; + A = C; + } + B = NULL; + C = NULL; +} + +condlines(A) ::= condlines(B) eols ELSE cond_else(C). { + A = NULL; + if (ctx->ok) { + if (B->context_ndx >= C->context_ndx) { + fprintf(stderr, "unreachable else condition\n"); + ctx->ok = 0; + } + if (B->cond == CONFIG_COND_ELSE) { + fprintf(stderr, "unreachable condition following else catch-all\n"); + ctx->ok = 0; + } + } + if (ctx->ok) { + size_t pos; + data_config *dc; + dc = (data_config *)array_extract_element_klen(ctx->all_configs, CONST_BUF_LEN(C->key)); + force_assert(C == dc); + buffer_copy_buffer(C->key, B->key); + C->comp = B->comp; + /*buffer_copy_buffer(C->comp_key, B->comp_key);*/ + /*C->string = buffer_init_buffer(B->string);*/ + pos = buffer_string_length(C->key)-buffer_string_length(B->string)-2; + switch(B->cond) { + case CONFIG_COND_NE: + C->key->ptr[pos] = '='; /* opposite cond */ + /*buffer_copy_string_len(C->op, CONST_STR_LEN("=="));*/ + break; + case CONFIG_COND_EQ: + C->key->ptr[pos] = '!'; /* opposite cond */ + /*buffer_copy_string_len(C->op, CONST_STR_LEN("!="));*/ + break; + case CONFIG_COND_NOMATCH: + C->key->ptr[pos] = '='; /* opposite cond */ + /*buffer_copy_string_len(C->op, CONST_STR_LEN("=~"));*/ + break; + case CONFIG_COND_MATCH: + C->key->ptr[pos] = '!'; /* opposite cond */ + /*buffer_copy_string_len(C->op, CONST_STR_LEN("!~"));*/ + break; + default: /* should not happen; CONFIG_COND_ELSE checked further above */ + force_assert(0); + } + + if (NULL == (dc = (data_config *)array_get_element_klen(ctx->all_configs, CONST_BUF_LEN(C->key)))) { + /* re-insert into ctx->all_configs with new C->key */ + array_insert_unique(ctx->all_configs, (data_unset *)C); + C->prev = B; + B->next = C; + } else { + fprintf(stderr, "unreachable else condition\n"); + ctx->ok = 0; + C->fn->free((data_unset *)C); + C = dc; + } + + A = C; + } + B = NULL; + C = NULL; +} + +condlines(A) ::= condline(B). { + A = B; + B = NULL; +} + +condline(A) ::= context LCURLY metalines RCURLY. { + A = NULL; + if (ctx->ok) { + data_config *cur; + + cur = ctx->current; + configparser_pop(ctx); + + force_assert(cur && ctx->current); + + A = cur; + } +} + +cond_else(A) ::= context_else LCURLY metalines RCURLY. { + A = NULL; + if (ctx->ok) { + data_config *cur; + + cur = ctx->current; + configparser_pop(ctx); + + force_assert(cur && ctx->current); + + A = cur; + } +} + +context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expression(D). { + data_config *dc; + buffer *b = NULL, *rvalue, *op = NULL; + + if (ctx->ok && D->type != TYPE_STRING) { + fprintf(stderr, "rvalue must be string"); + ctx->ok = 0; + } + + if (ctx->ok) { + switch(E) { + case CONFIG_COND_NE: + op = buffer_init_string("!="); + break; + case CONFIG_COND_EQ: + op = buffer_init_string("=="); + break; + case CONFIG_COND_NOMATCH: + op = buffer_init_string("!~"); + break; + case CONFIG_COND_MATCH: + op = buffer_init_string("=~"); + break; + default: + force_assert(0); + return; /* unreachable */ + } + + b = buffer_init(); + buffer_copy_buffer(b, ctx->current->key); + buffer_append_string_len(b, CONST_STR_LEN("/")); + buffer_append_string_buffer(b, B); + buffer_append_string_buffer(b, C); + buffer_append_string_buffer(b, op); + rvalue = ((data_string*)D)->value; + buffer_append_string_buffer(b, rvalue); + + if (NULL != (dc = (data_config *)array_get_element_klen(ctx->all_configs, CONST_BUF_LEN(b)))) { + configparser_push(ctx, dc, 0); + } else { + static const struct { + comp_key_t comp; + char *comp_key; + size_t len; + } comps[] = { + { COMP_SERVER_SOCKET, CONST_STR_LEN("SERVER[\"socket\"]" ) }, + { COMP_HTTP_URL, CONST_STR_LEN("HTTP[\"url\"]" ) }, + { COMP_HTTP_HOST, CONST_STR_LEN("HTTP[\"host\"]" ) }, + { COMP_HTTP_REQUEST_HEADER,CONST_STR_LEN("HTTP[\"referer\"]" ) }, + { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) }, + { COMP_HTTP_REQUEST_HEADER,CONST_STR_LEN("HTTP[\"user-agent\"]" ) }, + { COMP_HTTP_LANGUAGE, CONST_STR_LEN("HTTP[\"language\"]" ) }, + { COMP_HTTP_REQUEST_HEADER,CONST_STR_LEN("HTTP[\"cookie\"]" ) }, + { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remoteip\"]" ) }, + { COMP_HTTP_REMOTE_IP, CONST_STR_LEN("HTTP[\"remote-ip\"]" ) }, + { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"querystring\"]") }, + { COMP_HTTP_QUERY_STRING, CONST_STR_LEN("HTTP[\"query-string\"]") }, + { COMP_HTTP_REQUEST_METHOD, CONST_STR_LEN("HTTP[\"request-method\"]") }, + { COMP_HTTP_SCHEME, CONST_STR_LEN("HTTP[\"scheme\"]" ) }, + { COMP_UNSET, NULL, 0 }, + }; + size_t i; + + dc = data_config_init(); + + buffer_copy_buffer(dc->key, b); + buffer_copy_buffer(dc->op, op); + buffer_copy_buffer(dc->comp_tag, C); + buffer_copy_buffer(dc->comp_key, B); + buffer_append_string_len(dc->comp_key, CONST_STR_LEN("[\"")); + buffer_append_string_buffer(dc->comp_key, C); + buffer_append_string_len(dc->comp_key, CONST_STR_LEN("\"]")); + dc->cond = E; + + for (i = 0; comps[i].comp_key; i ++) { + if (buffer_is_equal_string( + dc->comp_key, comps[i].comp_key, comps[i].len)) { + dc->comp = comps[i].comp; + break; + } + } + if (COMP_UNSET == dc->comp) { + if (buffer_is_equal_string(B, CONST_STR_LEN("REQUEST_HEADER"))) { + dc->comp = COMP_HTTP_REQUEST_HEADER; + } + else { + fprintf(stderr, "error comp_key %s", dc->comp_key->ptr); + ctx->ok = 0; + } + } + else if (COMP_HTTP_LANGUAGE == dc->comp) { + dc->comp = COMP_HTTP_REQUEST_HEADER; + buffer_copy_string_len(dc->comp_tag, CONST_STR_LEN("Accept-Language")); + } + else if (COMP_HTTP_USER_AGENT == dc->comp) { + dc->comp = COMP_HTTP_REQUEST_HEADER; + buffer_copy_string_len(dc->comp_tag, CONST_STR_LEN("User-Agent")); + } + else if (COMP_HTTP_REMOTE_IP == dc->comp + && (dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE)) { + char * const slash = strchr(rvalue->ptr, '/'); /* CIDR mask */ + char * const colon = strchr(rvalue->ptr, ':'); /* IPv6 */ + if (NULL != slash && slash == rvalue->ptr){/*(skip AF_UNIX /path/file)*/ + } + else if (NULL != slash) { + char *nptr; + const unsigned long nm_bits = strtoul(slash + 1, &nptr, 10); + if (*nptr || 0 == nm_bits || nm_bits > (NULL != colon ? 128 : 32)) { + /*(also rejects (slash+1 == nptr) which results in nm_bits = 0)*/ + fprintf(stderr, "invalid or missing netmask: %s\n", rvalue->ptr); + ctx->ok = 0; + } + else { + int rc; + buffer_string_set_length(rvalue, (size_t)(slash - rvalue->ptr)); /*(truncate)*/ + rc = (NULL == colon) + ? http_request_host_normalize(rvalue, 0) + : configparser_remoteip_normalize_compat(rvalue); + buffer_append_string_len(rvalue, CONST_STR_LEN("/")); + buffer_append_int(rvalue, (int)nm_bits); + if (0 != rc) { + fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr); + ctx->ok = 0; + } + } + } + else { + int rc = (NULL == colon) + ? http_request_host_normalize(rvalue, 0) + : configparser_remoteip_normalize_compat(rvalue); + if (0 != rc) { + fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr); + ctx->ok = 0; + } + } + } + else if (COMP_SERVER_SOCKET == dc->comp) { + /*(redundant with parsing in network.c; not actually required here)*/ + if (rvalue->ptr[0] != ':' /*(network.c special-cases ":" and "[]")*/ + && !(rvalue->ptr[0] == '[' && rvalue->ptr[1] == ']')) { + if (http_request_host_normalize(rvalue, 0)) { + fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr); + ctx->ok = 0; + } + } + } + else if (COMP_HTTP_HOST == dc->comp) { + if (dc->cond == CONFIG_COND_EQ || dc->cond == CONFIG_COND_NE) { + if (http_request_host_normalize(rvalue, 0)) { + fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr); + ctx->ok = 0; + } + } + } + + dc->string = buffer_init_buffer(rvalue); + + if (ctx->ok) switch(E) { + case CONFIG_COND_NE: + case CONFIG_COND_EQ: + break; + case CONFIG_COND_NOMATCH: + case CONFIG_COND_MATCH: { + if (!data_config_pcre_compile(dc)) { + ctx->ok = 0; + } + break; + } + + default: + fprintf(stderr, "unknown condition for $%s[%s]\n", + B->ptr, C->ptr); + ctx->ok = 0; + break; + } + + if (ctx->ok) { + configparser_push(ctx, dc, 1); + } else { + dc->fn->free((data_unset*) dc); + } + } + } + + buffer_free(b); + buffer_free(op); + buffer_free(B); + B = NULL; + buffer_free(C); + C = NULL; + if (D) D->fn->free(D); + D = NULL; +} + +context_else ::= . { + if (ctx->ok) { + data_config *dc = data_config_init(); + buffer_copy_buffer(dc->key, ctx->current->key); + buffer_append_string_len(dc->key, CONST_STR_LEN("/")); + buffer_append_string_len(dc->key, CONST_STR_LEN("else_tmp_token")); + dc->cond = CONFIG_COND_ELSE; + configparser_push(ctx, dc, 1); + } +} + +cond(A) ::= EQ. { + A = CONFIG_COND_EQ; +} +cond(A) ::= MATCH. { + A = CONFIG_COND_MATCH; +} +cond(A) ::= NE. { + A = CONFIG_COND_NE; +} +cond(A) ::= NOMATCH. { + A = CONFIG_COND_NOMATCH; +} + +stringop(A) ::= expression(B). { + A = NULL; + if (ctx->ok) { + if (B->type == TYPE_STRING) { + A = ((data_string*)B)->value; + ((data_string*)B)->value = NULL; + } else if (B->type == TYPE_INTEGER) { + A = buffer_init(); + buffer_copy_int(A, ((data_integer *)B)->value); + } else { + fprintf(stderr, "operand must be string"); + ctx->ok = 0; + } + } + if (B) B->fn->free(B); + B = NULL; +} + +include ::= INCLUDE stringop(A). { + if (ctx->ok) { + if (0 != config_parse_file(ctx->srv, ctx, A->ptr)) { + ctx->ok = 0; + } + } + buffer_free(A); + A = NULL; +} + +include_shell ::= INCLUDE_SHELL stringop(A). { + if (ctx->ok) { + if (0 != config_parse_cmd(ctx->srv, ctx, A->ptr)) { + ctx->ok = 0; + } + } + buffer_free(A); + A = NULL; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/connections-glue.c b/data/lighttpd/lighttpd-1.4.53/src/connections-glue.c new file mode 100644 index 000000000..8b4f97121 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/connections-glue.c @@ -0,0 +1,506 @@ +#include "first.h" + +#include "sys-socket.h" +#include "base.h" +#include "connections.h" +#include "fdevent.h" +#include "http_header.h" +#include "log.h" +#include "response.h" + +#include <errno.h> +#include <string.h> + +const char *connection_get_state(connection_state_t state) { + switch (state) { + case CON_STATE_CONNECT: return "connect"; + case CON_STATE_READ: return "read"; + case CON_STATE_READ_POST: return "readpost"; + case CON_STATE_WRITE: return "write"; + case CON_STATE_CLOSE: return "close"; + case CON_STATE_ERROR: return "error"; + case CON_STATE_HANDLE_REQUEST: return "handle-req"; + case CON_STATE_REQUEST_START: return "req-start"; + case CON_STATE_REQUEST_END: return "req-end"; + case CON_STATE_RESPONSE_START: return "resp-start"; + case CON_STATE_RESPONSE_END: return "resp-end"; + default: return "(unknown)"; + } +} + +const char *connection_get_short_state(connection_state_t state) { + switch (state) { + case CON_STATE_CONNECT: return "."; + case CON_STATE_READ: return "r"; + case CON_STATE_READ_POST: return "R"; + case CON_STATE_WRITE: return "W"; + case CON_STATE_CLOSE: return "C"; + case CON_STATE_ERROR: return "E"; + case CON_STATE_HANDLE_REQUEST: return "h"; + case CON_STATE_REQUEST_START: return "q"; + case CON_STATE_REQUEST_END: return "Q"; + case CON_STATE_RESPONSE_START: return "s"; + case CON_STATE_RESPONSE_END: return "S"; + default: return "x"; + } +} + +int connection_set_state(server *srv, connection *con, connection_state_t state) { + UNUSED(srv); + + con->state = state; + + return 0; +} + +static int connection_handle_read_post_cq_compact(chunkqueue *cq) { + /* combine first mem chunk with next non-empty mem chunk + * (loop if next chunk is empty) */ + chunk *c; + while (NULL != (c = cq->first) && NULL != c->next) { + buffer *mem = c->next->mem; + off_t offset = c->next->offset; + size_t blen = buffer_string_length(mem) - (size_t)offset; + force_assert(c->type == MEM_CHUNK); + force_assert(c->next->type == MEM_CHUNK); + buffer_append_string_len(c->mem, mem->ptr+offset, blen); + c->next->offset = c->offset; + c->next->mem = c->mem; + c->mem = mem; + c->offset = offset + (off_t)blen; + chunkqueue_remove_finished_chunks(cq); + if (0 != blen) return 1; + } + return 0; +} + +static int connection_handle_read_post_chunked_crlf(chunkqueue *cq) { + /* caller might check chunkqueue_length(cq) >= 2 before calling here + * to limit return value to either 1 for good or -1 for error */ + chunk *c; + buffer *b; + char *p; + size_t len; + + /* caller must have called chunkqueue_remove_finished_chunks(cq), so if + * chunkqueue is not empty, it contains chunk with at least one char */ + if (chunkqueue_is_empty(cq)) return 0; + + c = cq->first; + b = c->mem; + p = b->ptr+c->offset; + if (p[0] != '\r') return -1; /* error */ + if (p[1] == '\n') return 1; + len = buffer_string_length(b) - (size_t)c->offset; + if (1 != len) return -1; /* error */ + + while (NULL != (c = c->next)) { + b = c->mem; + len = buffer_string_length(b) - (size_t)c->offset; + if (0 == len) continue; + p = b->ptr+c->offset; + return (p[0] == '\n') ? 1 : -1; /* error if not '\n' */ + } + return 0; +} + +handler_t connection_handle_read_post_error(server *srv, connection *con, int http_status) { + UNUSED(srv); + + con->keep_alive = 0; + + /*(do not change status if response headers already set and possibly sent)*/ + if (0 != con->bytes_header) return HANDLER_ERROR; + + http_response_body_clear(con, 0); + con->http_status = http_status; + con->mode = DIRECT; + return HANDLER_FINISHED; +} + +static handler_t connection_handle_read_post_chunked(server *srv, connection *con, chunkqueue *cq, chunkqueue *dst_cq) { + + /* con->conf.max_request_size is in kBytes */ + const off_t max_request_size = (off_t)con->conf.max_request_size << 10; + off_t te_chunked = con->request.te_chunked; + do { + off_t len = cq->bytes_in - cq->bytes_out; + + while (0 == te_chunked) { + char *p; + chunk *c = cq->first; + if (NULL == c) break; + force_assert(c->type == MEM_CHUNK); + p = strchr(c->mem->ptr+c->offset, '\n'); + if (NULL != p) { /* found HTTP chunked header line */ + off_t hsz = p + 1 - (c->mem->ptr+c->offset); + unsigned char *s = (unsigned char *)c->mem->ptr+c->offset; + for (unsigned char u;(u=(unsigned char)hex2int(*s))!=0xFF;++s) { + if (te_chunked > (off_t)(1uLL<<(8*sizeof(off_t)-5))-1) { + log_error_write(srv, __FILE__, __LINE__, "s", + "chunked data size too large -> 400"); + /* 400 Bad Request */ + return connection_handle_read_post_error(srv, con, 400); + } + te_chunked <<= 4; + te_chunked |= u; + } + while (*s == ' ' || *s == '\t') ++s; + if (*s != '\r' && *s != ';') { + log_error_write(srv, __FILE__, __LINE__, "s", + "chunked header invalid chars -> 400"); + /* 400 Bad Request */ + return connection_handle_read_post_error(srv, con, 400); + } + + if (hsz >= 1024) { + /* prevent theoretical integer overflow + * casting to (size_t) and adding 2 (for "\r\n") */ + log_error_write(srv, __FILE__, __LINE__, "s", + "chunked header line too long -> 400"); + /* 400 Bad Request */ + return connection_handle_read_post_error(srv, con, 400); + } + + if (0 == te_chunked) { + /* do not consume final chunked header until + * (optional) trailers received along with + * request-ending blank line "\r\n" */ + if (p[0] == '\r' && p[1] == '\n') { + /*(common case with no trailers; final \r\n received)*/ + hsz += 2; + } + else { + /* trailers or final CRLF crosses into next cq chunk */ + hsz -= 2; + do { + c = cq->first; + p = strstr(c->mem->ptr+c->offset+hsz, "\r\n\r\n"); + } while (NULL == p + && connection_handle_read_post_cq_compact(cq)); + if (NULL == p) { + /*(effectively doubles max request field size + * potentially received by backend, if in the future + * these trailers are added to request headers)*/ + if ((off_t)buffer_string_length(c->mem) - c->offset + < srv->srvconf.max_request_field_size) { + break; + } + else { + /* ignore excessively long trailers; + * disable keep-alive on connection */ + con->keep_alive = 0; + } + } + hsz = p + 4 - (c->mem->ptr+c->offset); + /* trailers currently ignored, but could be processed + * here if 0 == con->conf.stream_request_body, taking + * care to reject any fields forbidden in trailers, + * making trailers available to CGI and other backends*/ + } + chunkqueue_mark_written(cq, (size_t)hsz); + con->request.content_length = dst_cq->bytes_in; + break; /* done reading HTTP chunked request body */ + } + + /* consume HTTP chunked header */ + chunkqueue_mark_written(cq, (size_t)hsz); + len = cq->bytes_in - cq->bytes_out; + + if (0 !=max_request_size + && (max_request_size < te_chunked + || max_request_size - te_chunked < dst_cq->bytes_in)) { + log_error_write(srv, __FILE__, __LINE__, "sos", + "request-size too long:", + dst_cq->bytes_in + te_chunked, "-> 413"); + /* 413 Payload Too Large */ + return connection_handle_read_post_error(srv, con, 413); + } + + te_chunked += 2; /*(for trailing "\r\n" after chunked data)*/ + + break; /* read HTTP chunked header */ + } + + /*(likely better ways to handle chunked header crossing chunkqueue + * chunks, but this situation is not expected to occur frequently)*/ + if ((off_t)buffer_string_length(c->mem) - c->offset >= 1024) { + log_error_write(srv, __FILE__, __LINE__, "s", + "chunked header line too long -> 400"); + /* 400 Bad Request */ + return connection_handle_read_post_error(srv, con, 400); + } + else if (!connection_handle_read_post_cq_compact(cq)) { + break; + } + } + if (0 == te_chunked) break; + + if (te_chunked > 2) { + if (len > te_chunked-2) len = te_chunked-2; + if (dst_cq->bytes_in + te_chunked <= 64*1024) { + /* avoid buffering request bodies <= 64k on disk */ + chunkqueue_steal(dst_cq, cq, len); + } + else if (0 != chunkqueue_steal_with_tempfiles(srv,dst_cq,cq,len)) { + /* 500 Internal Server Error */ + return connection_handle_read_post_error(srv, con, 500); + } + te_chunked -= len; + len = cq->bytes_in - cq->bytes_out; + } + + if (len < te_chunked) break; + + if (2 == te_chunked) { + if (-1 == connection_handle_read_post_chunked_crlf(cq)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "chunked data missing end CRLF -> 400"); + /* 400 Bad Request */ + return connection_handle_read_post_error(srv, con, 400); + } + chunkqueue_mark_written(cq, 2);/*consume \r\n at end of chunk data*/ + te_chunked -= 2; + } + + } while (!chunkqueue_is_empty(cq)); + + con->request.te_chunked = te_chunked; + return HANDLER_GO_ON; +} + +static handler_t connection_handle_read_body_unknown(server *srv, connection *con, chunkqueue *cq, chunkqueue *dst_cq) { + /* con->conf.max_request_size is in kBytes */ + const off_t max_request_size = (off_t)con->conf.max_request_size << 10; + chunkqueue_append_chunkqueue(dst_cq, cq); + if (0 != max_request_size && dst_cq->bytes_in > max_request_size) { + log_error_write(srv, __FILE__, __LINE__, "sos", + "request-size too long:", dst_cq->bytes_in, "-> 413"); + /* 413 Payload Too Large */ + return connection_handle_read_post_error(srv, con, 413); + } + return HANDLER_GO_ON; +} + +static off_t connection_write_throttle(server *srv, connection *con, off_t max_bytes) { + UNUSED(srv); + if (con->conf.global_kbytes_per_second) { + off_t limit = con->conf.global_kbytes_per_second * 1024 - *(con->conf.global_bytes_per_second_cnt_ptr); + if (limit <= 0) { + /* we reached the global traffic limit */ + con->traffic_limit_reached = 1; + + return 0; + } else { + if (max_bytes > limit) max_bytes = limit; + } + } + + if (con->conf.kbytes_per_second) { + off_t limit = con->conf.kbytes_per_second * 1024 - con->bytes_written_cur_second; + if (limit <= 0) { + /* we reached the traffic limit */ + con->traffic_limit_reached = 1; + + return 0; + } else { + if (max_bytes > limit) max_bytes = limit; + } + } + + return max_bytes; +} + +int connection_write_chunkqueue(server *srv, connection *con, chunkqueue *cq, off_t max_bytes) { + int ret = -1; + off_t written = 0; + #ifdef TCP_CORK + int corked = 0; + #endif + + max_bytes = connection_write_throttle(srv, con, max_bytes); + if (0 == max_bytes) return 1; + + written = cq->bytes_out; + + #ifdef TCP_CORK + /* Linux: put a cork into socket as we want to combine write() calls + * but only if we really have multiple chunks including non-MEM_CHUNK, + * and only if TCP socket + */ + if (cq->first && cq->first->next) { + const int sa_family = sock_addr_get_family(&con->srv_socket->addr); + if (sa_family == AF_INET || sa_family == AF_INET6) { + chunk *c = cq->first; + while (c->type == MEM_CHUNK && NULL != (c = c->next)) ; + if (NULL != c) { + corked = 1; + (void)setsockopt(con->fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked)); + } + } + } + #endif + + ret = con->network_write(srv, con, cq, max_bytes); + if (ret >= 0) { + chunkqueue_remove_finished_chunks(cq); + ret = chunkqueue_is_empty(cq) ? 0 : 1; + } + + #ifdef TCP_CORK + if (corked) { + corked = 0; + (void)setsockopt(con->fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked)); + } + #endif + + written = cq->bytes_out - written; + con->bytes_written += written; + con->bytes_written_cur_second += written; + *(con->conf.global_bytes_per_second_cnt_ptr) += written; + + return ret; +} + +static int connection_write_100_continue(server *srv, connection *con) { + /* Make best effort to send all or none of "HTTP/1.1 100 Continue" */ + /* (Note: also choosing not to update con->write_request_ts + * which differs from connections.c:connection_handle_write()) */ + static const char http_100_continue[] = "HTTP/1.1 100 Continue\r\n\r\n"; + chunkqueue *cq; + off_t written; + int rc; + + off_t max_bytes = + connection_write_throttle(srv, con, sizeof(http_100_continue)-1); + if (max_bytes < (off_t)sizeof(http_100_continue)-1) { + return 1; /* success; skip sending if throttled to partial */ + } + + cq = con->write_queue; + written = cq->bytes_out; + + chunkqueue_append_mem(cq,http_100_continue,sizeof(http_100_continue)-1); + rc = con->network_write(srv, con, cq, sizeof(http_100_continue)-1); + + written = cq->bytes_out - written; + con->bytes_written += written; + con->bytes_written_cur_second += written; + *(con->conf.global_bytes_per_second_cnt_ptr) += written; + + if (rc < 0) { + connection_set_state(srv, con, CON_STATE_ERROR); + return 0; /* error */ + } + + if (written == sizeof(http_100_continue)-1) { + chunkqueue_remove_finished_chunks(cq); + } else if (0 == written) { + /* skip sending 100 Continue if send would block */ + chunkqueue_mark_written(cq, sizeof(http_100_continue)-1); + con->is_writable = 0; + } + /* else partial write (unlikely), which can cause corrupt + * response if response is later cleared, e.g. sending errdoc. + * However, situation of partial write can occur here only on + * keep-alive request where client has sent pipelined request, + * and more than 0 chars were written, but fewer than 25 chars */ + + return 1; /* success; sent all or none of "HTTP/1.1 100 Continue" */ +} + +handler_t connection_handle_read_post_state(server *srv, connection *con) { + chunkqueue *cq = con->read_queue; + chunkqueue *dst_cq = con->request_content_queue; + + int is_closed = 0; + + if (con->is_readable) { + con->read_idle_ts = srv->cur_ts; + + switch(con->network_read(srv, con, con->read_queue, MAX_READ_LIMIT)) { + case -1: + connection_set_state(srv, con, CON_STATE_ERROR); + return HANDLER_ERROR; + case -2: + is_closed = 1; + break; + default: + break; + } + } + + chunkqueue_remove_finished_chunks(cq); + + /* Check for Expect: 100-continue in request headers + * if no request body received yet */ + if (chunkqueue_is_empty(cq) && 0 == dst_cq->bytes_in + && con->request.http_version != HTTP_VERSION_1_0 + && chunkqueue_is_empty(con->write_queue) && con->is_writable) { + buffer *vb = http_header_request_get(con, HTTP_HEADER_EXPECT, CONST_STR_LEN("Expect")); + if (NULL != vb && 0 == buffer_caseless_compare(CONST_BUF_LEN(vb), CONST_STR_LEN("100-continue"))) { + http_header_request_unset(con, HTTP_HEADER_EXPECT, CONST_STR_LEN("Expect")); + if (!connection_write_100_continue(srv, con)) { + return HANDLER_ERROR; + } + } + } + + if (con->request.content_length < 0) { + /*(-1: Transfer-Encoding: chunked, -2: unspecified length)*/ + handler_t rc = (-1 == con->request.content_length) + ? connection_handle_read_post_chunked(srv, con, cq, dst_cq) + : connection_handle_read_body_unknown(srv, con, cq, dst_cq); + if (HANDLER_GO_ON != rc) return rc; + } + else if (con->request.content_length <= 64*1024) { + /* don't buffer request bodies <= 64k on disk */ + chunkqueue_steal(dst_cq, cq, (off_t)con->request.content_length - dst_cq->bytes_in); + } + else if (0 != chunkqueue_steal_with_tempfiles(srv, dst_cq, cq, (off_t)con->request.content_length - dst_cq->bytes_in)) { + /* writing to temp file failed */ + return connection_handle_read_post_error(srv, con, 500); /* Internal Server Error */ + } + + chunkqueue_remove_finished_chunks(cq); + + if (dst_cq->bytes_in == (off_t)con->request.content_length) { + /* Content is ready */ + con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; + if (con->state == CON_STATE_READ_POST) { + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + } + return HANDLER_GO_ON; + } else if (is_closed) { + #if 0 + return connection_handle_read_post_error(srv, con, 400); /* Bad Request */ + #endif + return HANDLER_ERROR; + } else { + con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; + return (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST) + ? HANDLER_GO_ON + : HANDLER_WAIT_FOR_EVENT; + } +} + +void connection_response_reset(server *srv, connection *con) { + UNUSED(srv); + + con->mode = DIRECT; + con->http_status = 0; + con->is_writable = 1; + con->file_finished = 0; + con->file_started = 0; + con->response.keep_alive = 0; + if (con->physical.path) { /*(skip for mod_fastcgi authorizer)*/ + buffer_clear(con->physical.doc_root); + buffer_reset(con->physical.path); + buffer_clear(con->physical.basedir); + buffer_reset(con->physical.rel_path); + buffer_clear(con->physical.etag); + } + con->response.htags = 0; + array_reset_data_strings(con->response.headers); + http_response_body_clear(con, 0); +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/connections.c b/data/lighttpd/lighttpd-1.4.53/src/connections.c new file mode 100644 index 000000000..fa6df0ee6 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/connections.c @@ -0,0 +1,1409 @@ +#include "first.h" + +#include "base.h" +#include "buffer.h" +#include "log.h" +#include "connections.h" +#include "fdevent.h" +#include "http_header.h" + +#include "configfile.h" +#include "request.h" +#include "response.h" +#include "network.h" +#include "http_chunk.h" +#include "stat_cache.h" +#include "joblist.h" + +#include "plugin.h" + +#include "inet_ntop_cache.h" + +#include <sys/stat.h> + +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#ifdef HAVE_SYS_FILIO_H +# include <sys/filio.h> +#endif + +#include "sys-socket.h" + +typedef struct { + PLUGIN_DATA; +} plugin_data; + +static connection *connections_get_new_connection(server *srv) { + connections *conns = srv->conns; + size_t i; + + if (conns->size == conns->used) { + conns->size += srv->max_conns >= 128 ? 128 : srv->max_conns > 16 ? 16 : srv->max_conns; + conns->ptr = realloc(conns->ptr, sizeof(*conns->ptr) * conns->size); + force_assert(NULL != conns->ptr); + + for (i = conns->used; i < conns->size; i++) { + conns->ptr[i] = connection_init(srv); + connection_reset(srv, conns->ptr[i]); + } + } + + conns->ptr[conns->used]->ndx = conns->used; + return conns->ptr[conns->used++]; +} + +static int connection_del(server *srv, connection *con) { + size_t i; + connections *conns = srv->conns; + connection *temp; + + if (con == NULL) return -1; + + if (-1 == con->ndx) return -1; + + buffer_clear(con->uri.authority); + buffer_reset(con->uri.path); + buffer_reset(con->uri.query); + buffer_reset(con->request.orig_uri); + + i = con->ndx; + + /* not last element */ + + if (i != conns->used - 1) { + temp = conns->ptr[i]; + conns->ptr[i] = conns->ptr[conns->used - 1]; + conns->ptr[conns->used - 1] = temp; + + conns->ptr[i]->ndx = i; + conns->ptr[conns->used - 1]->ndx = -1; + } + + conns->used--; + + con->ndx = -1; +#if 0 + fprintf(stderr, "%s.%d: del: (%d)", __FILE__, __LINE__, conns->used); + for (i = 0; i < conns->used; i++) { + fprintf(stderr, "%d ", conns->ptr[i]->fd); + } + fprintf(stderr, "\n"); +#endif + return 0; +} + +static int connection_close(server *srv, connection *con) { + if (con->fd < 0) con->fd = -con->fd; + + plugins_call_handle_connection_close(srv, con); + + fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd); + fdevent_unregister(srv->ev, con->fd); +#ifdef __WIN32 + if (closesocket(con->fd)) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "(warning) close:", con->fd, strerror(errno)); + } +#else + if (close(con->fd)) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "(warning) close:", con->fd, strerror(errno)); + } +#endif + else { + srv->cur_fds--; + } + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed for fd", con->fd); + } + con->fd = -1; + + /* plugins should have cleaned themselves up */ + for (size_t i = 0; i < srv->plugins.used; ++i) { + plugin *p = ((plugin **)(srv->plugins.ptr))[i]; + plugin_data *pd = p->data; + if (!pd || NULL == con->plugin_ctx[pd->id]) continue; + log_error_write(srv, __FILE__, __LINE__, "sb", + "missing cleanup in", p->name); + con->plugin_ctx[pd->id] = NULL; + } + + connection_del(srv, con); + connection_set_state(srv, con, CON_STATE_CONNECT); + + return 0; +} + +static void connection_read_for_eos(server *srv, connection *con) { + /* we have to do the linger_on_close stuff regardless + * of con->keep_alive; even non-keepalive sockets may + * still have unread data, and closing before reading + * it will make the client not see all our output. + */ + ssize_t len; + const int type = con->dst_addr.plain.sa_family; + char buf[16384]; + do { + len = fdevent_socket_read_discard(con->fd, buf, sizeof(buf), + type, SOCK_STREAM); + } while (len > 0 || (len < 0 && errno == EINTR)); + + if (len < 0 && errno == EAGAIN) return; + #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN + if (len < 0 && errno == EWOULDBLOCK) return; + #endif + + /* 0 == len || (len < 0 && (errno is a non-recoverable error)) */ + con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1); +} + +static void connection_handle_close_state(server *srv, connection *con) { + connection_read_for_eos(srv, con); + + if (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT) { + connection_close(srv, con); + } +} + +static void connection_handle_shutdown(server *srv, connection *con) { + plugins_call_handle_connection_shut_wr(srv, con); + + srv->con_closed++; + connection_reset(srv, con); + + /* close the connection */ + if (con->fd >= 0 && 0 == shutdown(con->fd, SHUT_WR)) { + con->close_timeout_ts = srv->cur_ts; + connection_set_state(srv, con, CON_STATE_CLOSE); + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "shutdown for fd", con->fd); + } + } else { + connection_close(srv, con); + } +} + +static void connection_handle_response_end_state(server *srv, connection *con) { + /* log the request */ + /* (even if error, connection dropped, still write to access log if http_status) */ + if (con->http_status) { + plugins_call_handle_request_done(srv, con); + } + + if (con->state != CON_STATE_ERROR) srv->con_written++; + + if (con->request.content_length != con->request_content_queue->bytes_in + || con->state == CON_STATE_ERROR) { + /* request body is present and has not been read completely */ + con->keep_alive = 0; + } + + if (con->keep_alive) { + connection_reset(srv, con); +#if 0 + con->request_start = srv->cur_ts; + con->read_idle_ts = srv->cur_ts; +#endif + connection_set_state(srv, con, CON_STATE_REQUEST_START); + } else { + connection_handle_shutdown(srv, con); + } +} + +static void connection_handle_errdoc_init(connection *con) { + /* modules that produce headers required with error response should + * typically also produce an error document. Make an exception for + * mod_auth WWW-Authenticate response header. */ + buffer *www_auth = NULL; + if (401 == con->http_status) { + buffer *vb = http_header_response_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("WWW-Authenticate")); + if (NULL != vb) www_auth = buffer_init_buffer(vb); + } + + buffer_reset(con->physical.path); + con->response.htags = 0; + array_reset_data_strings(con->response.headers); + http_response_body_clear(con, 0); + + if (NULL != www_auth) { + http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(www_auth)); + buffer_free(www_auth); + } +} + +static int connection_handle_write_prepare(server *srv, connection *con) { + if (con->mode == DIRECT) { + /* static files */ + switch(con->request.http_method) { + case HTTP_METHOD_GET: + case HTTP_METHOD_POST: + case HTTP_METHOD_HEAD: + break; + case HTTP_METHOD_OPTIONS: + /* + * 400 is coming from the request-parser BEFORE uri.path is set + * 403 is from the response handler when noone else catched it + * + * */ + if ((!con->http_status || con->http_status == 200) && !buffer_string_is_empty(con->uri.path) && + con->uri.path->ptr[0] != '*') { + http_response_body_clear(con, 0); + http_header_response_append(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); + con->http_status = 200; + con->file_finished = 1; + + } + break; + default: + if (0 == con->http_status) { + con->http_status = 501; + } + break; + } + } + + if (con->http_status == 0) { + con->http_status = 403; + } + + switch(con->http_status) { + case 204: /* class: header only */ + case 205: + case 304: + /* disable chunked encoding again as we have no body */ + http_response_body_clear(con, 1); + con->file_finished = 1; + break; + default: /* class: header + body */ + /* only custom body for 4xx and 5xx */ + if (con->http_status < 400 || con->http_status >= 600) break; + + if (con->mode != DIRECT && (!con->conf.error_intercept || con->error_handler_saved_status)) break; + if (con->mode == DIRECT && con->error_handler_saved_status >= 65535) break; + + con->file_finished = 0; + + connection_handle_errdoc_init(con); + + /* try to send static errorfile */ + if (!buffer_string_is_empty(con->conf.errorfile_prefix)) { + stat_cache_entry *sce = NULL; + + buffer_copy_buffer(con->physical.path, con->conf.errorfile_prefix); + buffer_append_int(con->physical.path, con->http_status); + buffer_append_string_len(con->physical.path, CONST_STR_LEN(".html")); + + if (0 == http_chunk_append_file(srv, con, con->physical.path)) { + con->file_finished = 1; + if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + stat_cache_content_type_get(srv, con, con->physical.path, sce); + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + } + } + } + + if (!con->file_finished) { + buffer *b = srv->tmp_buf; + + buffer_reset(con->physical.path); + + con->file_finished = 1; + + /* build default error-page */ + buffer_copy_string_len(b, CONST_STR_LEN( + "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" + " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" + " <head>\n" + " <title>")); + http_status_append(b, con->http_status); + + buffer_append_string_len(b, CONST_STR_LEN( + "</title>\n" + " </head>\n" + " <body>\n" + " <h1>")); + http_status_append(b, con->http_status); + + buffer_append_string_len(b, CONST_STR_LEN("</h1>\n" + " </body>\n" + "</html>\n" + )); + + (void)http_chunk_append_mem(srv, con, CONST_BUF_LEN(b)); + + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); + } + break; + } + + /* Allow filter plugins to change response headers before they are written. */ + switch(plugins_call_handle_response_start(srv, con)) { + case HANDLER_GO_ON: + case HANDLER_FINISHED: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "s", "response_start plugin failed"); + return -1; + } + + if (con->file_finished) { + /* we have all the content and chunked encoding is not used, set a content-length */ + + if (!(con->response.htags & (HTTP_HEADER_CONTENT_LENGTH|HTTP_HEADER_TRANSFER_ENCODING))) { + off_t qlen = chunkqueue_length(con->write_queue); + + /** + * The Content-Length header only can be sent if we have content: + * - HEAD doesn't have a content-body (but have a content-length) + * - 1xx, 204 and 304 don't have a content-body (RFC 2616 Section 4.3) + * + * Otherwise generate a Content-Length header as chunked encoding is not + * available + */ + if ((con->http_status >= 100 && con->http_status < 200) || + con->http_status == 204 || + con->http_status == 304) { + /* no Content-Body, no Content-Length */ + http_header_response_unset(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); + } else if (qlen > 0 || con->request.http_method != HTTP_METHOD_HEAD) { + /* qlen = 0 is important for Redirects (301, ...) as they MAY have + * a content. Browsers are waiting for a Content otherwise + */ + buffer_copy_int(srv->tmp_buf, qlen); + http_header_response_set(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"), CONST_BUF_LEN(srv->tmp_buf)); + } + } + } else { + /** + * the file isn't finished yet, but we have all headers + * + * to get keep-alive we either need: + * - Content-Length: ... (HTTP/1.0 and HTTP/1.0) or + * - Transfer-Encoding: chunked (HTTP/1.1) + * - Upgrade: ... (lighttpd then acts as transparent proxy) + */ + + if (!(con->response.htags & (HTTP_HEADER_CONTENT_LENGTH|HTTP_HEADER_TRANSFER_ENCODING|HTTP_HEADER_UPGRADE))) { + if (con->request.http_method == HTTP_METHOD_CONNECT + && con->http_status == 200) { + /*(no transfer-encoding if successful CONNECT)*/ + } else if (con->request.http_version == HTTP_VERSION_1_1) { + off_t qlen = chunkqueue_length(con->write_queue); + con->response.send_chunked = 1; + if (qlen) { + /* create initial Transfer-Encoding: chunked segment */ + buffer * const b = chunkqueue_prepend_buffer_open(con->write_queue); + buffer_append_uint_hex(b, (uintmax_t)qlen); + buffer_append_string_len(b, CONST_STR_LEN("\r\n")); + chunkqueue_prepend_buffer_commit(con->write_queue); + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("\r\n")); + } + http_header_response_append(con, HTTP_HEADER_TRANSFER_ENCODING, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked")); + } else { + con->keep_alive = 0; + } + } + + /** + * if the backend sent a Connection: close, follow the wish + * + * NOTE: if the backend sent Connection: Keep-Alive, but no Content-Length, we + * will close the connection. That's fine. We can always decide the close + * the connection + * + * FIXME: to be nice we should remove the Connection: ... + */ + if (con->response.htags & HTTP_HEADER_CONNECTION) { + /* a subrequest disable keep-alive although the client wanted it */ + if (con->keep_alive && !con->response.keep_alive) { + con->keep_alive = 0; + } + } + } + + if (con->request.http_method == HTTP_METHOD_HEAD) { + /** + * a HEAD request has the same as a GET + * without the content + */ + http_response_body_clear(con, 1); + con->file_finished = 1; + } + + http_response_write_header(srv, con); + + return 0; +} + +static int connection_handle_write(server *srv, connection *con) { + switch(connection_write_chunkqueue(srv, con, con->write_queue, MAX_WRITE_LIMIT)) { + case 0: + con->write_request_ts = srv->cur_ts; + if (con->file_finished) { + connection_set_state(srv, con, CON_STATE_RESPONSE_END); + } + break; + case -1: /* error on our side */ + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed: write failed on fd", con->fd); + connection_set_state(srv, con, CON_STATE_ERROR); + break; + case -2: /* remote close */ + connection_set_state(srv, con, CON_STATE_ERROR); + break; + case 1: + con->write_request_ts = srv->cur_ts; + con->is_writable = 0; + + /* not finished yet -> WRITE */ + break; + } + + return 0; +} + + + +connection *connection_init(server *srv) { + connection *con; + + UNUSED(srv); + + con = calloc(1, sizeof(*con)); + force_assert(NULL != con); + + con->fd = 0; + con->ndx = -1; + con->fde_ndx = -1; + con->bytes_written = 0; + con->bytes_read = 0; + con->bytes_header = 0; + con->loops_per_request = 0; + +#define CLEAN(x) \ + con->x = buffer_init(); + + CLEAN(request.uri); + CLEAN(request.request_line); + CLEAN(request.request); + CLEAN(request.pathinfo); + + CLEAN(request.orig_uri); + + CLEAN(uri.scheme); + CLEAN(uri.authority); + CLEAN(uri.path); + CLEAN(uri.path_raw); + CLEAN(uri.query); + + CLEAN(physical.doc_root); + CLEAN(physical.path); + CLEAN(physical.basedir); + CLEAN(physical.rel_path); + CLEAN(physical.etag); + CLEAN(parse_request); + + CLEAN(server_name); + CLEAN(proto); + CLEAN(dst_addr_buf); + +#undef CLEAN + con->write_queue = chunkqueue_init(); + con->read_queue = chunkqueue_init(); + con->request_content_queue = chunkqueue_init(); + + con->request.headers = array_init(); + con->response.headers = array_init(); + con->environment = array_init(); + + /* init plugin specific connection structures */ + + con->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *)); + force_assert(NULL != con->plugin_ctx); + + con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t)); + force_assert(NULL != con->cond_cache); + config_setup_connection(srv, con); + + return con; +} + +void connections_free(server *srv) { + connections *conns = srv->conns; + size_t i; + + if (NULL == conns) return; + + for (i = 0; i < conns->size; i++) { + connection *con = conns->ptr[i]; + + connection_reset(srv, con); + + chunkqueue_free(con->write_queue); + chunkqueue_free(con->read_queue); + chunkqueue_free(con->request_content_queue); + array_free(con->request.headers); + array_free(con->response.headers); + array_free(con->environment); + +#define CLEAN(x) \ + buffer_free(con->x); + + CLEAN(request.uri); + CLEAN(request.request_line); + CLEAN(request.request); + CLEAN(request.pathinfo); + + CLEAN(request.orig_uri); + + CLEAN(uri.scheme); + CLEAN(uri.authority); + CLEAN(uri.path); + CLEAN(uri.path_raw); + CLEAN(uri.query); + + CLEAN(physical.doc_root); + CLEAN(physical.path); + CLEAN(physical.basedir); + CLEAN(physical.etag); + CLEAN(physical.rel_path); + CLEAN(parse_request); + + CLEAN(server_name); + CLEAN(proto); + CLEAN(dst_addr_buf); +#undef CLEAN + free(con->plugin_ctx); + free(con->cond_cache); + + free(con); + } + + free(conns->ptr); + free(conns); + srv->conns = NULL; +} + + +int connection_reset(server *srv, connection *con) { + plugins_call_connection_reset(srv, con); + + connection_response_reset(srv, con); + con->is_readable = 1; + + con->bytes_written = 0; + con->bytes_written_cur_second = 0; + con->bytes_read = 0; + con->bytes_header = 0; + con->loops_per_request = 0; + + con->request.http_method = HTTP_METHOD_UNSET; + con->request.http_version = HTTP_VERSION_UNSET; + +#define CLEAN(x) \ + buffer_reset(con->x); + + CLEAN(request.uri); + CLEAN(request.request_line); + CLEAN(request.pathinfo); + CLEAN(request.request); + + /* CLEAN(request.orig_uri); */ + + /* CLEAN(uri.path); */ + CLEAN(uri.path_raw); + /* CLEAN(uri.query); */ + + CLEAN(parse_request); + +#undef CLEAN + + buffer_clear(con->uri.scheme); + /*buffer_clear(con->proto);*//* set to default in connection_accepted() */ + /*buffer_clear(con->uri.authority);*/ + buffer_clear(con->server_name); + + con->request.http_host = NULL; + con->request.content_length = 0; + con->request.te_chunked = 0; + con->request.htags = 0; + + array_reset_data_strings(con->request.headers); + array_reset_data_strings(con->environment); + + chunkqueue_reset(con->request_content_queue); + + /* The cond_cache gets reset in response.c */ + /* config_cond_cache_reset(srv, con); */ + + con->header_len = 0; + con->async_callback = 0; + con->error_handler_saved_status = 0; + /*con->error_handler_saved_method = HTTP_METHOD_UNSET;*/ + /*(error_handler_saved_method value is not valid unless error_handler_saved_status is set)*/ + + config_setup_connection(srv, con); + + return 0; +} + +static void connection_read_header(server *srv, connection *con) { + chunk *c, *last_chunk; + off_t last_offset; + chunkqueue *cq = con->read_queue; + + chunkqueue_remove_finished_chunks(cq); + + /* we might have got several packets at once + */ + + /* if there is a \r\n\r\n in the chunkqueue + * + * scan the chunk-queue twice + * 1. to find the \r\n\r\n + * 2. to copy the header-packet + * + */ + + last_chunk = NULL; + last_offset = 0; + + for (c = cq->first; c; c = c->next) { + size_t i; + size_t len = buffer_string_length(c->mem) - c->offset; + const char *b = c->mem->ptr + c->offset; + + for (i = 0; i < len; ++i) { + char ch = b[i]; + + if ('\r' == ch) { + /* chec if \n\r\n follows */ + size_t j = i+1; + chunk *cc = c; + const char header_end[] = "\r\n\r\n"; + int header_end_match_pos = 1; + + for ( ; cc; cc = cc->next, j = 0 ) { + size_t bblen = buffer_string_length(cc->mem) - cc->offset; + const char *bb = cc->mem->ptr + cc->offset; + + for ( ; j < bblen; j++) { + ch = bb[j]; + + if (ch == header_end[header_end_match_pos]) { + header_end_match_pos++; + if (4 == header_end_match_pos) { + last_chunk = cc; + last_offset = j+1; + goto found_header_end; + } + } else { + goto reset_search; + } + } + } + } else if ('\n' == ch) { + /* check if \n follows */ + if (i+1 < len) { + if (b[i+1] == '\n') { + last_chunk = c; + last_offset = i+2; + break; + } /* else goto reset_search; */ + } else { + for (chunk *cc = c->next; cc; cc = cc->next) { + size_t bblen = buffer_string_length(cc->mem) - cc->offset; + const char *bb = cc->mem->ptr + cc->offset; + if (0 == bblen) continue; + if (bb[0] == '\n') { + last_chunk = cc; + last_offset = 1; + goto found_header_end; + } else { + goto reset_search; + } + } + } + } +reset_search: ; + } + } +found_header_end: + + /* found */ + if (last_chunk) { + buffer_reset(con->request.request); + + for (c = cq->first; c; c = c->next) { + size_t len = buffer_string_length(c->mem) - c->offset; + + if (c == last_chunk) { + len = last_offset; + } + + buffer_append_string_len(con->request.request, c->mem->ptr + c->offset, len); + c->offset += len; + cq->bytes_out += len; + + if (c == last_chunk) break; + } + + connection_set_state(srv, con, CON_STATE_REQUEST_END); + } + + if ((last_chunk ? buffer_string_length(con->request.request) : (size_t)chunkqueue_length(cq)) + > srv->srvconf.max_request_field_size) { + log_error_write(srv, __FILE__, __LINE__, "s", "oversized request-header -> sending Status 431"); + con->http_status = 431; /* Request Header Fields Too Large */ + con->keep_alive = 0; + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + } + + chunkqueue_remove_finished_chunks(cq); +} + +/** + * handle request header read + * + * we get called by the state-engine and by the fdevent-handler + */ +static int connection_handle_read_state(server *srv, connection *con) { + int is_closed = 0; /* the connection got closed, if we don't have a complete header, -> error */ + + if (con->request_count > 1 && 0 == con->bytes_read) { + + /* update request_start timestamp when first byte of + * next request is received on a keep-alive connection */ + con->request_start = srv->cur_ts; + if (con->conf.high_precision_timestamps) + log_clock_gettime_realtime(&con->request_start_hp); + + if (!chunkqueue_is_empty(con->read_queue)) { + /*(if partially read next request and unable to read() any bytes below, + * then will unnecessarily scan again here before subsequent read())*/ + connection_read_header(srv, con); + if (con->state != CON_STATE_READ) { + con->read_idle_ts = srv->cur_ts; + return 0; + } + } + } + + if (con->is_readable) { + con->read_idle_ts = srv->cur_ts; + + switch (con->network_read(srv, con, con->read_queue, MAX_READ_LIMIT)) { + case -1: + connection_set_state(srv, con, CON_STATE_ERROR); + return -1; + case -2: + is_closed = 1; + break; + default: + break; + } + } + + connection_read_header(srv, con); + + if (con->state == CON_STATE_READ && is_closed) { + /* the connection got closed and we didn't got enough data to leave CON_STATE_READ; + * the only way is to leave here */ + connection_set_state(srv, con, CON_STATE_ERROR); + } + + return 0; +} + +static handler_t connection_handle_fdevent(server *srv, void *context, int revents) { + connection *con = context; + + joblist_append(srv, con); + + if (con->srv_socket->is_ssl) { + /* ssl may read and write for both reads and writes */ + if (revents & (FDEVENT_IN | FDEVENT_OUT)) { + con->is_readable = 1; + con->is_writable = 1; + } + } else { + if (revents & FDEVENT_IN) { + con->is_readable = 1; + } + if (revents & FDEVENT_OUT) { + con->is_writable = 1; + /* we don't need the event twice */ + } + } + + + if (con->state == CON_STATE_READ) { + connection_handle_read_state(srv, con); + } + + if (con->state == CON_STATE_WRITE && + !chunkqueue_is_empty(con->write_queue) && + con->is_writable) { + + if (-1 == connection_handle_write(srv, con)) { + connection_set_state(srv, con, CON_STATE_ERROR); + + log_error_write(srv, __FILE__, __LINE__, "ds", + con->fd, + "handle write failed."); + } + } + + if (con->state == CON_STATE_CLOSE) { + /* flush the read buffers */ + connection_read_for_eos(srv, con); + } + + + /* attempt (above) to read data in kernel socket buffers + * prior to handling FDEVENT_HUP and FDEVENT_ERR */ + + if ((revents & ~(FDEVENT_IN | FDEVENT_OUT)) && con->state != CON_STATE_ERROR) { + if (con->state == CON_STATE_CLOSE) { + con->close_timeout_ts = srv->cur_ts - (HTTP_LINGER_TIMEOUT+1); + } else if (revents & FDEVENT_HUP) { + connection_set_state(srv, con, CON_STATE_ERROR); + } else if (revents & FDEVENT_RDHUP) { + int events = fdevent_event_get_interest(srv->ev, con->fd); + events &= ~(FDEVENT_IN|FDEVENT_RDHUP); + con->conf.stream_request_body &= ~(FDEVENT_STREAM_REQUEST_BUFMIN|FDEVENT_STREAM_REQUEST_POLLIN); + con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLRDHUP; + con->is_readable = 1; /*(can read 0 for end-of-stream)*/ + con->keep_alive = 0; + if (con->request.content_length < -1) { /*(transparent proxy mode; no more data to read)*/ + con->request.content_length = con->request_content_queue->bytes_in; + } + if (sock_addr_get_family(&con->dst_addr) == AF_UNIX) { + /* future: will getpeername() on AF_UNIX properly check if still connected? */ + fdevent_event_set(srv->ev, &con->fde_ndx, con->fd, events); + } else if (fdevent_is_tcp_half_closed(con->fd)) { + /* Success of fdevent_is_tcp_half_closed() after FDEVENT_RDHUP indicates TCP FIN received, + * but does not distinguish between client shutdown(fd, SHUT_WR) and client close(fd). + * Remove FDEVENT_RDHUP so that we do not spin on the ready event. + * However, a later TCP RST will not be detected until next write to socket. + * future: might getpeername() to check for TCP RST on half-closed sockets + * (without FDEVENT_RDHUP interest) when checking for write timeouts + * once a second in server.c, though getpeername() on Windows might not indicate this */ + con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_TCP_FIN; + fdevent_event_set(srv->ev, &con->fde_ndx, con->fd, events); + } else { + /* Failure of fdevent_is_tcp_half_closed() indicates TCP RST + * (or unable to tell (unsupported OS), though should not + * be setting FDEVENT_RDHUP in that case) */ + connection_set_state(srv, con, CON_STATE_ERROR); + } + } else if (revents & FDEVENT_ERR) { /* error, connection reset */ + connection_set_state(srv, con, CON_STATE_ERROR); + } else { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed: poll() -> ???", revents); + } + } + + return HANDLER_FINISHED; +} + + +connection *connection_accept(server *srv, server_socket *srv_socket) { + int cnt; + sock_addr cnt_addr; + size_t cnt_len = sizeof(cnt_addr); /*(size_t intentional; not socklen_t)*/ + + /** + * check if we can still open a new connections + * + * see #1216 + */ + + if (srv->conns->used >= srv->max_conns) { + return NULL; + } + + cnt = fdevent_accept_listenfd(srv_socket->fd, (struct sockaddr *) &cnt_addr, &cnt_len); + if (-1 == cnt) { + switch (errno) { + case EAGAIN: +#if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + case EINTR: + /* we were stopped _before_ we had a connection */ + case ECONNABORTED: /* this is a FreeBSD thingy */ + /* we were stopped _after_ we had a connection */ + break; + case EMFILE: + /* out of fds */ + break; + default: + log_error_write(srv, __FILE__, __LINE__, "ssd", "accept failed:", strerror(errno), errno); + } + return NULL; + } else { + if (sock_addr_get_family(&cnt_addr) != AF_UNIX) { + network_accept_tcp_nagle_disable(cnt); + } + return connection_accepted(srv, srv_socket, &cnt_addr, cnt); + } +} + + +/* 0: everything ok, -1: error, -2: con closed */ +static int connection_read_cq(server *srv, connection *con, chunkqueue *cq, off_t max_bytes) { + ssize_t len; + char *mem = NULL; + size_t mem_len = 0; + force_assert(cq == con->read_queue); /*(code transform assumption; minimize diff)*/ + force_assert(max_bytes == MAX_READ_LIMIT); /*(code transform assumption; minimize diff)*/ + + /* check avail data to read and obtain memory into which to read + * fill previous chunk if it has sufficient space + * (use mem_len=0 to obtain large buffer at least half of chunk_buf_sz) + */ + { + int frd; + if (0 == fdevent_ioctl_fionread(con->fd, S_IFSOCK, &frd)) { + mem_len = (frd < MAX_READ_LIMIT) ? (size_t)frd : MAX_READ_LIMIT; + } + } + mem = chunkqueue_get_memory(con->read_queue, &mem_len); + +#if defined(__WIN32) + len = recv(con->fd, mem, mem_len, 0); +#else + len = read(con->fd, mem, mem_len); +#endif /* __WIN32 */ + + chunkqueue_use_memory(con->read_queue, len > 0 ? len : 0); + + if (len < 0) { + con->is_readable = 0; + +#if defined(__WIN32) + { + int lastError = WSAGetLastError(); + switch (lastError) { + case EAGAIN: + return 0; + case EINTR: + /* we have been interrupted before we could read */ + con->is_readable = 1; + return 0; + case ECONNRESET: + /* suppress logging for this error, expected for keep-alive */ + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sd", "connection closed - recv failed: ", lastError); + break; + } + } +#else /* __WIN32 */ + switch (errno) { + case EAGAIN: + return 0; + case EINTR: + /* we have been interrupted before we could read */ + con->is_readable = 1; + return 0; + case ECONNRESET: + /* suppress logging for this error, expected for keep-alive */ + break; + default: + log_error_write(srv, __FILE__, __LINE__, "ssd", "connection closed - read failed: ", strerror(errno), errno); + break; + } +#endif /* __WIN32 */ + + connection_set_state(srv, con, CON_STATE_ERROR); + + return -1; + } else if (len == 0) { + con->is_readable = 0; + /* the other end close the connection -> KEEP-ALIVE */ + + /* pipelining */ + + return -2; + } else if (len != (ssize_t) mem_len) { + /* we got less then expected, wait for the next fd-event */ + + con->is_readable = 0; + } + + con->bytes_read += len; + return 0; +} + + +static int connection_write_cq(server *srv, connection *con, chunkqueue *cq, off_t max_bytes) { + return srv->network_backend_write(srv, con->fd, cq, max_bytes); +} + + +connection *connection_accepted(server *srv, server_socket *srv_socket, sock_addr *cnt_addr, int cnt) { + connection *con; + + srv->cur_fds++; + + /* ok, we have the connection, register it */ +#if 0 + log_error_write(srv, __FILE__, __LINE__, "sd", + "appected()", cnt); +#endif + srv->con_opened++; + + con = connections_get_new_connection(srv); + + con->fd = cnt; + con->fde_ndx = -1; + fdevent_register(srv->ev, con->fd, connection_handle_fdevent, con); + con->network_read = connection_read_cq; + con->network_write = connection_write_cq; + + connection_set_state(srv, con, CON_STATE_REQUEST_START); + + con->connection_start = srv->cur_ts; + con->dst_addr = *cnt_addr; + buffer_copy_string(con->dst_addr_buf, inet_ntop_cache_get_ip(srv, &(con->dst_addr))); + con->srv_socket = srv_socket; + + config_cond_cache_reset(srv, con); + con->conditional_is_valid[COMP_SERVER_SOCKET] = 1; + con->conditional_is_valid[COMP_HTTP_REMOTE_IP] = 1; + + buffer_copy_string_len(con->proto, CONST_STR_LEN("http")); + if (HANDLER_GO_ON != plugins_call_handle_connection_accept(srv, con)) { + connection_reset(srv, con); + connection_close(srv, con); + return NULL; + } + if (con->http_status < 0) connection_set_state(srv, con, CON_STATE_WRITE); + return con; +} + + +int connection_state_machine(server *srv, connection *con) { + int done = 0, r; + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state at start", + con->fd, + connection_get_state(con->state)); + } + + while (done == 0) { + size_t ostate = con->state; + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state for fd", con->fd, connection_get_state(con->state)); + } + + switch (con->state) { + case CON_STATE_REQUEST_START: /* transient */ + con->request_start = srv->cur_ts; + con->read_idle_ts = srv->cur_ts; + if (con->conf.high_precision_timestamps) + log_clock_gettime_realtime(&con->request_start_hp); + + con->request_count++; + con->loops_per_request = 0; + + connection_set_state(srv, con, CON_STATE_READ); + + break; + case CON_STATE_REQUEST_END: /* transient */ + buffer_clear(con->uri.authority); + buffer_reset(con->uri.path); + buffer_reset(con->uri.query); + buffer_reset(con->request.orig_uri); + + if (http_request_parse(srv, con)) { + /* we have to read some data from the POST request */ + + connection_set_state(srv, con, CON_STATE_READ_POST); + + break; + } + + connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); + + break; + case CON_STATE_READ_POST: + case CON_STATE_HANDLE_REQUEST: + /* + * the request is parsed + * + * decided what to do with the request + * - + * + * + */ + + switch (r = http_response_prepare(srv, con)) { + case HANDLER_WAIT_FOR_EVENT: + if (!con->file_finished && (!con->file_started || 0 == con->conf.stream_response_body)) { + break; /* come back here */ + } + /* response headers received from backend; fall through to start response */ + /* fall through */ + case HANDLER_FINISHED: + if (con->http_status == 0) con->http_status = 200; + if (con->error_handler_saved_status > 0) { + con->request.http_method = con->error_handler_saved_method; + } + if (con->mode == DIRECT || con->conf.error_intercept) { + if (con->error_handler_saved_status) { + const int subreq_status = con->http_status; + if (con->error_handler_saved_status > 0) { + con->http_status = con->error_handler_saved_status; + } else if (con->http_status == 404 || con->http_status == 403) { + /* error-handler-404 is a 404 */ + con->http_status = -con->error_handler_saved_status; + } else { + /* error-handler-404 is back and has generated content */ + /* if Status: was set, take it otherwise use 200 */ + } + if (200 <= subreq_status && subreq_status <= 299) { + /*(flag value to indicate that error handler succeeded) + *(for (con->mode == DIRECT))*/ + con->error_handler_saved_status = 65535; /* >= 1000 */ + } + } else if (con->http_status >= 400) { + buffer *error_handler = NULL; + if (!buffer_string_is_empty(con->conf.error_handler)) { + error_handler = con->conf.error_handler; + } else if ((con->http_status == 404 || con->http_status == 403) + && !buffer_string_is_empty(con->conf.error_handler_404)) { + error_handler = con->conf.error_handler_404; + } + + if (error_handler) { + /* call error-handler */ + + /* set REDIRECT_STATUS to save current HTTP status code + * for access by dynamic handlers + * https://redmine.lighttpd.net/issues/1828 */ + buffer_copy_int(srv->tmp_buf, con->http_status); + http_header_env_set(con, CONST_STR_LEN("REDIRECT_STATUS"), CONST_BUF_LEN(srv->tmp_buf)); + + if (error_handler == con->conf.error_handler) { + plugins_call_connection_reset(srv, con); + + if (con->request.content_length) { + if (con->request.content_length != con->request_content_queue->bytes_in) { + con->keep_alive = 0; + } + con->request.content_length = 0; + chunkqueue_reset(con->request_content_queue); + } + + con->is_writable = 1; + con->file_finished = 0; + con->file_started = 0; + con->response.keep_alive = 0; + + con->error_handler_saved_status = con->http_status; + con->error_handler_saved_method = con->request.http_method; + + con->request.http_method = HTTP_METHOD_GET; + } else { /*(preserve behavior for server.error-handler-404)*/ + con->error_handler_saved_status = -con->http_status; /*(negative to flag old behavior)*/ + } + + buffer_copy_buffer(con->request.uri, error_handler); + connection_handle_errdoc_init(con); + con->http_status = 0; /*(after connection_handle_errdoc_init())*/ + + done = -1; + break; + } + } + } + + /* we have something to send, go on */ + connection_set_state(srv, con, CON_STATE_RESPONSE_START); + break; + case HANDLER_WAIT_FOR_FD: + srv->want_fds++; + + fdwaitqueue_append(srv, con); + + break; + case HANDLER_COMEBACK: + done = -1; + break; + case HANDLER_ERROR: + /* something went wrong */ + connection_set_state(srv, con, CON_STATE_ERROR); + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sdd", "unknown ret-value: ", con->fd, r); + break; + } + + if (con->state == CON_STATE_HANDLE_REQUEST && ostate == CON_STATE_READ_POST) { + ostate = CON_STATE_HANDLE_REQUEST; + } + break; + case CON_STATE_RESPONSE_START: + /* + * the decision is done + * - create the HTTP-Response-Header + * + */ + + if (-1 == connection_handle_write_prepare(srv, con)) { + connection_set_state(srv, con, CON_STATE_ERROR); + + break; + } + + connection_set_state(srv, con, CON_STATE_WRITE); + break; + case CON_STATE_RESPONSE_END: /* transient */ + case CON_STATE_ERROR: /* transient */ + connection_handle_response_end_state(srv, con); + break; + case CON_STATE_CONNECT: + chunkqueue_reset(con->read_queue); + + con->request_count = 0; + + break; + case CON_STATE_CLOSE: + connection_handle_close_state(srv, con); + break; + case CON_STATE_READ: + connection_handle_read_state(srv, con); + break; + case CON_STATE_WRITE: + do { + /* only try to write if we have something in the queue */ + if (!chunkqueue_is_empty(con->write_queue)) { + if (con->is_writable) { + if (-1 == connection_handle_write(srv, con)) { + log_error_write(srv, __FILE__, __LINE__, "ds", + con->fd, + "handle write failed."); + connection_set_state(srv, con, CON_STATE_ERROR); + break; + } + if (con->state != CON_STATE_WRITE) break; + } + } else if (con->file_finished) { + connection_set_state(srv, con, CON_STATE_RESPONSE_END); + break; + } + + if (con->mode != DIRECT && !con->file_finished) { + switch(r = plugins_call_handle_subrequest(srv, con)) { + case HANDLER_WAIT_FOR_EVENT: + case HANDLER_FINISHED: + case HANDLER_GO_ON: + break; + case HANDLER_WAIT_FOR_FD: + srv->want_fds++; + fdwaitqueue_append(srv, con); + break; + case HANDLER_COMEBACK: + default: + log_error_write(srv, __FILE__, __LINE__, "sdd", "unexpected subrequest handler ret-value: ", con->fd, r); + /* fall through */ + case HANDLER_ERROR: + connection_set_state(srv, con, CON_STATE_ERROR); + break; + } + } + } while (con->state == CON_STATE_WRITE && (!chunkqueue_is_empty(con->write_queue) ? con->is_writable : con->file_finished)); + + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sdd", + "unknown state:", con->fd, con->state); + + break; + } + + if (done == -1) { + done = 0; + } else if (ostate == con->state) { + done = 1; + } + } + + if (srv->srvconf.log_state_handling) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "state at exit:", + con->fd, + connection_get_state(con->state)); + } + + r = 0; + switch(con->state) { + case CON_STATE_READ: + r = FDEVENT_IN | FDEVENT_RDHUP; + break; + case CON_STATE_WRITE: + /* request write-fdevent only if we really need it + * - if we have data to write + * - if the socket is not writable yet + */ + if (!chunkqueue_is_empty(con->write_queue) && + (con->is_writable == 0) && + (con->traffic_limit_reached == 0)) { + r |= FDEVENT_OUT; + } + /* fall through */ + case CON_STATE_READ_POST: + if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN) { + r |= FDEVENT_IN | FDEVENT_RDHUP; + } + break; + case CON_STATE_CLOSE: + r = FDEVENT_IN; + break; + default: + break; + } + if (con->fd >= 0) { + const int events = fdevent_event_get_interest(srv->ev, con->fd); + if (con->is_readable < 0) { + con->is_readable = 0; + r |= FDEVENT_IN; + } + if (con->is_writable < 0) { + con->is_writable = 0; + r |= FDEVENT_OUT; + } + if (events & FDEVENT_RDHUP) { + r |= FDEVENT_RDHUP; + } + if (r != events) { + /* update timestamps when enabling interest in events */ + if ((r & FDEVENT_IN) && !(events & FDEVENT_IN)) { + con->read_idle_ts = srv->cur_ts; + } + if ((r & FDEVENT_OUT) && !(events & FDEVENT_OUT)) { + con->write_request_ts = srv->cur_ts; + } + fdevent_event_set(srv->ev, &con->fde_ndx, con->fd, r); + } + } + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/connections.h b/data/lighttpd/lighttpd-1.4.53/src/connections.h new file mode 100644 index 000000000..f498e5562 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/connections.h @@ -0,0 +1,23 @@ +#ifndef _CONNECTIONS_H_ +#define _CONNECTIONS_H_ +#include "first.h" + +#include "base.h" + +connection *connection_init(server *srv); +int connection_reset(server *srv, connection *con); +void connections_free(server *srv); + +connection * connection_accept(server *srv, server_socket *srv_sock); +connection * connection_accepted(server *srv, server_socket *srv_socket, sock_addr *cnt_addr, int cnt); + +int connection_set_state(server *srv, connection *con, connection_state_t state); +const char * connection_get_state(connection_state_t state); +const char * connection_get_short_state(connection_state_t state); +int connection_state_machine(server *srv, connection *con); +handler_t connection_handle_read_post_state(server *srv, connection *con); +handler_t connection_handle_read_post_error(server *srv, connection *con, int http_status); +int connection_write_chunkqueue(server *srv, connection *con, chunkqueue *c, off_t max_bytes); +void connection_response_reset(server *srv, connection *con); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/crc32.c b/data/lighttpd/lighttpd-1.4.53/src/crc32.c new file mode 100644 index 000000000..e22f1795b --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/crc32.c @@ -0,0 +1,84 @@ +#include "first.h" + +#include "crc32.h" + +#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF]) + +static const unsigned int crc_c[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + + +uint32_t generate_crc32c(const char *buffer, size_t length) { + size_t i; + uint32_t crc32 = ~0L; + + for (i = 0; i < length; i++){ + CRC32C(crc32, (unsigned char)buffer[i]); + } + return ~crc32; +} + diff --git a/data/lighttpd/lighttpd-1.4.53/src/crc32.h b/data/lighttpd/lighttpd-1.4.53/src/crc32.h new file mode 100644 index 000000000..7df1718a5 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/crc32.h @@ -0,0 +1,7 @@ +#ifndef __crc32cr_table_h__ +#define __crc32cr_table_h__ +#include "first.h" + +uint32_t generate_crc32c(const char *string, size_t length); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/data_array.c b/data/lighttpd/lighttpd-1.4.53/src/data_array.c new file mode 100644 index 000000000..69b5d0309 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/data_array.c @@ -0,0 +1,70 @@ +#include "first.h" + +#include "array.h" + +#include <string.h> +#include <stdlib.h> + +static data_unset *data_array_copy(const data_unset *s) { + data_array *src = (data_array *)s; + data_array *ds = data_array_init(); + + buffer_copy_buffer(ds->key, src->key); + array_free(ds->value); + ds->value = array_init_array(src->value); + ds->is_index_key = src->is_index_key; + return (data_unset *)ds; +} + +static void data_array_free(data_unset *d) { + data_array *ds = (data_array *)d; + + buffer_free(ds->key); + array_free(ds->value); + + free(d); +} + +static void data_array_reset(data_unset *d) { + data_array *ds = (data_array *)d; + + /* reused array elements */ + buffer_reset(ds->key); + array_reset(ds->value); +} + +static int data_array_insert_dup(data_unset *dst, data_unset *src) { + UNUSED(dst); + + src->fn->free(src); + + return 0; +} + +static void data_array_print(const data_unset *d, int depth) { + data_array *ds = (data_array *)d; + + array_print(ds->value, depth); +} + +data_array *data_array_init(void) { + static const struct data_methods fn = { + data_array_reset, + data_array_copy, + data_array_free, + data_array_insert_dup, + data_array_print, + }; + data_array *ds; + + ds = calloc(1, sizeof(*ds)); + force_assert(NULL != ds); + + ds->key = buffer_init(); + ds->value = array_init(); + + ds->type = TYPE_ARRAY; + ds->fn = &fn; + + return ds; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/data_config.c b/data/lighttpd/lighttpd-1.4.53/src/data_config.c new file mode 100644 index 000000000..764dc6002 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/data_config.c @@ -0,0 +1,221 @@ +#include "first.h" + +#include "base.h" /* (cond_cache_t) */ +#include "array.h" +#include "configfile.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef HAVE_PCRE_H +#include <pcre.h> +#endif + +static data_unset *data_config_copy(const data_unset *s) { + data_config *src = (data_config *)s; + data_config *ds = data_config_init(); + + ds->comp = src->comp; + buffer_copy_buffer(ds->key, src->key); + buffer_copy_buffer(ds->comp_tag, src->comp_tag); + buffer_copy_buffer(ds->comp_key, src->comp_key); + array_free(ds->value); + ds->value = array_init_array(src->value); + return (data_unset *)ds; +} + +static void data_config_free(data_unset *d) { + data_config *ds = (data_config *)d; + + buffer_free(ds->key); + buffer_free(ds->op); + buffer_free(ds->comp_tag); + buffer_free(ds->comp_key); + + array_free(ds->value); + vector_config_weak_clear(&ds->children); + + if (ds->string) buffer_free(ds->string); +#ifdef HAVE_PCRE_H + if (ds->regex) pcre_free(ds->regex); + if (ds->regex_study) pcre_free(ds->regex_study); +#endif + + free(d); +} + +static void data_config_reset(data_unset *d) { + data_config *ds = (data_config *)d; + + /* reused array elements */ + buffer_clear(ds->key); + buffer_clear(ds->comp_tag); + buffer_clear(ds->comp_key); + array_reset(ds->value); +} + +static int data_config_insert_dup(data_unset *dst, data_unset *src) { + UNUSED(dst); + + src->fn->free(src); + + return 0; +} + +static void data_config_print(const data_unset *d, int depth) { + data_config *ds = (data_config *)d; + array *a = (array *)ds->value; + size_t i; + size_t maxlen; + + if (0 == ds->context_ndx) { + fprintf(stdout, "config {\n"); + } + else { + if (ds->cond != CONFIG_COND_ELSE) { + fprintf(stdout, "$%s %s \"%s\" {\n", + ds->comp_key->ptr, ds->op->ptr, ds->string->ptr); + } else { + fprintf(stdout, "{\n"); + } + array_print_indent(depth + 1); + fprintf(stdout, "# block %d\n", ds->context_ndx); + } + depth ++; + + maxlen = array_get_max_key_length(a); + for (i = 0; i < a->used; i ++) { + data_unset *du = a->data[i]; + size_t len = buffer_string_length(du->key); + size_t j; + + array_print_indent(depth); + fprintf(stdout, "%s", du->key->ptr); + for (j = maxlen - len; j > 0; j --) { + fprintf(stdout, " "); + } + fprintf(stdout, " = "); + du->fn->print(du, depth); + fprintf(stdout, "\n"); + } + + fprintf(stdout, "\n"); + for (i = 0; i < ds->children.used; i ++) { + data_config *dc = ds->children.data[i]; + + /* only the 1st block of chaining */ + if (NULL == dc->prev) { + fprintf(stdout, "\n"); + array_print_indent(depth); + dc->fn->print((data_unset *) dc, depth); + fprintf(stdout, "\n"); + } + } + + depth --; + array_print_indent(depth); + fprintf(stdout, "}"); + if (0 != ds->context_ndx) { + if (ds->cond != CONFIG_COND_ELSE) { + fprintf(stdout, " # end of $%s %s \"%s\"", + ds->comp_key->ptr, ds->op->ptr, ds->string->ptr); + } else { + fprintf(stdout, " # end of else"); + } + } + + if (ds->next) { + fprintf(stdout, "\n"); + array_print_indent(depth); + fprintf(stdout, "else "); + ds->next->fn->print((data_unset *)ds->next, depth); + } +} + +data_config *data_config_init(void) { + static const struct data_methods fn = { + data_config_reset, + data_config_copy, + data_config_free, + data_config_insert_dup, + data_config_print, + }; + data_config *ds; + + ds = calloc(1, sizeof(*ds)); + + ds->key = buffer_init(); + ds->op = buffer_init(); + ds->comp_tag = buffer_init(); + ds->comp_key = buffer_init(); + ds->value = array_init(); + vector_config_weak_init(&ds->children); + + ds->type = TYPE_CONFIG; + ds->fn = &fn; + + return ds; +} + +int data_config_pcre_compile(data_config *dc) { +#ifdef HAVE_PCRE_H + /* (use fprintf() on error, as this is called from configparser.y) */ + const char *errptr; + int erroff, captures; + + if (dc->regex) pcre_free(dc->regex); + if (dc->regex_study) pcre_free(dc->regex_study); + + dc->regex = pcre_compile(dc->string->ptr, 0, &errptr, &erroff, NULL); + if (NULL == dc->regex) { + fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n", + dc->string->ptr, errptr, erroff); + return 0; + } + + dc->regex_study = pcre_study(dc->regex, 0, &errptr); + if (NULL == dc->regex_study && errptr != NULL) { + fprintf(stderr, "studying regex failed: %s -> %s\n", + dc->string->ptr, errptr); + return 0; + } + + erroff = pcre_fullinfo(dc->regex, dc->regex_study, PCRE_INFO_CAPTURECOUNT, + &captures); + if (0 != erroff) { + fprintf(stderr, "getting capture count for regex failed: %s\n", + dc->string->ptr); + return 0; + } else if (captures > 9) { + fprintf(stderr, "Too many captures in regex, use (?:...) instead of (...): %s\n", + dc->string->ptr); + return 0; + } + return 1; +#else + fprintf(stderr, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n" + "(perhaps just a missing pcre-devel package ?) \n", + dc->comp_key->ptr, dc->comp_tag->ptr); + return 0; +#endif +} + +int data_config_pcre_exec(data_config *dc, cond_cache_t *cache, buffer *b) { +#ifdef HAVE_PCRE_H + #ifndef elementsof + #define elementsof(x) (sizeof(x) / sizeof(x[0])) + #endif + cache->patterncount = + pcre_exec(dc->regex, dc->regex_study, CONST_BUF_LEN(b), 0, 0, + cache->matches, elementsof(cache->matches)); + if (cache->patterncount > 0) + cache->comp_value = b; /* holds pointer to b (!) for pattern subst */ + return cache->patterncount; +#else + UNUSED(dc); + UNUSED(cache); + UNUSED(b); + return 0; +#endif +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/data_integer.c b/data/lighttpd/lighttpd-1.4.53/src/data_integer.c new file mode 100644 index 000000000..020acdc26 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/data_integer.c @@ -0,0 +1,71 @@ +#include "first.h" + +#include "array.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static data_unset *data_integer_copy(const data_unset *s) { + data_integer *src = (data_integer *)s; + data_integer *ds = data_integer_init(); + + buffer_copy_buffer(ds->key, src->key); + ds->is_index_key = src->is_index_key; + ds->value = src->value; + return (data_unset *)ds; +} + +static void data_integer_free(data_unset *d) { + data_integer *ds = (data_integer *)d; + + buffer_free(ds->key); + + free(d); +} + +static void data_integer_reset(data_unset *d) { + data_integer *ds = (data_integer *)d; + + /* reused integer elements */ + buffer_clear(ds->key); + ds->value = 0; +} + +static int data_integer_insert_dup(data_unset *dst, data_unset *src) { + UNUSED(dst); + + src->fn->free(src); + + return 0; +} + +static void data_integer_print(const data_unset *d, int depth) { + data_integer *ds = (data_integer *)d; + UNUSED(depth); + + fprintf(stdout, "%d", ds->value); +} + + +data_integer *data_integer_init(void) { + static const struct data_methods fn = { + data_integer_reset, + data_integer_copy, + data_integer_free, + data_integer_insert_dup, + data_integer_print, + }; + data_integer *ds; + + ds = calloc(1, sizeof(*ds)); + force_assert(NULL != ds); + + ds->key = buffer_init(); + ds->value = 0; + + ds->type = TYPE_INTEGER; + ds->fn = &fn; + + return ds; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/data_string.c b/data/lighttpd/lighttpd-1.4.53/src/data_string.c new file mode 100644 index 000000000..14f58de05 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/data_string.c @@ -0,0 +1,98 @@ +#include "first.h" + +#include "array.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +static data_unset *data_string_copy(const data_unset *s) { + data_string *src = (data_string *)s; + data_string *ds = data_string_init(); + + buffer_copy_buffer(ds->key, src->key); + buffer_copy_buffer(ds->value, src->value); + ds->is_index_key = src->is_index_key; + return (data_unset *)ds; +} + +static void data_string_free(data_unset *d) { + data_string *ds = (data_string *)d; + + buffer_free(ds->key); + buffer_free(ds->value); + + free(d); +} + +static void data_string_reset(data_unset *d) { + data_string *ds = (data_string *)d; + + /* reused array elements */ + buffer_reset(ds->key); + buffer_reset(ds->value); +} + +static int data_string_insert_dup(data_unset *dst, data_unset *src) { + data_string *ds_dst = (data_string *)dst; + data_string *ds_src = (data_string *)src; + + if (!buffer_is_empty(ds_dst->value)) { + buffer_append_string_len(ds_dst->value, CONST_STR_LEN(", ")); + buffer_append_string_buffer(ds_dst->value, ds_src->value); + } else { + buffer_copy_buffer(ds_dst->value, ds_src->value); + } + + src->fn->free(src); + + return 0; +} + +static void data_string_print(const data_unset *d, int depth) { + data_string *ds = (data_string *)d; + size_t i, len; + UNUSED(depth); + + /* empty and uninitialized strings */ + if (buffer_string_is_empty(ds->value)) { + fputs("\"\"", stdout); + return; + } + + /* print out the string as is, except prepend " with backslash */ + putc('"', stdout); + len = buffer_string_length(ds->value); + for (i = 0; i < len; i++) { + unsigned char c = ds->value->ptr[i]; + if (c == '"') { + fputs("\\\"", stdout); + } else { + putc(c, stdout); + } + } + putc('"', stdout); +} + + +data_string *data_string_init(void) { + static const struct data_methods fn = { + data_string_reset, + data_string_copy, + data_string_free, + data_string_insert_dup, + data_string_print, + }; + data_string *ds; + + ds = calloc(1, sizeof(*ds)); + force_assert(NULL != ds); + + ds->key = buffer_init(); + ds->value = buffer_init(); + + ds->type = TYPE_STRING; + ds->fn = &fn; + + return ds; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/etag.c b/data/lighttpd/lighttpd-1.4.53/src/etag.c new file mode 100644 index 000000000..7aab2bbb7 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/etag.c @@ -0,0 +1,180 @@ +#include "first.h" + +#include "buffer.h" +#include "etag.h" + +#include <sys/stat.h> +#include <string.h> + +int etag_is_equal(buffer *etag, const char *line, int weak_ok) { + enum { + START = 0, + CHECK, + CHECK_QUOTED, + SKIP, + SKIP_QUOTED, + TAIL + } state = START; + + const char *current; + const char *tok_start; + const char *tok = NULL; + int matched; + + if ('*' == line[0] && '\0' == line[1]) { + return 1; + } + + if (!etag || buffer_string_is_empty(etag)) return 0; + tok_start = etag->ptr; + + if ('W' == tok_start[0]) { + if (!weak_ok || '/' != tok_start[1]) return 0; /* bad etag */ + tok_start = tok_start + 2; + } + + if ('"' != tok_start[0]) return 0; /* bad etag */ + /* we start comparing after the first '"' */ + ++tok_start; + + for (current = line; *current; ++current) { + switch (state) { + case START: + /* wait for etag to start; ignore whitespace and ',' */ + switch (*current) { + case 'W': + /* weak etag always starts with 'W/"' */ + if ('/' != *++current) return 0; /* bad etag list */ + if ('"' != *++current) return 0; /* bad etag list */ + if (!weak_ok) { + state = SKIP; + } else { + state = CHECK; + tok = tok_start; + } + break; + case '"': + /* strong etag starts with '"' */ + state = CHECK; + tok = tok_start; + break; + case ' ': + case ',': + case '\t': + case '\r': + case '\n': + break; + default: + return 0; /* bad etag list */ + } + break; + case CHECK: + /* compare etags (after the beginning '"') + * quoted-pairs must match too (i.e. quoted in both strings): + * > (RFC 2616:) both validators MUST be identical in every way + */ + matched = *tok && *tok == *current; + ++tok; + switch (*current) { + case '\\': + state = matched ? CHECK_QUOTED : SKIP_QUOTED; + break; + case '"': + if (*tok) { + /* bad etag - string should end after '"' */ + return 0; + } + if (matched) { + /* matching etag: strings were equal */ + return 1; + } + + state = TAIL; + break; + default: + if (!matched) { + /* strings not matching, skip remainder of etag */ + state = SKIP; + } + break; + } + break; + case CHECK_QUOTED: + if (!*tok || *tok != *current) { + /* strings not matching, skip remainder of etag */ + state = SKIP; + break; + } + ++tok; + state = CHECK; + break; + case SKIP: + /* wait for final (not quoted) '"' */ + switch (*current) { + case '\\': + state = SKIP_QUOTED; + break; + case '"': + state = TAIL; + break; + } + break; + case SKIP_QUOTED: + state = SKIP; + break; + case TAIL: + /* search for ',', ignore white space */ + switch (*current) { + case ',': + state = START; + break; + case ' ': + case '\t': + case '\r': + case '\n': + break; + default: + return 0; /* bad etag list */ + } + break; + } + } + /* no matching etag found */ + return 0; +} + +int etag_create(buffer *etag, struct stat *st,etag_flags_t flags) { + if (0 == flags) return 0; + + buffer_clear(etag); + + if (flags & ETAG_USE_INODE) { + buffer_append_int(etag, st->st_ino); + buffer_append_string_len(etag, CONST_STR_LEN("-")); + } + + if (flags & ETAG_USE_SIZE) { + buffer_append_int(etag, st->st_size); + buffer_append_string_len(etag, CONST_STR_LEN("-")); + } + + if (flags & ETAG_USE_MTIME) { + buffer_append_int(etag, st->st_mtime); + } + + return 0; +} + +int etag_mutate(buffer *mut, buffer *etag) { + size_t i, len; + uint32_t h; + + len = buffer_string_length(etag); + for (h=0, i=0; i < len; ++i) h = (h<<5)^(h>>27)^(etag->ptr[i]); + + buffer_copy_string_len(mut, CONST_STR_LEN("\"")); + buffer_append_int(mut, h); + buffer_append_string_len(mut, CONST_STR_LEN("\"")); + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/etag.h b/data/lighttpd/lighttpd-1.4.53/src/etag.h new file mode 100644 index 000000000..cf0e38a98 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/etag.h @@ -0,0 +1,16 @@ +#ifndef ETAG_H +#define ETAG_H +#include "first.h" + +#include "buffer.h" + +struct stat; /* declaration */ + +typedef enum { ETAG_USE_INODE = 1, ETAG_USE_MTIME = 2, ETAG_USE_SIZE = 4 } etag_flags_t; + +int etag_is_equal(buffer *etag, const char *matches, int weak_ok); +int etag_create(buffer *etag, struct stat *st, etag_flags_t flags); +int etag_mutate(buffer *mut, buffer *etag); + + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/fastcgi.h b/data/lighttpd/lighttpd-1.4.53/src/fastcgi.h new file mode 100644 index 000000000..629279e32 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/fastcgi.h @@ -0,0 +1,139 @@ +/* + * fastcgi.h -- + * + * Defines for the FastCGI protocol. + * + * + * Copyright (c) 1995-1996 Open Market, Inc. + * + * See the file "LICENSE.TERMS" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * $Id: fastcgi.h,v 1.1.1.1 2003/10/18 09:54:10 weigon Exp $ + * + * License: Open Market License (OML) + * https://fedoraproject.org/wiki/Licensing/Open_Market_License (LICENSE.TERMS) + */ + +#ifndef _FASTCGI_H +#define _FASTCGI_H + +/* + * Listening socket file number + */ +#define FCGI_LISTENSOCK_FILENO 0 + +typedef struct { + unsigned char version; + unsigned char type; + unsigned char requestIdB1; + unsigned char requestIdB0; + unsigned char contentLengthB1; + unsigned char contentLengthB0; + unsigned char paddingLength; + unsigned char reserved; +} FCGI_Header; + +#define FCGI_MAX_LENGTH 0xffff + +/* + * Number of bytes in a FCGI_Header. Future versions of the protocol + * will not reduce this number. + */ +#define FCGI_HEADER_LEN 8 + +/* + * Value for version component of FCGI_Header + */ +#define FCGI_VERSION_1 1 + +/* + * Values for type component of FCGI_Header + */ +#define FCGI_BEGIN_REQUEST 1 +#define FCGI_ABORT_REQUEST 2 +#define FCGI_END_REQUEST 3 +#define FCGI_PARAMS 4 +#define FCGI_STDIN 5 +#define FCGI_STDOUT 6 +#define FCGI_STDERR 7 +#define FCGI_DATA 8 +#define FCGI_GET_VALUES 9 +#define FCGI_GET_VALUES_RESULT 10 +#define FCGI_UNKNOWN_TYPE 11 +#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) + +/* + * Value for requestId component of FCGI_Header + */ +#define FCGI_NULL_REQUEST_ID 0 + + +typedef struct { + unsigned char roleB1; + unsigned char roleB0; + unsigned char flags; + unsigned char reserved[5]; +} FCGI_BeginRequestBody; + +typedef struct { + FCGI_Header header; + FCGI_BeginRequestBody body; +} FCGI_BeginRequestRecord; + +/* + * Mask for flags component of FCGI_BeginRequestBody + */ +#define FCGI_KEEP_CONN 1 + +/* + * Values for role component of FCGI_BeginRequestBody + */ +#define FCGI_RESPONDER 1 +#define FCGI_AUTHORIZER 2 +#define FCGI_FILTER 3 + + +typedef struct { + unsigned char appStatusB3; + unsigned char appStatusB2; + unsigned char appStatusB1; + unsigned char appStatusB0; + unsigned char protocolStatus; + unsigned char reserved[3]; +} FCGI_EndRequestBody; + +typedef struct { + FCGI_Header header; + FCGI_EndRequestBody body; +} FCGI_EndRequestRecord; + +/* + * Values for protocolStatus component of FCGI_EndRequestBody + */ +#define FCGI_REQUEST_COMPLETE 0 +#define FCGI_CANT_MPX_CONN 1 +#define FCGI_OVERLOADED 2 +#define FCGI_UNKNOWN_ROLE 3 + + +/* + * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records + */ +#define FCGI_MAX_CONNS "FCGI_MAX_CONNS" +#define FCGI_MAX_REQS "FCGI_MAX_REQS" +#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS" + + +typedef struct { + unsigned char type; + unsigned char reserved[7]; +} FCGI_UnknownTypeBody; + +typedef struct { + FCGI_Header header; + FCGI_UnknownTypeBody body; +} FCGI_UnknownTypeRecord; + +#endif /* _FASTCGI_H */ + diff --git a/data/lighttpd/lighttpd-1.4.53/src/fdevent.c b/data/lighttpd/lighttpd-1.4.53/src/fdevent.c new file mode 100644 index 000000000..e1c2459da --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/fdevent.c @@ -0,0 +1,1033 @@ +#include "first.h" + +#include "fdevent_impl.h" +#include "fdevent.h" +#include "base.h" +#include "buffer.h" +#include "log.h" + +#include <sys/types.h> +#include <sys/wait.h> +#include "sys-socket.h" + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <time.h> + +#ifdef SOCK_CLOEXEC +static int use_sock_cloexec; +#endif +#ifdef SOCK_NONBLOCK +static int use_sock_nonblock; +#endif + +int fdevent_config(server *srv) { + static const struct ev_map { fdevent_handler_t et; const char *name; } event_handlers[] = + { + /* - epoll is most reliable + * - select works everywhere + */ +#ifdef FDEVENT_USE_LINUX_EPOLL + { FDEVENT_HANDLER_LINUX_SYSEPOLL, "linux-sysepoll" }, + { FDEVENT_HANDLER_LINUX_SYSEPOLL, "epoll" }, +#endif +#ifdef FDEVENT_USE_SOLARIS_PORT + { FDEVENT_HANDLER_SOLARIS_PORT, "solaris-eventports" }, +#endif +#ifdef FDEVENT_USE_SOLARIS_DEVPOLL + { FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll" }, +#endif +#ifdef FDEVENT_USE_FREEBSD_KQUEUE + { FDEVENT_HANDLER_FREEBSD_KQUEUE, "freebsd-kqueue" }, + { FDEVENT_HANDLER_FREEBSD_KQUEUE, "kqueue" }, +#endif +#ifdef FDEVENT_USE_POLL + { FDEVENT_HANDLER_POLL, "poll" }, +#endif +#ifdef FDEVENT_USE_SELECT + { FDEVENT_HANDLER_SELECT, "select" }, +#endif +#ifdef FDEVENT_USE_LIBEV + { FDEVENT_HANDLER_LIBEV, "libev" }, +#endif + { FDEVENT_HANDLER_UNSET, NULL } + }; + + if (buffer_string_is_empty(srv->srvconf.event_handler)) { + /* choose a good default + * + * the event_handler list is sorted by 'goodness' + * taking the first available should be the best solution + */ + srv->event_handler = event_handlers[0].et; + + if (FDEVENT_HANDLER_UNSET == srv->event_handler) { + log_error_write(srv, __FILE__, __LINE__, "s", + "sorry, there is no event handler for this system"); + + return -1; + } + + buffer_copy_string(srv->srvconf.event_handler, event_handlers[0].name); + } else { + /* + * User override + */ + + for (size_t i = 0; event_handlers[i].name; i++) { + if (0 == strcmp(event_handlers[i].name, srv->srvconf.event_handler->ptr)) { + srv->event_handler = event_handlers[i].et; + break; + } + } + + if (FDEVENT_HANDLER_UNSET == srv->event_handler) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "the selected event-handler in unknown or not supported:", + srv->srvconf.event_handler ); + + return -1; + } + } + + #ifdef FDEVENT_USE_SELECT + if (srv->event_handler == FDEVENT_HANDLER_SELECT) { + /* select limits itself + * + * as it is a hard limit and will lead to a segfault we add some safety + * */ + srv->max_fds = FD_SETSIZE - 200; + } + else + #endif + { + srv->max_fds = 4096; + } + + return 0; +} + +const char * fdevent_show_event_handlers(void) { + return + "\nEvent Handlers:\n\n" +#ifdef FDEVENT_USE_SELECT + "\t+ select (generic)\n" +#else + "\t- select (generic)\n" +#endif +#ifdef FDEVENT_USE_POLL + "\t+ poll (Unix)\n" +#else + "\t- poll (Unix)\n" +#endif +#ifdef FDEVENT_USE_LINUX_EPOLL + "\t+ epoll (Linux)\n" +#else + "\t- epoll (Linux)\n" +#endif +#ifdef FDEVENT_USE_SOLARIS_DEVPOLL + "\t+ /dev/poll (Solaris)\n" +#else + "\t- /dev/poll (Solaris)\n" +#endif +#ifdef FDEVENT_USE_SOLARIS_PORT + "\t+ eventports (Solaris)\n" +#else + "\t- eventports (Solaris)\n" +#endif +#ifdef FDEVENT_USE_FREEBSD_KQUEUE + "\t+ kqueue (FreeBSD)\n" +#else + "\t- kqueue (FreeBSD)\n" +#endif +#ifdef FDEVENT_USE_LIBEV + "\t+ libev (generic)\n" +#else + "\t- libev (generic)\n" +#endif + ; +} + +fdevents *fdevent_init(server *srv) { + fdevents *ev; + int type = srv->event_handler; + size_t maxfds; + + #ifdef SOCK_CLOEXEC + /* Test if SOCK_CLOEXEC is supported by kernel. + * Linux kernels < 2.6.27 might return EINVAL if SOCK_CLOEXEC used + * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=529929 + * http://www.linksysinfo.org/index.php?threads/lighttpd-no-longer-starts-toastman-1-28-0510-7.73132/ + * Test if SOCK_NONBLOCK is ignored by kernel on sockets. + * (reported on Android running a custom ROM) + * https://redmine.lighttpd.net/issues/2883 + */ + int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (fd >= 0) { + int flags = fcntl(fd, F_GETFL, 0); + use_sock_nonblock = (-1 != flags && (flags & O_NONBLOCK)); + use_sock_cloexec = 1; + close(fd); + } + #endif + + #ifdef FDEVENT_USE_SELECT + if (type == FDEVENT_HANDLER_SELECT) { + if (srv->max_fds > (int)FD_SETSIZE - 200) { + srv->max_fds = (int)FD_SETSIZE - 200; + } + } + #endif + maxfds = srv->max_fds + 1; /*(+1 for event-handler fd)*/ + + ev = calloc(1, sizeof(*ev)); + force_assert(NULL != ev); + ev->srv = srv; + ev->fdarray = calloc(maxfds, sizeof(*ev->fdarray)); + if (NULL == ev->fdarray) { + log_error_write(srv, __FILE__, __LINE__, "SDS", + "server.max-fds too large? (", maxfds-1, ")"); + free(ev); + return NULL; + } + ev->maxfds = maxfds; + + switch(type) { + case FDEVENT_HANDLER_POLL: + if (0 != fdevent_poll_init(ev)) { + log_error_write(srv, __FILE__, __LINE__, "S", + "event-handler poll failed"); + goto error; + } + return ev; + case FDEVENT_HANDLER_SELECT: + if (0 != fdevent_select_init(ev)) { + log_error_write(srv, __FILE__, __LINE__, "S", + "event-handler select failed"); + goto error; + } + return ev; + case FDEVENT_HANDLER_LINUX_SYSEPOLL: + if (0 != fdevent_linux_sysepoll_init(ev)) { + log_error_write(srv, __FILE__, __LINE__, "S", + "event-handler linux-sysepoll failed, try to set server.event-handler = \"poll\" or \"select\""); + goto error; + } + return ev; + case FDEVENT_HANDLER_SOLARIS_DEVPOLL: + if (0 != fdevent_solaris_devpoll_init(ev)) { + log_error_write(srv, __FILE__, __LINE__, "S", + "event-handler solaris-devpoll failed, try to set server.event-handler = \"poll\" or \"select\""); + goto error; + } + return ev; + case FDEVENT_HANDLER_SOLARIS_PORT: + if (0 != fdevent_solaris_port_init(ev)) { + log_error_write(srv, __FILE__, __LINE__, "S", + "event-handler solaris-eventports failed, try to set server.event-handler = \"poll\" or \"select\""); + goto error; + } + return ev; + case FDEVENT_HANDLER_FREEBSD_KQUEUE: + if (0 != fdevent_freebsd_kqueue_init(ev)) { + log_error_write(srv, __FILE__, __LINE__, "S", + "event-handler freebsd-kqueue failed, try to set server.event-handler = \"poll\" or \"select\""); + goto error; + } + return ev; + case FDEVENT_HANDLER_LIBEV: + if (0 != fdevent_libev_init(ev)) { + log_error_write(srv, __FILE__, __LINE__, "S", + "event-handler libev failed, try to set server.event-handler = \"poll\" or \"select\""); + goto error; + } + return ev; + case FDEVENT_HANDLER_UNSET: + default: + break; + } + +error: + free(ev->fdarray); + free(ev); + + log_error_write(srv, __FILE__, __LINE__, "S", + "event-handler is unknown, try to set server.event-handler = \"poll\" or \"select\""); + return NULL; +} + +void fdevent_free(fdevents *ev) { + size_t i; + if (!ev) return; + + if (ev->free) ev->free(ev); + + for (i = 0; i < ev->maxfds; i++) { + /* (fdevent_sched_run() should already have been run, + * but take reasonable precautions anyway) */ + if (ev->fdarray[i]) + free((fdnode *)((uintptr_t)ev->fdarray[i] & ~0x3)); + } + + free(ev->fdarray); + free(ev); +} + +int fdevent_reset(fdevents *ev) { + if (ev->reset) return ev->reset(ev); + + return 0; +} + +static fdnode *fdnode_init(void) { + fdnode *fdn; + + fdn = calloc(1, sizeof(*fdn)); + force_assert(NULL != fdn); + fdn->fd = -1; + return fdn; +} + +static void fdnode_free(fdnode *fdn) { + free(fdn); +} + +int fdevent_register(fdevents *ev, int fd, fdevent_handler handler, void *ctx) { + fdnode *fdn; + + fdn = fdnode_init(); + fdn->handler = handler; + fdn->fd = fd; + fdn->ctx = ctx; + fdn->handler_ctx = NULL; + fdn->events = 0; + + ev->fdarray[fd] = fdn; + + return 0; +} + +int fdevent_unregister(fdevents *ev, int fd) { + fdnode *fdn; + + if (!ev) return 0; + fdn = ev->fdarray[fd]; + if ((uintptr_t)fdn & 0x3) return 0; /*(should not happen)*/ + + fdnode_free(fdn); + + ev->fdarray[fd] = NULL; + + return 0; +} + +void fdevent_sched_close(fdevents *ev, int fd, int issock) { + fdnode *fdn; + if (!ev) return; + fdn = ev->fdarray[fd]; + if ((uintptr_t)fdn & 0x3) return; + ev->fdarray[fd] = (fdnode *)((uintptr_t)fdn | (issock ? 0x1 : 0x2)); + fdn->ctx = ev->pendclose; + ev->pendclose = fdn; +} + +void fdevent_sched_run(server *srv, fdevents *ev) { + for (fdnode *fdn = ev->pendclose; fdn; ) { + int fd, rc; + fdnode *fdn_tmp; + #ifdef _WIN32 + rc = (uintptr_t)fdn & 0x3; + #endif + fdn = (fdnode *)((uintptr_t)fdn & ~0x3); + fd = fdn->fd; + #ifdef _WIN32 + if (rc == 0x1) { + rc = closesocket(fd); + } + else if (rc == 0x2) { + rc = close(fd); + } + #else + rc = close(fd); + #endif + + if (0 != rc) { + log_error_write(srv, __FILE__, __LINE__, "sds", "close failed ", fd, strerror(errno)); + } + else { + --srv->cur_fds; + } + + fdn_tmp = fdn; + fdn = (fdnode *)fdn->ctx; /* next */ + /*(fdevent_unregister)*/ + fdnode_free(fdn_tmp); + ev->fdarray[fd] = NULL; + } + ev->pendclose = NULL; +} + +int fdevent_event_get_interest(const fdevents *ev, int fd) { + return fd >= 0 ? ev->fdarray[fd]->events : 0; +} + +void fdevent_event_del(fdevents *ev, int *fde_ndx, int fd) { + if (-1 == fd) return; + if ((uintptr_t)ev->fdarray[fd] & 0x3) return; + + if (ev->event_del) *fde_ndx = ev->event_del(ev, *fde_ndx, fd); + ev->fdarray[fd]->events = 0; +} + +void fdevent_event_set(fdevents *ev, int *fde_ndx, int fd, int events) { + if (-1 == fd) return; + + /*(Note: skips registering with kernel if initial events is 0, + * so caller should pass non-zero events for initial registration. + * If never registered due to never being called with non-zero events, + * then FDEVENT_HUP or FDEVENT_ERR will never be returned.) */ + if (ev->fdarray[fd]->events == events) return;/*(no change; nothing to do)*/ + + if (ev->event_set) *fde_ndx = ev->event_set(ev, *fde_ndx, fd, events); + ev->fdarray[fd]->events = events; +} + +void fdevent_event_add(fdevents *ev, int *fde_ndx, int fd, int event) { + int events; + if (-1 == fd) return; + + events = ev->fdarray[fd]->events; + if ((events & event) == event) return; /*(no change; nothing to do)*/ + + events |= event; + if (ev->event_set) *fde_ndx = ev->event_set(ev, *fde_ndx, fd, events); + ev->fdarray[fd]->events = events; +} + +void fdevent_event_clr(fdevents *ev, int *fde_ndx, int fd, int event) { + int events; + if (-1 == fd) return; + + events = ev->fdarray[fd]->events; + if (!(events & event)) return; /*(no change; nothing to do)*/ + + events &= ~event; + if (ev->event_set) *fde_ndx = ev->event_set(ev, *fde_ndx, fd, events); + ev->fdarray[fd]->events = events; +} + +int fdevent_poll(fdevents *ev, int timeout_ms) { + if (ev->poll == NULL) SEGFAULT(); + return ev->poll(ev, timeout_ms); +} + +int fdevent_event_get_revent(fdevents *ev, size_t ndx) { + if (ev->event_get_revent == NULL) SEGFAULT(); + + return ev->event_get_revent(ev, ndx); +} + +int fdevent_event_get_fd(fdevents *ev, size_t ndx) { + if (ev->event_get_fd == NULL) SEGFAULT(); + + return ev->event_get_fd(ev, ndx); +} + +fdevent_handler fdevent_get_handler(fdevents *ev, int fd) { + if (ev->fdarray[fd] == NULL) SEGFAULT(); + if ((uintptr_t)ev->fdarray[fd] & 0x3) return NULL; + if (ev->fdarray[fd]->fd != fd) SEGFAULT(); + + return ev->fdarray[fd]->handler; +} + +void * fdevent_get_context(fdevents *ev, int fd) { + if (ev->fdarray[fd] == NULL) SEGFAULT(); + if ((uintptr_t)ev->fdarray[fd] & 0x3) return NULL; + if (ev->fdarray[fd]->fd != fd) SEGFAULT(); + + return ev->fdarray[fd]->ctx; +} + +void fdevent_setfd_cloexec(int fd) { +#ifdef FD_CLOEXEC + if (fd < 0) return; + force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC)); +#else + UNUSED(fd); +#endif +} + +void fdevent_clrfd_cloexec(int fd) { +#ifdef FD_CLOEXEC + if (fd >= 0) force_assert(-1 != fcntl(fd, F_SETFD, 0)); +#else + UNUSED(fd); +#endif +} + +int fdevent_fcntl_set_nb(fdevents *ev, int fd) { + UNUSED(ev); +#ifdef O_NONBLOCK + return fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR); +#else + UNUSED(fd); + return 0; +#endif +} + +int fdevent_fcntl_set_nb_cloexec(fdevents *ev, int fd) { + fdevent_setfd_cloexec(fd); + return fdevent_fcntl_set_nb(ev, fd); +} + +int fdevent_fcntl_set_nb_cloexec_sock(fdevents *ev, int fd) { +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + if (use_sock_cloexec && use_sock_nonblock) + return 0; +#endif + return fdevent_fcntl_set_nb_cloexec(ev, fd); +} + +int fdevent_socket_cloexec(int domain, int type, int protocol) { + int fd; +#ifdef SOCK_CLOEXEC + if (use_sock_cloexec) + return socket(domain, type | SOCK_CLOEXEC, protocol); +#endif + if (-1 != (fd = socket(domain, type, protocol))) { +#ifdef FD_CLOEXEC + force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC)); +#endif + } + return fd; +} + +int fdevent_socket_nb_cloexec(int domain, int type, int protocol) { + int fd; +#ifdef SOCK_CLOEXEC + if (use_sock_cloexec && use_sock_nonblock) + return socket(domain, type | SOCK_CLOEXEC | SOCK_NONBLOCK, protocol); +#endif + if (-1 != (fd = socket(domain, type, protocol))) { +#ifdef FD_CLOEXEC + force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC)); +#endif +#ifdef O_NONBLOCK + force_assert(-1 != fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR)); +#endif + } + return fd; +} + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +int fdevent_open_cloexec(const char *pathname, int flags, mode_t mode) { +#ifdef O_CLOEXEC + return open(pathname, flags | O_CLOEXEC | O_NOCTTY, mode); +#else + int fd = open(pathname, flags | O_NOCTTY, mode); +#ifdef FD_CLOEXEC + if (fd != -1) + force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC)); +#endif + return fd; +#endif +} + + +int fdevent_open_devnull(void) { + #if defined(_WIN32) + return fdevent_open_cloexec("nul", O_RDWR, 0); + #else + return fdevent_open_cloexec("/dev/null", O_RDWR, 0); + #endif +} + + +int fdevent_open_dirname(char *path) { + /*(handle special cases of no dirname or dirname is root directory)*/ + char * const c = strrchr(path, '/'); + const char * const dname = (NULL != c ? c == path ? "/" : path : "."); + int dfd; + int flags = O_RDONLY; + #ifdef O_DIRECTORY + flags |= O_DIRECTORY; + #endif + if (NULL != c) *c = '\0'; + dfd = fdevent_open_cloexec(dname, flags, 0); + if (NULL != c) *c = '/'; + return dfd; +} + + +int fdevent_accept_listenfd(int listenfd, struct sockaddr *addr, size_t *addrlen) { + int fd; + socklen_t len = (socklen_t) *addrlen; + + #if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + #if defined(__NetBSD__) + const int sock_cloexec = 1; + fd = paccept(listenfd, addr, &len, NULL, SOCK_CLOEXEC | SOCK_NONBLOCK); + #else + int sock_cloexec = use_sock_cloexec; + if (sock_cloexec) { + fd = accept4(listenfd, addr, &len, SOCK_CLOEXEC | SOCK_NONBLOCK); + if (fd >= 0) { + if (!use_sock_nonblock) { + if (0 != fdevent_fcntl_set_nb(NULL, fd)) { + close(fd); + fd = -1; + } + } + } else if (errno == ENOSYS || errno == ENOTSUP) { + fd = accept(listenfd, addr, &len); + sock_cloexec = 0; + } + } + else { + fd = accept(listenfd, addr, &len); + } + #endif + #else + const int sock_cloexec = 0; + fd = accept(listenfd, addr, &len); + #endif + + if (fd >= 0) { + *addrlen = (size_t)len; + if (!sock_cloexec && 0 != fdevent_fcntl_set_nb_cloexec(NULL, fd)) { + close(fd); + fd = -1; + } + } + return fd; +} + + +int fdevent_event_next_fdndx(fdevents *ev, int ndx) { + if (ev->event_next_fdndx) return ev->event_next_fdndx(ev, ndx); + + return -1; +} + + +#ifdef __APPLE__ +#include <crt_externs.h> +#define environ (* _NSGetEnviron()) +#else +extern char **environ; +#endif +char ** fdevent_environ (void) { return environ; } + + +#ifdef FD_CLOEXEC +static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd) { + if (oldfd >= 0) { + if (oldfd != newfd) { + force_assert(oldfd > STDERR_FILENO); + if (newfd != dup2(oldfd, newfd)) return -1; + } + else { + fdevent_clrfd_cloexec(newfd); + } + } + return newfd; +} +#else +static int fdevent_dup2_close_clrfd_cloexec(int oldfd, int newfd, int reuse) { + if (oldfd >= 0) { + if (oldfd != newfd) { + force_assert(oldfd > STDERR_FILENO); + if (newfd != dup2(oldfd, newfd)) return -1; + if (!reuse) close(oldfd); + } + } + return newfd; +} +#endif + + +int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr) { + #ifdef FD_CLOEXEC + if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO)) + return -1; + if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO)) + return -1; + if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO)) + return -1; + #else + if (STDIN_FILENO != fdevent_dup2_close_clrfd_cloexec(fdin, STDIN_FILENO, + fdin == fdout + || fdin == fderr)) + return -1; + if (STDOUT_FILENO != fdevent_dup2_close_clrfd_cloexec(fdout, STDOUT_FILENO, + fdout == fderr)) + return -1; + if (STDERR_FILENO != fdevent_dup2_close_clrfd_cloexec(fderr, STDERR_FILENO, + 0)) + return -1; + #endif + + return 0; +} + + +#include <stdio.h> /* perror() */ +#include <signal.h> /* signal() */ + +pid_t fdevent_fork_execve(const char *name, char *argv[], char *envp[], int fdin, int fdout, int fderr, int dfd) { + #ifdef HAVE_FORK + + pid_t pid = fork(); + if (0 != pid) return pid; /* parent (pid > 0) or fork() error (-1 == pid) */ + + /* child (0 == pid) */ + + if (-1 != dfd) { + if (0 != fchdir(dfd)) + _exit(errno); + close(dfd); + } + + if (0 != fdevent_set_stdin_stdout_stderr(fdin, fdout, fderr)) _exit(errno); + #ifdef FD_CLOEXEC + /*(might not be sufficient for open fds, but modern OS have FD_CLOEXEC)*/ + for (int i = 3; i < 256; ++i) close(i); + #endif + + /* reset_signals which may have been ignored (SIG_IGN) */ + #ifdef SIGTTOU + signal(SIGTTOU, SIG_DFL); + #endif + #ifdef SIGTTIN + signal(SIGTTIN, SIG_DFL); + #endif + #ifdef SIGTSTP + signal(SIGTSTP, SIG_DFL); + #endif + signal(SIGPIPE, SIG_DFL); + + execve(name, argv, envp ? envp : environ); + + if (0 == memcmp(argv[0], "/bin/sh", sizeof("/bin/sh")-1) + && argv[1] && 0 == memcmp(argv[1], "-c", sizeof("-c")-1)) + perror(argv[2]); + else + perror(argv[0]); + _exit(errno); + + #else + + UNUSED(name); + UNUSED(argv); + UNUSED(envp); + UNUSED(fdin); + UNUSED(fdout); + UNUSED(fderr); + UNUSED(dfd); + return (pid_t)-1; + + #endif +} + + +typedef struct fdevent_cmd_pipe { + pid_t pid; + int fds[2]; + const char *cmd; + time_t start; +} fdevent_cmd_pipe; + +typedef struct fdevent_cmd_pipes { + fdevent_cmd_pipe *ptr; + size_t used; + size_t size; +} fdevent_cmd_pipes; + +static fdevent_cmd_pipes cmd_pipes; + + +static pid_t fdevent_open_logger_pipe_spawn(const char *logger, int rfd) { + char *args[4]; + int devnull = fdevent_open_devnull(); + pid_t pid; + + if (-1 == devnull) { + return -1; + } + + *(const char **)&args[0] = "/bin/sh"; + *(const char **)&args[1] = "-c"; + *(const char **)&args[2] = logger; + args[3] = NULL; + + pid = fdevent_fork_execve(args[0], args, NULL, rfd, devnull, devnull, -1); + + if (pid > 0) { + close(devnull); + } + else { + int errnum = errno; + close(devnull); + errno = errnum; + } + return pid; +} + + +static void fdevent_restart_logger_pipe(fdevent_cmd_pipe *fcp, time_t ts) { + if (fcp->pid > 0) return; /* assert */ + if (fcp->start + 5 < ts) { /* limit restart to once every 5 sec */ + /* restart child process using existing pipe fds */ + fcp->start = ts; + fcp->pid = fdevent_open_logger_pipe_spawn(fcp->cmd, fcp->fds[0]); + } +} + + +void fdevent_restart_logger_pipes(time_t ts) { + for (size_t i = 0; i < cmd_pipes.used; ++i) { + fdevent_cmd_pipe * const fcp = cmd_pipes.ptr+i; + if (fcp->pid > 0) continue; + fdevent_restart_logger_pipe(fcp, ts); + } +} + + +int fdevent_waitpid_logger_pipe_pid(pid_t pid, time_t ts) { + for (size_t i = 0; i < cmd_pipes.used; ++i) { + fdevent_cmd_pipe * const fcp = cmd_pipes.ptr+i; + if (pid != fcp->pid) continue; + fcp->pid = -1; + fdevent_restart_logger_pipe(fcp, ts); + return 1; + } + return 0; +} + + +void fdevent_clr_logger_pipe_pids(void) { + for (size_t i = 0; i < cmd_pipes.used; ++i) { + fdevent_cmd_pipe *fcp = cmd_pipes.ptr+i; + fcp->pid = -1; + } +} + + +int fdevent_reaped_logger_pipe(pid_t pid) { + for (size_t i = 0; i < cmd_pipes.used; ++i) { + fdevent_cmd_pipe *fcp = cmd_pipes.ptr+i; + if (fcp->pid == pid) { + time_t ts = time(NULL); + if (fcp->start + 5 < ts) { /* limit restart to once every 5 sec */ + fcp->start = ts; + fcp->pid = fdevent_open_logger_pipe_spawn(fcp->cmd,fcp->fds[0]); + return 1; + } + else { + fcp->pid = -1; + return -1; + } + } + } + return 0; +} + + +void fdevent_close_logger_pipes(void) { + for (size_t i = 0; i < cmd_pipes.used; ++i) { + fdevent_cmd_pipe *fcp = cmd_pipes.ptr+i; + close(fcp->fds[0]); + if (fcp->fds[1] != STDERR_FILENO) close(fcp->fds[1]); + } + free(cmd_pipes.ptr); + cmd_pipes.ptr = NULL; + cmd_pipes.used = 0; + cmd_pipes.size = 0; +} + + +void fdevent_breakagelog_logger_pipe(int fd) { + for (size_t i = 0; i < cmd_pipes.used; ++i) { + fdevent_cmd_pipe *fcp = cmd_pipes.ptr+i; + if (fcp->fds[1] != fd) continue; + fcp->fds[1] = STDERR_FILENO; + break; + } +} + + +static void fdevent_init_logger_pipe(const char *cmd, int fds[2], pid_t pid) { + fdevent_cmd_pipe *fcp; + if (cmd_pipes.used == cmd_pipes.size) { + cmd_pipes.size += 4; + cmd_pipes.ptr = + realloc(cmd_pipes.ptr, cmd_pipes.size * sizeof(fdevent_cmd_pipe)); + force_assert(cmd_pipes.ptr); + } + fcp = cmd_pipes.ptr + cmd_pipes.used++; + fcp->cmd = cmd; /* note: cmd must persist in memory (or else copy here) */ + fcp->fds[0] = fds[0]; + fcp->fds[1] = fds[1]; + fcp->pid = pid; + fcp->start = time(NULL); +} + + +static int fdevent_open_logger_pipe(const char *logger) { + int fds[2]; + pid_t pid; + if (pipe(fds)) { + return -1; + } + fdevent_setfd_cloexec(fds[0]); + fdevent_setfd_cloexec(fds[1]); + /*(nonblocking write() from lighttpd)*/ + if (0 != fdevent_fcntl_set_nb(NULL, fds[1])) { /*(ignore)*/ } + + pid = fdevent_open_logger_pipe_spawn(logger, fds[0]); + + if (pid > 0) { + fdevent_init_logger_pipe(logger, fds, pid); + return fds[1]; + } + else { + int errnum = errno; + close(fds[0]); + close(fds[1]); + errno = errnum; + return -1; + } +} + + +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +int fdevent_open_logger(const char *logger) { + if (logger[0] != '|') { + int flags = O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE; + return fdevent_open_cloexec(logger, flags, 0644); + } + else { + return fdevent_open_logger_pipe(logger+1); /*(skip the '|')*/ + } +} + +int fdevent_cycle_logger(const char *logger, int *curfd) { + if (logger[0] != '|') { + int fd = fdevent_open_logger(logger); + if (-1 == fd) return -1; /*(error; leave *curfd as-is)*/ + if (-1 != *curfd) close(*curfd); + *curfd = fd; + } + return *curfd; +} + + +#ifndef MSG_DONTWAIT +#define MSG_DONTWAIT 0 +#endif +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + + +ssize_t fdevent_socket_read_discard (int fd, char *buf, size_t sz, int family, int so_type) { + #if defined(MSG_TRUNC) && defined(__linux__) + if ((family == AF_INET || family == AF_INET6) && so_type == SOCK_STREAM) { + ssize_t len = recv(fd, buf, sz, MSG_TRUNC|MSG_DONTWAIT|MSG_NOSIGNAL); + if (len >= 0 || errno != EINVAL) return len; + } + #else + UNUSED(family); + UNUSED(so_type); + #endif + return read(fd, buf, sz); +} + + +#include <sys/ioctl.h> +#ifdef HAVE_SYS_FILIO_H +#include <sys/filio.h> /* FIONREAD (for illumos (OpenIndiana)) */ +#endif +#ifdef _WIN32 +#include <winsock2.h> +#endif +int fdevent_ioctl_fionread (int fd, int fdfmt, int *toread) { + #ifdef _WIN32 + if (fdfmt != S_IFSOCK) { errno = ENOTSOCK; return -1; } + return ioctlsocket(fd, FIONREAD, toread); + #else + #ifdef __CYGWIN__ + /*(cygwin supports FIONREAD on pipes, not sockets)*/ + if (fdfmt != S_IFIFO) { errno = EOPNOTSUPP; return -1; } + #else + UNUSED(fdfmt); + #endif + return ioctl(fd, FIONREAD, toread); + #endif +} + + +int fdevent_connect_status(int fd) { + /* try to finish the connect() */ + /*(should be called after connect() only when fd is writable (POLLOUT))*/ + int opt; + socklen_t len = sizeof(opt); + return (0 == getsockopt(fd,SOL_SOCKET,SO_ERROR,&opt,&len)) ? opt : errno; +} + + +#include <netinet/tcp.h> +#if (defined(__APPLE__) && defined(__MACH__)) \ + || defined(__FreeBSD__) || defined(__NetBSD__) \ + || defined(__OpenBSD__) || defined(__DragonFly__) +#include <netinet/tcp_fsm.h> +#endif + +/* fd must be TCP socket (AF_INET, AF_INET6), end-of-stream recv() 0 bytes */ +int fdevent_is_tcp_half_closed(int fd) { + #ifdef TCP_CONNECTION_INFO /* Darwin */ + struct tcp_connection_info tcpi; + socklen_t tlen = sizeof(tcpi); + return (0 == getsockopt(fd, IPPROTO_TCP, TCP_CONNECTION_INFO, &tcpi, &tlen) + && tcpi.tcpi_state == TCPS_CLOSE_WAIT); + #elif defined(TCP_INFO) && defined(TCPS_CLOSE_WAIT) + /* FreeBSD, NetBSD (not present in OpenBSD or DragonFlyBSD) */ + struct tcp_info tcpi; + socklen_t tlen = sizeof(tcpi); + return (0 == getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpi, &tlen) + && tcpi.tcpi_state == TCPS_CLOSE_WAIT); + #elif defined(TCP_INFO) && defined(__linux__) + /* Linux (TCP_CLOSE_WAIT is enum, so can not #ifdef TCP_CLOSE_WAIT) */ + struct tcp_info tcpi; + socklen_t tlen = sizeof(tcpi);/*SOL_TCP == IPPROTO_TCP*/ + return (0 == getsockopt(fd, SOL_TCP, TCP_INFO, &tcpi, &tlen) + && tcpi.tcpi_state == TCP_CLOSE_WAIT); + #else + UNUSED(fd); + /*(0 != getpeername() error might indicate TCP RST, but success + * would not differentiate between half-close and full-close)*/ + return 0; /* false (not half-closed) or TCP state unknown */ + #endif +} + + +int fdevent_set_tcp_nodelay (const int fd, const int opt) +{ + return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); +} + + +int fdevent_set_so_reuseaddr (const int fd, const int opt) +{ + return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/fdevent.h b/data/lighttpd/lighttpd-1.4.53/src/fdevent.h new file mode 100644 index 000000000..ced9602a7 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/fdevent.h @@ -0,0 +1,97 @@ +#ifndef _FDEVENT_H_ +#define _FDEVENT_H_ +#include "first.h" + +#include "base_decls.h" + +struct fdevents; /* declaration */ +typedef struct fdevents fdevents; + +typedef handler_t (*fdevent_handler)(struct server *srv, void *ctx, int revents); + +/* these are the POLL* values from <bits/poll.h> (linux poll) + */ + +#define FDEVENT_IN BV(0) +#define FDEVENT_PRI BV(1) +#define FDEVENT_OUT BV(2) +#define FDEVENT_ERR BV(3) +#define FDEVENT_HUP BV(4) +#define FDEVENT_NVAL BV(5) +#define FDEVENT_RDHUP BV(13) + +#define FDEVENT_STREAM_REQUEST BV(0) +#define FDEVENT_STREAM_REQUEST_BUFMIN BV(1) +#define FDEVENT_STREAM_REQUEST_POLLRDHUP BV(12) +#define FDEVENT_STREAM_REQUEST_TCP_FIN BV(13) +#define FDEVENT_STREAM_REQUEST_BACKEND_SHUT_WR BV(14) +#define FDEVENT_STREAM_REQUEST_POLLIN BV(15) + +#define FDEVENT_STREAM_RESPONSE BV(0) +#define FDEVENT_STREAM_RESPONSE_BUFMIN BV(1) +#define FDEVENT_STREAM_RESPONSE_POLLRDHUP BV(15) + +int fdevent_config(server *srv); +const char * fdevent_show_event_handlers(void); +fdevents *fdevent_init(struct server *srv); +int fdevent_reset(fdevents *ev); /* "init" after fork() */ +void fdevent_free(fdevents *ev); + +int fdevent_event_get_interest(const fdevents *ev, int fd); +void fdevent_event_set(fdevents *ev, int *fde_ndx, int fd, int events); /* events can be FDEVENT_IN, FDEVENT_OUT or FDEVENT_IN | FDEVENT_OUT */ +void fdevent_event_add(fdevents *ev, int *fde_ndx, int fd, int event); /* events can be FDEVENT_IN or FDEVENT_OUT */ +void fdevent_event_clr(fdevents *ev, int *fde_ndx, int fd, int event); /* events can be FDEVENT_IN or FDEVENT_OUT */ +void fdevent_event_del(fdevents *ev, int *fde_ndx, int fd); +int fdevent_event_get_revent(fdevents *ev, size_t ndx); +int fdevent_event_get_fd(fdevents *ev, size_t ndx); +fdevent_handler fdevent_get_handler(fdevents *ev, int fd); +void * fdevent_get_context(fdevents *ev, int fd); + +int fdevent_event_next_fdndx(fdevents *ev, int ndx); + +int fdevent_poll(fdevents *ev, int timeout_ms); + +int fdevent_register(fdevents *ev, int fd, fdevent_handler handler, void *ctx); +int fdevent_unregister(fdevents *ev, int fd); +void fdevent_sched_close(fdevents *ev, int fd, int issock); +void fdevent_sched_run(struct server *srv, fdevents *ev); + +void fdevent_setfd_cloexec(int fd); +void fdevent_clrfd_cloexec(int fd); +int fdevent_fcntl_set_nb(fdevents *ev, int fd); +int fdevent_fcntl_set_nb_cloexec(fdevents *ev, int fd); +int fdevent_fcntl_set_nb_cloexec_sock(fdevents *ev, int fd); +int fdevent_socket_cloexec(int domain, int type, int protocol); +int fdevent_socket_nb_cloexec(int domain, int type, int protocol); +int fdevent_open_cloexec(const char *pathname, int flags, mode_t mode); + +struct sockaddr; +int fdevent_accept_listenfd(int listenfd, struct sockaddr *addr, size_t *addrlen); + +char ** fdevent_environ(void); +int fdevent_open_devnull(void); +int fdevent_open_dirname(char *path); +int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr); +pid_t fdevent_fork_execve(const char *name, char *argv[], char *envp[], int fdin, int fdout, int fderr, int dfd); +int fdevent_open_logger(const char *logger); +int fdevent_cycle_logger(const char *logger, int *curfd); +int fdevent_reaped_logger_pipe(pid_t pid); +int fdevent_waitpid_logger_pipe_pid(pid_t pid, time_t ts); +void fdevent_restart_logger_pipes(time_t ts); +void fdevent_close_logger_pipes(void); +void fdevent_breakagelog_logger_pipe(int fd); +void fdevent_clr_logger_pipe_pids(void); + +ssize_t fdevent_socket_read_discard (int fd, char *buf, size_t sz, int family, int so_type); + +int fdevent_ioctl_fionread (int fd, int fdfmt, int *toread); + +int fdevent_connect_status(int fd); + +/* fd must be TCP socket (AF_INET, AF_INET6), end-of-stream recv() 0 bytes */ +int fdevent_is_tcp_half_closed(int fd); +int fdevent_set_tcp_nodelay (const int fd, const int opt); + +int fdevent_set_so_reuseaddr (const int fd, const int opt); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/fdevent_freebsd_kqueue.c b/data/lighttpd/lighttpd-1.4.53/src/fdevent_freebsd_kqueue.c new file mode 100644 index 000000000..a1344c603 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/fdevent_freebsd_kqueue.c @@ -0,0 +1,232 @@ +#include "first.h" + +#include "fdevent_impl.h" +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + +#include <sys/types.h> + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> + +#ifdef FDEVENT_USE_FREEBSD_KQUEUE +# include <sys/event.h> +# include <sys/time.h> + +static void fdevent_freebsd_kqueue_free(fdevents *ev) { + close(ev->kq_fd); + free(ev->kq_results); +} + +static int fdevent_freebsd_kqueue_event_del(fdevents *ev, int fde_ndx, int fd) { + int ret, n = 0; + struct kevent kev[2]; + struct timespec ts; + int oevents; + + if (fde_ndx < 0) return -1; + + oevents = ev->fdarray[fd]->events; + + if (oevents & FDEVENT_IN) { + EV_SET(&kev[n], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); + n++; + } + if (oevents & FDEVENT_OUT) { + EV_SET(&kev[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + n++; + } + + if (0 == n) return -1; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + ret = kevent(ev->kq_fd, + kev, n, + NULL, 0, + &ts); + + if (ret == -1) { + log_error_write(ev->srv, __FILE__, __LINE__, "SS", + "kqueue event delete failed: ", strerror(errno)); + + return -1; + } + + return -1; +} + +static int fdevent_freebsd_kqueue_event_set(fdevents *ev, int fde_ndx, int fd, int events) { + int ret, n = 0; + struct kevent kev[2]; + struct timespec ts; + int oevents = ev->fdarray[fd]->events; + int addevents = events & ~oevents; + int delevents = ~events & oevents; + + UNUSED(fde_ndx); + + if (events == oevents) return fd; + + if (addevents & FDEVENT_IN) { + EV_SET(&kev[n], fd, EVFILT_READ, EV_ADD, 0, 0, NULL); + n++; + } else if (delevents & FDEVENT_IN) { + EV_SET(&kev[n], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); + n++; + } + if (addevents & FDEVENT_OUT) { + EV_SET(&kev[n], fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); + n++; + } else if (delevents & FDEVENT_OUT) { + EV_SET(&kev[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + n++; + } + + if (0 == n) return fd; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + ret = kevent(ev->kq_fd, + kev, n, + NULL, 0, + &ts); + + if (ret == -1) { + log_error_write(ev->srv, __FILE__, __LINE__, "SS", + "kqueue event set failed: ", strerror(errno)); + + return -1; + } + + return fd; +} + +static int fdevent_freebsd_kqueue_poll(fdevents *ev, int timeout_ms) { + int ret; + struct timespec ts; + + ts.tv_sec = timeout_ms / 1000; + ts.tv_nsec = (timeout_ms % 1000) * 1000000; + + ret = kevent(ev->kq_fd, + NULL, 0, + ev->kq_results, ev->maxfds, + &ts); + + if (ret == -1) { + switch(errno) { + case EINTR: + /* we got interrupted, perhaps just a SIGCHLD of a CGI script */ + return 0; + default: + log_error_write(ev->srv, __FILE__, __LINE__, "SS", + "kqueue failed polling: ", strerror(errno)); + break; + } + } + + return ret; +} + +static int fdevent_freebsd_kqueue_event_get_revent(fdevents *ev, size_t ndx) { + int events = 0, e; + + int filt = e = ev->kq_results[ndx].filter; + + if (e == EVFILT_READ) { + events |= FDEVENT_IN; + } else if (e == EVFILT_WRITE) { + events |= FDEVENT_OUT; + } + + e = ev->kq_results[ndx].flags; + + if (e & EV_EOF) { + if (filt == EVFILT_READ) { + events |= FDEVENT_RDHUP; + } else { + events |= FDEVENT_HUP; + } + } + + if (e & EV_ERROR) { + events |= FDEVENT_ERR; + } + + return events; +} + +static int fdevent_freebsd_kqueue_event_get_fd(fdevents *ev, size_t ndx) { + return ev->kq_results[ndx].ident; +} + +static int fdevent_freebsd_kqueue_event_next_fdndx(fdevents *ev, int ndx) { + UNUSED(ev); + + return (ndx < 0) ? 0 : ndx + 1; +} + +static int fdevent_freebsd_kqueue_reset(fdevents *ev) { + if (-1 == (ev->kq_fd = kqueue())) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "kqueue failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); + + return -1; + } + + return 0; +} + + +int fdevent_freebsd_kqueue_init(fdevents *ev) { + ev->type = FDEVENT_HANDLER_FREEBSD_KQUEUE; +#define SET(x) \ + ev->x = fdevent_freebsd_kqueue_##x; + + SET(free); + SET(poll); + SET(reset); + + SET(event_del); + SET(event_set); + + SET(event_next_fdndx); + SET(event_get_fd); + SET(event_get_revent); + + ev->kq_fd = -1; + + ev->kq_results = calloc(ev->maxfds, sizeof(*ev->kq_results)); + force_assert(NULL != ev->kq_results); + + /* check that kqueue works */ + + if (-1 == (ev->kq_fd = kqueue())) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "kqueue failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); + + return -1; + } + + close(ev->kq_fd); + ev->kq_fd = -1; + + return 0; +} +#else +int fdevent_freebsd_kqueue_init(fdevents *ev) { + UNUSED(ev); + + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "kqueue not available, try to set server.event-handler = \"poll\" or \"select\""); + + return -1; +} +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/fdevent_impl.h b/data/lighttpd/lighttpd-1.4.53/src/fdevent_impl.h new file mode 100644 index 000000000..70e8d94f3 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/fdevent_impl.h @@ -0,0 +1,154 @@ +#ifndef INCLUDED_FDEVENT_IMPL_H +#define INCLUDED_FDEVENT_IMPL_H +#include "first.h" + +/* select event-system */ + +#if defined(HAVE_EPOLL_CTL) && defined(HAVE_SYS_EPOLL_H) +# define FDEVENT_USE_LINUX_EPOLL +struct epoll_event; /* declaration */ +#endif + +/* MacOS 10.3.x has poll.h under /usr/include/, all other unixes + * under /usr/include/sys/ */ +#if defined HAVE_POLL && (defined(HAVE_SYS_POLL_H) || defined(HAVE_POLL_H)) +# define FDEVENT_USE_POLL +struct pollfd; /* declaration */ +#endif + +#if defined HAVE_SELECT +# ifdef __WIN32 +# include <winsock2.h> +# endif +# define FDEVENT_USE_SELECT +# ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +# endif +#endif + +#if defined HAVE_SYS_DEVPOLL_H && defined(__sun) +# define FDEVENT_USE_SOLARIS_DEVPOLL +struct pollfd; /* declaration */ +#endif + +#if defined HAVE_PORT_H && defined HAVE_PORT_CREATE && defined(__sun) +# define FDEVENT_USE_SOLARIS_PORT +# include <port.h> +#endif + +#if defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE +# define FDEVENT_USE_FREEBSD_KQUEUE +struct kevent; /* declaration */ +#endif + +#if defined HAVE_LIBEV +# define FDEVENT_USE_LIBEV +struct ev_loop; /* declaration */ +#endif + +#include "base_decls.h" +#include "fdevent.h" /* (*fdevent_handler) */ + +typedef enum { + FDEVENT_HANDLER_UNSET, + FDEVENT_HANDLER_SELECT, + FDEVENT_HANDLER_POLL, + FDEVENT_HANDLER_LINUX_SYSEPOLL, + FDEVENT_HANDLER_SOLARIS_DEVPOLL, + FDEVENT_HANDLER_SOLARIS_PORT, + FDEVENT_HANDLER_FREEBSD_KQUEUE, + FDEVENT_HANDLER_LIBEV +} fdevent_handler_t; + +typedef struct _fdnode { + fdevent_handler handler; + void *ctx; + void *handler_ctx; + int fd; + int events; +} fdnode; + +/** + * array of unused fd's + * + */ + +#ifdef FDEVENT_USE_POLL +typedef struct { + int *ptr; + + size_t used; + size_t size; +} buffer_int; +#endif + +struct fdevents { + struct server *srv; + fdevent_handler_t type; + + fdnode **fdarray; + fdnode *pendclose; + size_t maxfds; + + #ifdef FDEVENT_USE_LINUX_EPOLL + int epoll_fd; + struct epoll_event *epoll_events; + #endif + #ifdef FDEVENT_USE_POLL + struct pollfd *pollfds; + + size_t size; + size_t used; + + buffer_int unused; + #endif + #ifdef FDEVENT_USE_SELECT + fd_set select_read; + fd_set select_write; + fd_set select_error; + + fd_set select_set_read; + fd_set select_set_write; + fd_set select_set_error; + + int select_max_fd; + #endif + #ifdef FDEVENT_USE_SOLARIS_DEVPOLL + int devpoll_fd; + struct pollfd *devpollfds; + #endif + #ifdef FDEVENT_USE_SOLARIS_PORT + port_event_t *port_events; + #endif + #ifdef FDEVENT_USE_FREEBSD_KQUEUE + int kq_fd; + struct kevent *kq_results; + #endif + #ifdef FDEVENT_USE_SOLARIS_PORT + int port_fd; + #endif + #ifdef FDEVENT_USE_LIBEV + struct ev_loop *libev_loop; + #endif + int (*reset)(struct fdevents *ev); + void (*free)(struct fdevents *ev); + + int (*event_set)(struct fdevents *ev, int fde_ndx, int fd, int events); + int (*event_del)(struct fdevents *ev, int fde_ndx, int fd); + int (*event_get_revent)(struct fdevents *ev, size_t ndx); + int (*event_get_fd)(struct fdevents *ev, size_t ndx); + + int (*event_next_fdndx)(struct fdevents *ev, int ndx); + + int (*poll)(struct fdevents *ev, int timeout_ms); +}; + +int fdevent_select_init(struct fdevents *ev); +int fdevent_poll_init(struct fdevents *ev); +int fdevent_linux_sysepoll_init(struct fdevents *ev); +int fdevent_solaris_devpoll_init(struct fdevents *ev); +int fdevent_solaris_port_init(struct fdevents *ev); +int fdevent_freebsd_kqueue_init(struct fdevents *ev); +int fdevent_libev_init(struct fdevents *ev); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/fdevent_libev.c b/data/lighttpd/lighttpd-1.4.53/src/fdevent_libev.c new file mode 100644 index 000000000..bc24359e6 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/fdevent_libev.c @@ -0,0 +1,177 @@ +#include "first.h" + +#include <stdlib.h> + +#include "fdevent_impl.h" +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + +#ifdef FDEVENT_USE_LIBEV + +# include <ev.h> + +static void io_watcher_cb(struct ev_loop *loop, ev_io *w, int revents) { + fdevents *ev = w->data; + fdevent_handler handler = fdevent_get_handler(ev, w->fd); + void *context = fdevent_get_context(ev, w->fd); + int r = 0; + UNUSED(loop); + if (NULL == handler) return; + + if (revents & EV_READ) r |= FDEVENT_IN; + if (revents & EV_WRITE) r |= FDEVENT_OUT; + if (revents & EV_ERROR) r |= FDEVENT_ERR; + + switch (r = (*handler)(ev->srv, context, r)) { + case HANDLER_FINISHED: + case HANDLER_GO_ON: + case HANDLER_WAIT_FOR_EVENT: + case HANDLER_WAIT_FOR_FD: + break; + case HANDLER_ERROR: + /* should never happen */ + SEGFAULT(); + break; + default: + log_error_write(ev->srv, __FILE__, __LINE__, "d", r); + break; + } +} + +static void fdevent_libev_free(fdevents *ev) { + UNUSED(ev); +} + +static int fdevent_libev_event_del(fdevents *ev, int fde_ndx, int fd) { + fdnode *fdn; + ev_io *watcher; + + if (-1 == fde_ndx) return -1; + + fdn = ev->fdarray[fd]; + watcher = fdn->handler_ctx; + + if (!watcher) return -1; + + ev_io_stop(ev->libev_loop, watcher); + free(watcher); + fdn->handler_ctx = NULL; + + return -1; +} + +static int fdevent_libev_event_set(fdevents *ev, int fde_ndx, int fd, int events) { + fdnode *fdn = ev->fdarray[fd]; + ev_io *watcher = fdn->handler_ctx; + int ev_events = 0; + UNUSED(fde_ndx); + + if (events & FDEVENT_IN) ev_events |= EV_READ; + if (events & FDEVENT_OUT) ev_events |= EV_WRITE; + + if (!watcher) { + fdn->handler_ctx = watcher = calloc(1, sizeof(ev_io)); + force_assert(watcher); + + ev_io_init(watcher, io_watcher_cb, fd, ev_events); + watcher->data = ev; + ev_io_start(ev->libev_loop, watcher); + } else { + if ((watcher->events & (EV_READ | EV_WRITE)) != ev_events) { + ev_io_stop(ev->libev_loop, watcher); + ev_io_set(watcher, watcher->fd, ev_events); + ev_io_start(ev->libev_loop, watcher); + } + } + + return fd; +} + +static void timeout_watcher_cb(struct ev_loop *loop, ev_timer *w, int revents) { + UNUSED(loop); + UNUSED(w); + UNUSED(revents); +} + +static ev_timer timeout_watcher; + +static int fdevent_libev_poll(fdevents *ev, int timeout_ms) { + timeout_watcher.repeat = (timeout_ms > 0) ? timeout_ms/1000.0 : 0.001; + + ev_timer_again(ev->libev_loop, &timeout_watcher); + ev_run(ev->libev_loop, EVRUN_ONCE); + + return 0; +} + +static int fdevent_libev_event_get_revent(fdevents *ev, size_t ndx) { + UNUSED(ev); + UNUSED(ndx); + + return 0; +} + +static int fdevent_libev_event_get_fd(fdevents *ev, size_t ndx) { + UNUSED(ev); + UNUSED(ndx); + + return -1; +} + +static int fdevent_libev_event_next_fdndx(fdevents *ev, int ndx) { + UNUSED(ev); + UNUSED(ndx); + + return -1; +} + +static int fdevent_libev_reset(fdevents *ev) { + UNUSED(ev); + + ev_default_fork(); + + return 0; +} + +int fdevent_libev_init(fdevents *ev) { + struct ev_timer * const timer = &timeout_watcher; + memset(timer, 0, sizeof(*timer)); + + ev->type = FDEVENT_HANDLER_LIBEV; +#define SET(x) \ + ev->x = fdevent_libev_##x; + + SET(free); + SET(poll); + SET(reset); + + SET(event_del); + SET(event_set); + + SET(event_next_fdndx); + SET(event_get_fd); + SET(event_get_revent); + + if (NULL == (ev->libev_loop = ev_default_loop(0))) { + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "ev_default_loop failed , try to set server.event-handler = \"poll\" or \"select\""); + + return -1; + } + + ev_timer_init(timer, timeout_watcher_cb, 0.0, 1.0); + + return 0; +} + +#else +int fdevent_libev_init(fdevents *ev) { + UNUSED(ev); + + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "libev not supported, try to set server.event-handler = \"poll\" or \"select\""); + + return -1; +} +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/fdevent_linux_sysepoll.c b/data/lighttpd/lighttpd-1.4.53/src/fdevent_linux_sysepoll.c new file mode 100644 index 000000000..453132df9 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/fdevent_linux_sysepoll.c @@ -0,0 +1,166 @@ +#include "first.h" + +#include "fdevent_impl.h" +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + +#include <sys/types.h> + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifdef FDEVENT_USE_LINUX_EPOLL + +# include <sys/epoll.h> + +#ifndef EPOLLRDHUP +#define EPOLLRDHUP 0 +#endif + +static void fdevent_linux_sysepoll_free(fdevents *ev) { + close(ev->epoll_fd); + free(ev->epoll_events); +} + +static int fdevent_linux_sysepoll_event_del(fdevents *ev, int fde_ndx, int fd) { + struct epoll_event ep; + + if (fde_ndx < 0) return -1; + + memset(&ep, 0, sizeof(ep)); + + ep.data.fd = fd; + ep.data.ptr = NULL; + + if (0 != epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, fd, &ep)) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "epoll_ctl failed: ", strerror(errno), ", dying"); + + SEGFAULT(); + + return 0; + } + + + return -1; +} + +static int fdevent_linux_sysepoll_event_set(fdevents *ev, int fde_ndx, int fd, int events) { + struct epoll_event ep; + int add = 0; + + if (fde_ndx == -1) add = 1; + + memset(&ep, 0, sizeof(ep)); + + ep.events = 0; + + if (events & FDEVENT_IN) ep.events |= EPOLLIN; + if (events & FDEVENT_OUT) ep.events |= EPOLLOUT; + if (events & FDEVENT_RDHUP) ep.events |= EPOLLRDHUP; + + /** + * + * with EPOLLET we don't get a FDEVENT_HUP + * if the close is delay after everything has + * sent. + * + */ + + ep.events |= EPOLLERR | EPOLLHUP /* | EPOLLET */; + + ep.data.ptr = NULL; + ep.data.fd = fd; + + if (0 != epoll_ctl(ev->epoll_fd, add ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, fd, &ep)) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "epoll_ctl failed: ", strerror(errno), ", dying"); + + SEGFAULT(); + + return 0; + } + + return fd; +} + +static int fdevent_linux_sysepoll_poll(fdevents *ev, int timeout_ms) { + return epoll_wait(ev->epoll_fd, ev->epoll_events, ev->maxfds, timeout_ms); +} + +static int fdevent_linux_sysepoll_event_get_revent(fdevents *ev, size_t ndx) { + int events = 0, e; + + e = ev->epoll_events[ndx].events; + if (e & EPOLLIN) events |= FDEVENT_IN; + if (e & EPOLLOUT) events |= FDEVENT_OUT; + if (e & EPOLLERR) events |= FDEVENT_ERR; + if (e & EPOLLHUP) events |= FDEVENT_HUP; + if (e & EPOLLPRI) events |= FDEVENT_PRI; + if (e & EPOLLRDHUP) events |= FDEVENT_RDHUP; + + return events; +} + +static int fdevent_linux_sysepoll_event_get_fd(fdevents *ev, size_t ndx) { +# if 0 + log_error_write(ev->srv, __FILE__, __LINE__, "SD, D", + "fdevent_linux_sysepoll_event_get_fd: ", (int) ndx, ev->epoll_events[ndx].data.fd); +# endif + + return ev->epoll_events[ndx].data.fd; +} + +static int fdevent_linux_sysepoll_event_next_fdndx(fdevents *ev, int ndx) { + size_t i; + + UNUSED(ev); + + i = (ndx < 0) ? 0 : ndx + 1; + + return i; +} + +int fdevent_linux_sysepoll_init(fdevents *ev) { + ev->type = FDEVENT_HANDLER_LINUX_SYSEPOLL; +#define SET(x) \ + ev->x = fdevent_linux_sysepoll_##x; + + SET(free); + SET(poll); + + SET(event_del); + SET(event_set); + + SET(event_next_fdndx); + SET(event_get_fd); + SET(event_get_revent); + + if (-1 == (ev->epoll_fd = epoll_create(ev->maxfds))) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "epoll_create failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); + + return -1; + } + + fdevent_setfd_cloexec(ev->epoll_fd); + + ev->epoll_events = malloc(ev->maxfds * sizeof(*ev->epoll_events)); + force_assert(NULL != ev->epoll_events); + + return 0; +} + +#else +int fdevent_linux_sysepoll_init(fdevents *ev) { + UNUSED(ev); + + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "linux-sysepoll not supported, try to set server.event-handler = \"poll\" or \"select\""); + + return -1; +} +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/fdevent_poll.c b/data/lighttpd/lighttpd-1.4.53/src/fdevent_poll.c new file mode 100644 index 000000000..76e941396 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/fdevent_poll.c @@ -0,0 +1,211 @@ +#include "first.h" + +#include "fdevent_impl.h" +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + +#include <sys/types.h> + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifdef FDEVENT_USE_POLL + +# ifdef HAVE_POLL_H +# include <poll.h> +# else +# include <sys/poll.h> +# endif + +#ifndef POLLRDHUP +#define POLLRDHUP 0 +#endif + +static void fdevent_poll_free(fdevents *ev) { + free(ev->pollfds); + if (ev->unused.ptr) free(ev->unused.ptr); +} + +static int fdevent_poll_event_del(fdevents *ev, int fde_ndx, int fd) { + if (fde_ndx < 0) return -1; + + if ((size_t)fde_ndx >= ev->used) { + log_error_write(ev->srv, __FILE__, __LINE__, "SdD", + "del! out of range ", fde_ndx, (int) ev->used); + SEGFAULT(); + } + + if (ev->pollfds[fde_ndx].fd == fd) { + size_t k = fde_ndx; + + ev->pollfds[k].fd = -1; + /* ev->pollfds[k].events = 0; */ + /* ev->pollfds[k].revents = 0; */ + + if (ev->unused.size == 0) { + ev->unused.size = 16; + ev->unused.ptr = malloc(sizeof(*(ev->unused.ptr)) * ev->unused.size); + force_assert(NULL != ev->unused.ptr); + } else if (ev->unused.size == ev->unused.used) { + ev->unused.size += 16; + ev->unused.ptr = realloc(ev->unused.ptr, sizeof(*(ev->unused.ptr)) * ev->unused.size); + force_assert(NULL != ev->unused.ptr); + } + + ev->unused.ptr[ev->unused.used++] = k; + } else { + log_error_write(ev->srv, __FILE__, __LINE__, "SdD", + "del! ", ev->pollfds[fde_ndx].fd, fd); + + SEGFAULT(); + } + + return -1; +} + +#if 0 +static int fdevent_poll_event_compress(fdevents *ev) { + size_t j; + + if (ev->used == 0) return 0; + if (ev->unused.used != 0) return 0; + + for (j = ev->used - 1; j + 1 > 0 && ev->pollfds[j].fd == -1; j--) ev->used--; + + return 0; +} +#endif + +static int fdevent_poll_event_set(fdevents *ev, int fde_ndx, int fd, int events) { + int pevents = 0; + if (events & FDEVENT_IN) pevents |= POLLIN; + if (events & FDEVENT_OUT) pevents |= POLLOUT; + if (events & FDEVENT_RDHUP) pevents |= POLLRDHUP; + + /* known index */ + + if (fde_ndx != -1) { + if (ev->pollfds[fde_ndx].fd == fd) { + ev->pollfds[fde_ndx].events = pevents; + + return fde_ndx; + } + log_error_write(ev->srv, __FILE__, __LINE__, "SdD", + "set: ", fde_ndx, ev->pollfds[fde_ndx].fd); + SEGFAULT(); + } + + if (ev->unused.used > 0) { + int k = ev->unused.ptr[--ev->unused.used]; + + ev->pollfds[k].fd = fd; + ev->pollfds[k].events = pevents; + + return k; + } else { + if (ev->size == 0) { + ev->size = 16; + ev->pollfds = malloc(sizeof(*ev->pollfds) * ev->size); + force_assert(NULL != ev->pollfds); + } else if (ev->size == ev->used) { + ev->size += 16; + ev->pollfds = realloc(ev->pollfds, sizeof(*ev->pollfds) * ev->size); + force_assert(NULL != ev->pollfds); + } + + ev->pollfds[ev->used].fd = fd; + ev->pollfds[ev->used].events = pevents; + + return ev->used++; + } +} + +static int fdevent_poll_poll(fdevents *ev, int timeout_ms) { +#if 0 + fdevent_poll_event_compress(ev); +#endif + return poll(ev->pollfds, ev->used, timeout_ms); +} + +static int fdevent_poll_event_get_revent(fdevents *ev, size_t ndx) { + int r, poll_r; + + if (ndx >= ev->used) { + log_error_write(ev->srv, __FILE__, __LINE__, "sii", + "dying because: event: ", (int) ndx, (int) ev->used); + + SEGFAULT(); + + return 0; + } + + if (ev->pollfds[ndx].revents & POLLNVAL) { + /* should never happen */ + SEGFAULT(); + } + + r = 0; + poll_r = ev->pollfds[ndx].revents; + + /* map POLL* to FDEVEN_*; they are probably the same, but still. */ + + if (poll_r & POLLIN) r |= FDEVENT_IN; + if (poll_r & POLLOUT) r |= FDEVENT_OUT; + if (poll_r & POLLERR) r |= FDEVENT_ERR; + if (poll_r & POLLHUP) r |= FDEVENT_HUP; + if (poll_r & POLLNVAL) r |= FDEVENT_NVAL; + if (poll_r & POLLPRI) r |= FDEVENT_PRI; + if (poll_r & POLLRDHUP) r |= FDEVENT_RDHUP; + + return r; +} + +static int fdevent_poll_event_get_fd(fdevents *ev, size_t ndx) { + return ev->pollfds[ndx].fd; +} + +static int fdevent_poll_event_next_fdndx(fdevents *ev, int ndx) { + size_t i; + + i = (ndx < 0) ? 0 : ndx + 1; + for (; i < ev->used; i++) { + if (ev->pollfds[i].revents) return i; + } + + return -1; +} + +int fdevent_poll_init(fdevents *ev) { + ev->type = FDEVENT_HANDLER_POLL; +#define SET(x) \ + ev->x = fdevent_poll_##x; + + SET(free); + SET(poll); + + SET(event_del); + SET(event_set); + + SET(event_next_fdndx); + SET(event_get_fd); + SET(event_get_revent); + + return 0; +} + + + + +#else +int fdevent_poll_init(fdevents *ev) { + UNUSED(ev); + + log_error_write(ev->srv, __FILE__, __LINE__, + "s", "poll is not supported, try to set server.event-handler = \"select\""); + + return -1; +} +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/fdevent_select.c b/data/lighttpd/lighttpd-1.4.53/src/fdevent_select.c new file mode 100644 index 000000000..2b4f85f47 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/fdevent_select.c @@ -0,0 +1,133 @@ +#include "first.h" + +#include "fdevent_impl.h" +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + +#include <sys/time.h> +#include <sys/types.h> + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#ifdef FDEVENT_USE_SELECT + +static int fdevent_select_reset(fdevents *ev) { + FD_ZERO(&(ev->select_set_read)); + FD_ZERO(&(ev->select_set_write)); + FD_ZERO(&(ev->select_set_error)); + ev->select_max_fd = -1; + + return 0; +} + +static int fdevent_select_event_del(fdevents *ev, int fde_ndx, int fd) { + if (fde_ndx < 0) return -1; + + FD_CLR(fd, &(ev->select_set_read)); + FD_CLR(fd, &(ev->select_set_write)); + FD_CLR(fd, &(ev->select_set_error)); + + return -1; +} + +static int fdevent_select_event_set(fdevents *ev, int fde_ndx, int fd, int events) { + UNUSED(fde_ndx); + + /* we should be protected by max-fds, but you never know */ + force_assert(fd < ((int)FD_SETSIZE)); + + if (events & FDEVENT_IN) { + FD_SET(fd, &(ev->select_set_read)); + } else { + FD_CLR(fd, &(ev->select_set_read)); + } + if (events & FDEVENT_OUT) { + FD_SET(fd, &(ev->select_set_write)); + } else { + FD_CLR(fd, &(ev->select_set_write)); + } + FD_SET(fd, &(ev->select_set_error)); + + if (fd > ev->select_max_fd) ev->select_max_fd = fd; + + return fd; +} + +static int fdevent_select_poll(fdevents *ev, int timeout_ms) { + struct timeval tv; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + ev->select_read = ev->select_set_read; + ev->select_write = ev->select_set_write; + ev->select_error = ev->select_set_error; + + return select(ev->select_max_fd + 1, &(ev->select_read), &(ev->select_write), &(ev->select_error), &tv); +} + +static int fdevent_select_event_get_revent(fdevents *ev, size_t ndx) { + int revents = 0; + + if (FD_ISSET(ndx, &(ev->select_read))) { + revents |= FDEVENT_IN; + } + if (FD_ISSET(ndx, &(ev->select_write))) { + revents |= FDEVENT_OUT; + } + if (FD_ISSET(ndx, &(ev->select_error))) { + revents |= FDEVENT_ERR; + } + + return revents; +} + +static int fdevent_select_event_get_fd(fdevents *ev, size_t ndx) { + UNUSED(ev); + + return ndx; +} + +static int fdevent_select_event_next_fdndx(fdevents *ev, int ndx) { + int i; + + i = (ndx < 0) ? 0 : ndx + 1; + + for (; i < ev->select_max_fd + 1; i++) { + if (FD_ISSET(i, &(ev->select_read))) return i; + if (FD_ISSET(i, &(ev->select_write))) return i; + if (FD_ISSET(i, &(ev->select_error))) return i; + } + + return -1; +} + +int fdevent_select_init(fdevents *ev) { + ev->type = FDEVENT_HANDLER_SELECT; +#define SET(x) \ + ev->x = fdevent_select_##x; + + SET(reset); + SET(poll); + + SET(event_del); + SET(event_set); + + SET(event_next_fdndx); + SET(event_get_fd); + SET(event_get_revent); + + return 0; +} + +#else +int fdevent_select_init(fdevents *ev) { + UNUSED(ev); + + return -1; +} +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/fdevent_solaris_devpoll.c b/data/lighttpd/lighttpd-1.4.53/src/fdevent_solaris_devpoll.c new file mode 100644 index 000000000..1eb8f756b --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/fdevent_solaris_devpoll.c @@ -0,0 +1,172 @@ +#include "first.h" + +#include "fdevent_impl.h" +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + +#include <sys/types.h> + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> + +#ifdef FDEVENT_USE_SOLARIS_DEVPOLL + +# include <sys/devpoll.h> +# include <sys/ioctl.h> + +static void fdevent_solaris_devpoll_free(fdevents *ev) { + free(ev->devpollfds); + close(ev->devpoll_fd); +} + +/* return -1 is fine here */ + +static int fdevent_solaris_devpoll_event_del(fdevents *ev, int fde_ndx, int fd) { + struct pollfd pfd; + + if (fde_ndx < 0) return -1; + + pfd.fd = fd; + pfd.events = POLLREMOVE; + pfd.revents = 0; + + if (-1 == write(ev->devpoll_fd, &pfd, sizeof(pfd))) { + log_error_write(ev->srv, __FILE__, __LINE__, "S(D, S)", + "(del) write failed: ", fd, strerror(errno)); + + return -1; + } + + return -1; +} + +static int fdevent_solaris_devpoll_event_set(fdevents *ev, int fde_ndx, int fd, int events) { + struct pollfd pfd; + int add = 0; + + int pevents = 0; + if (events & FDEVENT_IN) pevents |= POLLIN; + if (events & FDEVENT_OUT) pevents |= POLLOUT; + + if (fde_ndx == -1) add = 1; + + pfd.fd = fd; + pfd.events = pevents; + pfd.revents = 0; + + if (-1 == write(ev->devpoll_fd, &pfd, sizeof(pfd))) { + log_error_write(ev->srv, __FILE__, __LINE__, "S(D, S)", + "(set) write failed: ", fd, strerror(errno)); + + return -1; + } + + return fd; +} + +static int fdevent_solaris_devpoll_poll(fdevents *ev, int timeout_ms) { + struct dvpoll dopoll; + int ret; + + dopoll.dp_timeout = timeout_ms; + dopoll.dp_nfds = ev->maxfds - 1; + dopoll.dp_fds = ev->devpollfds; + + ret = ioctl(ev->devpoll_fd, DP_POLL, &dopoll); + + return ret; +} + +static int fdevent_solaris_devpoll_event_get_revent(fdevents *ev, size_t ndx) { + int r, poll_r; + + r = 0; + poll_r = ev->devpollfds[ndx].revents; + + /* map POLL* to FDEVEN_*; they are probably the same, but still. */ + + if (poll_r & POLLIN) r |= FDEVENT_IN; + if (poll_r & POLLOUT) r |= FDEVENT_OUT; + if (poll_r & POLLERR) r |= FDEVENT_ERR; + if (poll_r & POLLHUP) r |= FDEVENT_HUP; + if (poll_r & POLLNVAL) r |= FDEVENT_NVAL; + if (poll_r & POLLPRI) r |= FDEVENT_PRI; + + return r; +} + +static int fdevent_solaris_devpoll_event_get_fd(fdevents *ev, size_t ndx) { + return ev->devpollfds[ndx].fd; +} + +static int fdevent_solaris_devpoll_event_next_fdndx(fdevents *ev, int last_ndx) { + size_t i; + + UNUSED(ev); + + i = (last_ndx < 0) ? 0 : last_ndx + 1; + + return i; +} + +int fdevent_solaris_devpoll_reset(fdevents *ev) { + /* a forked process does only inherit the filedescriptor, + * but every operation on the device will lead to a EACCES */ + if ((ev->devpoll_fd = open("/dev/poll", O_RDWR)) < 0) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "opening /dev/poll failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); + + return -1; + } + + fdevent_setfd_cloexec(ev->devpoll_fd); + return 0; +} +int fdevent_solaris_devpoll_init(fdevents *ev) { + ev->type = FDEVENT_HANDLER_SOLARIS_DEVPOLL; +#define SET(x) \ + ev->x = fdevent_solaris_devpoll_##x; + + SET(free); + SET(poll); + SET(reset); + + SET(event_del); + SET(event_set); + + SET(event_next_fdndx); + SET(event_get_fd); + SET(event_get_revent); + + ev->devpollfds = malloc(sizeof(*ev->devpollfds) * ev->maxfds); + force_assert(NULL != ev->devpollfds); + + if ((ev->devpoll_fd = open("/dev/poll", O_RDWR)) < 0) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "opening /dev/poll failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); + + return -1; + } + + /* we just wanted to check if it works */ + close(ev->devpoll_fd); + + ev->devpoll_fd = -1; + + return 0; +} + +#else +int fdevent_solaris_devpoll_init(fdevents *ev) { + UNUSED(ev); + + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "solaris-devpoll not supported, try to set server.event-handler = \"poll\" or \"select\""); + + return -1; +} +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/fdevent_solaris_port.c b/data/lighttpd/lighttpd-1.4.53/src/fdevent_solaris_port.c new file mode 100644 index 000000000..ddfbb51b2 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/fdevent_solaris_port.c @@ -0,0 +1,175 @@ +#include "first.h" + +#include "fdevent_impl.h" +#include "fdevent.h" +#include "buffer.h" +#include "log.h" + +#include <sys/types.h> + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> + +#ifdef FDEVENT_USE_SOLARIS_PORT + +#include <sys/poll.h> +static const int SOLARIS_PORT_POLL_READ = POLLIN; +static const int SOLARIS_PORT_POLL_WRITE = POLLOUT; +static const int SOLARIS_PORT_POLL_READ_WRITE = POLLIN & POLLOUT; + +static int fdevent_solaris_port_event_del(fdevents *ev, int fde_ndx, int fd) { + if (fde_ndx < 0) return -1; + + if (0 != port_dissociate(ev->port_fd, PORT_SOURCE_FD, fd)) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "port_dissociate failed: ", strerror(errno), ", dying"); + + SEGFAULT(); + + return 0; + } + + return -1; +} + +static int fdevent_solaris_port_event_set(fdevents *ev, int fde_ndx, int fd, int events) { + const int* user_data = NULL; + + if ((events & FDEVENT_IN) && (events & FDEVENT_OUT)) { + user_data = &SOLARIS_PORT_POLL_READ_WRITE; + } else if (events & FDEVENT_IN) { + user_data = &SOLARIS_PORT_POLL_READ; + } else if (events & FDEVENT_OUT) { + user_data = &SOLARIS_PORT_POLL_WRITE; + } + + if (0 != port_associate(ev->port_fd, PORT_SOURCE_FD, fd, *user_data, (void*) user_data)) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "port_associate failed: ", strerror(errno), ", dying"); + + SEGFAULT(); + + return 0; + } + + return fd; +} + +static int fdevent_solaris_port_event_get_revent(fdevents *ev, size_t ndx) { + int events = 0, e; + + e = ev->port_events[ndx].portev_events; + if (e & POLLIN) events |= FDEVENT_IN; + if (e & POLLOUT) events |= FDEVENT_OUT; + if (e & POLLERR) events |= FDEVENT_ERR; + if (e & POLLHUP) events |= FDEVENT_HUP; + if (e & POLLPRI) events |= FDEVENT_PRI; + if (e & POLLNVAL) events |= FDEVENT_NVAL; + + return e; +} + +static int fdevent_solaris_port_event_get_fd(fdevents *ev, size_t ndx) { + return ev->port_events[ndx].portev_object; +} + +static int fdevent_solaris_port_event_next_fdndx(fdevents *ev, int ndx) { + size_t i; + + UNUSED(ev); + + i = (ndx < 0) ? 0 : ndx + 1; + + return i; +} + +static void fdevent_solaris_port_free(fdevents *ev) { + close(ev->port_fd); + free(ev->port_events); +} + +/* if there is any error it will return the return values of port_getn, otherwise it will return number of events **/ +static int fdevent_solaris_port_poll(fdevents *ev, int timeout_ms) { + int i = 0; + int ret; + unsigned int available_events, wait_for_events = 0; + const int *user_data; + + struct timespec timeout; + + timeout.tv_sec = timeout_ms/1000L; + timeout.tv_nsec = (timeout_ms % 1000L) * 1000000L; + + /* get the number of file descriptors with events */ + if ((ret = port_getn(ev->port_fd, ev->port_events, 0, &wait_for_events, &timeout)) < 0) return ret; + + /* wait for at least one event */ + if (0 == wait_for_events) wait_for_events = 1; + + available_events = wait_for_events; + + /* get the events of the file descriptors */ + if ((ret = port_getn(ev->port_fd, ev->port_events, ev->maxfds, &available_events, &timeout)) < 0) { + /* if errno == ETIME and available_event == wait_for_events we didn't get any events */ + /* for other errors we didn't get any events either */ + if (!(errno == ETIME && wait_for_events != available_events)) return ret; + } + + for (i = 0; i < available_events; ++i) { + user_data = (const int *) ev->port_events[i].portev_user; + + if ((ret = port_associate(ev->port_fd, PORT_SOURCE_FD, ev->port_events[i].portev_object, + *user_data, (void*) user_data)) < 0) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "port_associate failed: ", strerror(errno), ", dying"); + + SEGFAULT(); + + return 0; + } + } + + return available_events; +} + +int fdevent_solaris_port_init(fdevents *ev) { + ev->type = FDEVENT_HANDLER_SOLARIS_PORT; +#define SET(x) \ + ev->x = fdevent_solaris_port_##x; + + SET(free); + SET(poll); + + SET(event_del); + SET(event_set); + + SET(event_next_fdndx); + SET(event_get_fd); + SET(event_get_revent); + + if ((ev->port_fd = port_create()) < 0) { + log_error_write(ev->srv, __FILE__, __LINE__, "SSS", + "port_create() failed (", strerror(errno), "), try to set server.event-handler = \"poll\" or \"select\""); + + return -1; + } + + ev->port_events = malloc(ev->maxfds * sizeof(*ev->port_events)); + force_assert(NULL != ev->port_events); + + return 0; +} + +#else +int fdevent_solaris_port_init(fdevents *ev) { + UNUSED(ev); + + log_error_write(ev->srv, __FILE__, __LINE__, "S", + "solaris-eventports not supported, try to set server.event-handler = \"poll\" or \"select\""); + + return -1; +} +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/first.h b/data/lighttpd/lighttpd-1.4.53/src/first.h new file mode 100644 index 000000000..38c259296 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/first.h @@ -0,0 +1,97 @@ +#ifndef LI_FIRST_H +#define LI_FIRST_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#else +# ifndef _GNU_SOURCE +# define _GNU_SOURCE +# endif +#endif + +#ifndef __STDC_WANT_LIB_EXT1__ +#define __STDC_WANT_LIB_EXT1__ 1 +#endif + +#ifdef __COVERITY__ +#define _Float128 long double +#define _Float64x long double +#define _Float64 double +#define _Float32x double +#define _Float32 float +#endif + + +#include <sys/types.h> +#include <stddef.h> + +#if defined HAVE_STDINT_H +# include <stdint.h> +#elif defined HAVE_INTTYPES_H +# include <inttypes.h> +#endif + + +/* solaris and NetBSD 1.3.x again */ +#if (!defined(HAVE_STDINT_H)) && (!defined(HAVE_INTTYPES_H)) && (!defined(uint32_t)) +# define uint32_t u_int32_t +#endif + + +#include <limits.h> + +#ifndef SIZE_MAX +# ifdef SIZE_T_MAX +# define SIZE_MAX SIZE_T_MAX +# else +# define SIZE_MAX (~(size_t)0u) +# endif +#endif + +#ifndef SSIZE_MAX +# define SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1)) +#endif + + +#define UNUSED(x) ( (void)(x) ) + + +#ifndef __has_attribute /* clang */ +#define __has_attribute(x) 0 +#endif + +#ifdef __GNUC__ +#ifndef __GNUC_PREREQ +# ifdef __GNUC_PREREQ__ +# define __GNUC_PREREQ __GNUC_PREREQ__ +# elif defined __GNUC__ && defined __GNUC_MINOR__ +# define __GNUC_PREREQ(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +# else +# define __GNUC_PREREQ(maj, min) 0 +# endif +#endif +#else +#define __GNUC_PREREQ(maj,min) 0 +#endif + +#ifndef __attribute_cold__ +#if __has_attribute(cold) \ + || __GNUC_PREREQ(4,3) +#define __attribute_cold__ __attribute__((__cold__)) +#else +#define __attribute_cold__ +#endif +#endif + +#ifndef __attribute_noreturn__ +#if __has_attribute(noreturn) \ + || __GNUC_PREREQ(2,5) +#define __attribute_noreturn__ __attribute__((__noreturn__)) +#else +#define __attribute_noreturn__ +#endif +#endif + + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/gw_backend.c b/data/lighttpd/lighttpd-1.4.53/src/gw_backend.c new file mode 100644 index 000000000..62183febb --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/gw_backend.c @@ -0,0 +1,2604 @@ +#include "first.h" + +#include "gw_backend.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include "sys-socket.h" +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> + +#include "base.h" +#include "array.h" +#include "buffer.h" +#include "crc32.h" +#include "fdevent.h" +#include "log.h" +#include "sock_addr.h" + + + + +#include "status_counter.h" + +static int * gw_status_get_counter(server *srv, gw_host *host, gw_proc *proc, const char *tag, size_t len) { + buffer *b = srv->tmp_buf; + buffer_copy_string_len(b, CONST_STR_LEN("gw.backend.")); + buffer_append_string_buffer(b, host->id); + if (proc) { + buffer_append_string_len(b, CONST_STR_LEN(".")); + buffer_append_int(b, proc->id); + } + buffer_append_string_len(b, tag, len); + return status_counter_get_counter(srv, CONST_BUF_LEN(b)); +} + +static void gw_proc_tag_inc(server *srv, gw_host *host, gw_proc *proc, const char *tag, size_t len) { + ++(*gw_status_get_counter(srv, host, proc, tag, len)); +} + +static void gw_proc_load_inc(server *srv, gw_host *host, gw_proc *proc) { + *gw_status_get_counter(srv,host,proc,CONST_STR_LEN(".load")) = ++proc->load; + + status_counter_inc(srv, CONST_STR_LEN("gw.active-requests")); +} + +static void gw_proc_load_dec(server *srv, gw_host *host, gw_proc *proc) { + *gw_status_get_counter(srv,host,proc,CONST_STR_LEN(".load")) = --proc->load; + + status_counter_dec(srv, CONST_STR_LEN("gw.active-requests")); +} + +static void gw_host_assign(server *srv, gw_host *host) { + *gw_status_get_counter(srv,host,NULL,CONST_STR_LEN(".load")) = ++host->load; +} + +static void gw_host_reset(server *srv, gw_host *host) { + *gw_status_get_counter(srv,host,NULL,CONST_STR_LEN(".load")) = --host->load; +} + +static int gw_status_init(server *srv, gw_host *host, gw_proc *proc) { + *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".disabled")) = 0; + *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".died")) = 0; + *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".overloaded")) = 0; + *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".connected")) = 0; + *gw_status_get_counter(srv, host, proc, CONST_STR_LEN(".load")) = 0; + + *gw_status_get_counter(srv, host, NULL, CONST_STR_LEN(".load")) = 0; + + return 0; +} + + + + +static void gw_proc_set_state(gw_host *host, gw_proc *proc, int state) { + if ((int)proc->state == state) return; + if (proc->state == PROC_STATE_RUNNING) { + --host->active_procs; + } else if (state == PROC_STATE_RUNNING) { + ++host->active_procs; + } + proc->state = state; +} + + +static gw_proc *gw_proc_init(void) { + gw_proc *f = calloc(1, sizeof(*f)); + force_assert(f); + + f->unixsocket = buffer_init(); + f->connection_name = buffer_init(); + + f->prev = NULL; + f->next = NULL; + f->state = PROC_STATE_DIED; + + return f; +} + +static void gw_proc_free(gw_proc *f) { + if (!f) return; + + gw_proc_free(f->next); + + buffer_free(f->unixsocket); + buffer_free(f->connection_name); + free(f->saddr); + + free(f); +} + +static gw_host *gw_host_init(void) { + gw_host *f = calloc(1, sizeof(*f)); + force_assert(f); + + f->id = buffer_init(); + f->host = buffer_init(); + f->unixsocket = buffer_init(); + f->docroot = buffer_init(); + f->bin_path = buffer_init(); + f->bin_env = array_init(); + f->bin_env_copy = array_init(); + f->strip_request_uri = buffer_init(); + f->xsendfile_docroot = array_init(); + + return f; +} + +static void gw_host_free(gw_host *h) { + if (!h) return; + if (h->refcount) { + --h->refcount; + return; + } + + buffer_free(h->id); + buffer_free(h->host); + buffer_free(h->unixsocket); + buffer_free(h->docroot); + buffer_free(h->bin_path); + buffer_free(h->strip_request_uri); + array_free(h->bin_env); + array_free(h->bin_env_copy); + array_free(h->xsendfile_docroot); + + gw_proc_free(h->first); + gw_proc_free(h->unused_procs); + + for (size_t i = 0; i < h->args.used; ++i) free(h->args.ptr[i]); + free(h->args.ptr); + free(h); +} + +static gw_exts *gw_extensions_init(void) { + gw_exts *f = calloc(1, sizeof(*f)); + force_assert(f); + return f; +} + +static void gw_extensions_free(gw_exts *f) { + if (!f) return; + for (size_t i = 0; i < f->used; ++i) { + gw_extension *fe = f->exts[i]; + for (size_t j = 0; j < fe->used; ++j) { + gw_host_free(fe->hosts[j]); + } + buffer_free(fe->key); + free(fe->hosts); + free(fe); + } + free(f->exts); + free(f); +} + +static int gw_extension_insert(gw_exts *ext, buffer *key, gw_host *fh) { + gw_extension *fe = NULL; + for (size_t i = 0; i < ext->used; ++i) { + if (buffer_is_equal(key, ext->exts[i]->key)) { + fe = ext->exts[i]; + break; + } + } + + if (NULL == fe) { + fe = calloc(1, sizeof(*fe)); + force_assert(fe); + fe->key = buffer_init(); + fe->last_used_ndx = -1; + buffer_copy_buffer(fe->key, key); + + if (ext->size == 0) { + ext->size = 8; + ext->exts = malloc(ext->size * sizeof(*(ext->exts))); + force_assert(ext->exts); + } else if (ext->used == ext->size) { + ext->size += 8; + ext->exts = realloc(ext->exts, ext->size * sizeof(*(ext->exts))); + force_assert(ext->exts); + } + ext->exts[ext->used++] = fe; + fe->size = 4; + fe->hosts = malloc(fe->size * sizeof(*(fe->hosts))); + force_assert(fe->hosts); + } else if (fe->size == fe->used) { + fe->size += 4; + fe->hosts = realloc(fe->hosts, fe->size * sizeof(*(fe->hosts))); + force_assert(fe->hosts); + } + + fe->hosts[fe->used++] = fh; + return 0; +} + +static void gw_proc_connect_success(server *srv, gw_host *host, gw_proc *proc, int debug) { + gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".connected")); + proc->last_used = srv->cur_ts; + + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "ssdsbsd", + "got proc:", + "pid:", proc->pid, + "socket:", proc->connection_name, + "load:", proc->load); + } +} + +static void gw_proc_connect_error(server *srv, gw_host *host, gw_proc *proc, pid_t pid, int errnum, int debug) { + log_error_write(srv, __FILE__, __LINE__, "sssb", + "establishing connection failed:", strerror(errnum), + "socket:", proc->connection_name); + + if (!proc->is_local) { + proc->disabled_until = srv->cur_ts + host->disable_time; + gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED); + } + else if (proc->pid == pid && proc->state == PROC_STATE_RUNNING) { + /* several requests from lighttpd might reference the same proc + * + * Only one of them should mark the proc + * and all other ones should just take a new one. + * + * If a new proc was started with the old struct, this might + * otherwise lead to marking a perfectly good proc as dead + */ + log_error_write(srv, __FILE__, __LINE__, "sdssd", + "backend error; we'll disable for", host->disable_time, + "secs and send the request to another backend instead:", + "load:", host->load); + if (EAGAIN == errnum) { + /* - EAGAIN: cool down the backend; it is overloaded */ + #ifdef __linux__ + log_error_write(srv, __FILE__, __LINE__, "s", + "If this happened on Linux: You have run out of local ports. " + "Check the manual, section Performance how to handle this."); + #endif + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "sbsd", + "This means that you have more incoming requests than your " + "FastCGI backend can handle in parallel. It might help to " + "spawn more FastCGI backends or PHP children; if not, " + "decrease server.max-connections. The load for this FastCGI " + "backend", proc->connection_name, "is", proc->load); + } + proc->disabled_until = srv->cur_ts + host->disable_time; + gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED); + } + else { + /* we got a hard error from the backend like + * - ECONNREFUSED for tcp-ip sockets + * - ENOENT for unix-domain-sockets + */ + #if 0 + gw_proc_set_state(host, proc, PROC_STATE_DIED_WAIT_FOR_PID); + #else /* treat as overloaded (future: unless we send kill() signal)*/ + proc->disabled_until = srv->cur_ts + host->disable_time; + gw_proc_set_state(host, proc, PROC_STATE_OVERLOADED); + #endif + } + } + + if (EAGAIN == errnum) { + gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".overloaded")); + } + else { + gw_proc_tag_inc(srv, host, proc, CONST_STR_LEN(".died")); + } +} + +static void gw_proc_release(server *srv, gw_host *host, gw_proc *proc, int debug) { + gw_proc_load_dec(srv, host, proc); + + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "ssdsbsd", + "released proc:", + "pid:", proc->pid, + "socket:", proc->connection_name, + "load:", proc->load); + } +} + +static void gw_proc_check_enable(server *srv, gw_host *host, gw_proc *proc) { + if (srv->cur_ts <= proc->disabled_until) return; + if (proc->state != PROC_STATE_OVERLOADED) return; + + gw_proc_set_state(host, proc, PROC_STATE_RUNNING); + + log_error_write(srv, __FILE__, __LINE__, "sbbdb", + "gw-server re-enabled:", proc->connection_name, + host->host, host->port, host->unixsocket); +} + +static void gw_proc_waitpid_log(server *srv, gw_host *host, gw_proc *proc, int status) { + UNUSED(host); + if (WIFEXITED(status)) { + if (proc->state != PROC_STATE_KILLED) { + log_error_write(srv, __FILE__, __LINE__, "sdb", + "child exited:", + WEXITSTATUS(status), proc->connection_name); + } + } else if (WIFSIGNALED(status)) { + if (WTERMSIG(status) != SIGTERM && WTERMSIG(status) != SIGINT + && WTERMSIG(status) != host->kill_signal) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "child signalled:", WTERMSIG(status)); + } + } else { + log_error_write(srv, __FILE__, __LINE__, "sd", + "child died somehow:", status); + } +} + +static int gw_proc_waitpid(server *srv, gw_host *host, gw_proc *proc) { + int rc, status; + + if (!proc->is_local) return 0; + if (proc->pid <= 0) return 0; + + do { + rc = waitpid(proc->pid, &status, WNOHANG); + } while (-1 == rc && errno == EINTR); + if (0 == rc) return 0; /* child still running */ + + /* child terminated */ + if (-1 == rc) { + /* EINVAL or ECHILD no child processes */ + /* should not happen; someone else has cleaned up for us */ + log_error_write(srv, __FILE__, __LINE__, "sddss", + "pid ", proc->pid, proc->state, + "not found:", strerror(errno)); + } + else { + gw_proc_waitpid_log(srv, host, proc, status); + } + + proc->pid = 0; + if (proc->state != PROC_STATE_KILLED) + proc->disabled_until = srv->cur_ts; + gw_proc_set_state(host, proc, PROC_STATE_DIED); + return 1; +} + +static int gw_proc_sockaddr_init(server *srv, gw_host *host, gw_proc *proc) { + sock_addr addr; + socklen_t addrlen; + + if (!buffer_string_is_empty(proc->unixsocket)) { + if (1 != sock_addr_from_str_hints(srv, &addr, &addrlen, + proc->unixsocket->ptr, AF_UNIX, 0)) { + errno = EINVAL; + return -1; + } + buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("unix:")); + buffer_append_string_buffer(proc->connection_name, proc->unixsocket); + } + else { + /*(note: name resolution here is *blocking* if IP string not supplied)*/ + if (1 != sock_addr_from_str_hints(srv, &addr, &addrlen, + host->host->ptr, 0, proc->port)) { + errno = EINVAL; + return -1; + } + else { + /* overwrite host->host buffer with IP addr string so that + * any further use of gw_host does not block on DNS lookup */ + sock_addr_inet_ntop_copy_buffer(host->host, &addr); + host->family = sock_addr_get_family(&addr); + } + buffer_copy_string_len(proc->connection_name, CONST_STR_LEN("tcp:")); + buffer_append_string_buffer(proc->connection_name, host->host); + buffer_append_string_len(proc->connection_name, CONST_STR_LEN(":")); + buffer_append_int(proc->connection_name, proc->port); + } + + if (NULL != proc->saddr && proc->saddrlen < addrlen) { + free(proc->saddr); + proc->saddr = NULL; + } + if (NULL == proc->saddr) { + proc->saddr = (struct sockaddr *)malloc(addrlen); + force_assert(proc->saddr); + } + proc->saddrlen = addrlen; + memcpy(proc->saddr, &addr, addrlen); + return 0; +} + +static int env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) { + char *dst; + + if (!key || !val) return -1; + + dst = malloc(key_len + val_len + 3); + force_assert(dst); + memcpy(dst, key, key_len); + dst[key_len] = '='; + memcpy(dst + key_len + 1, val, val_len + 1); /* add the \0 from the value */ + + for (size_t i = 0; i < env->used; ++i) { + if (0 == strncmp(dst, env->ptr[i], key_len + 1)) { + free(env->ptr[i]); + env->ptr[i] = dst; + return 0; + } + } + + if (env->size == 0) { + env->size = 16; + env->ptr = malloc(env->size * sizeof(*env->ptr)); + force_assert(env->ptr); + } else if (env->size == env->used + 1) { + env->size += 16; + env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); + force_assert(env->ptr); + } + + env->ptr[env->used++] = dst; + + return 0; +} + +static int gw_spawn_connection(server *srv, gw_host *host, gw_proc *proc, int debug) { + int gw_fd; + int status; + struct timeval tv = { 0, 10 * 1000 }; + + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "sdb", + "new proc, socket:", proc->port, proc->unixsocket); + } + + gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0); + if (-1 == gw_fd) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "failed:", strerror(errno)); + return -1; + } + + do { + status = connect(gw_fd, proc->saddr, proc->saddrlen); + } while (-1 == status && errno == EINTR); + + if (-1 == status && errno != ENOENT + && !buffer_string_is_empty(proc->unixsocket)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "unlink", proc->unixsocket, + "after connect failed:", strerror(errno)); + unlink(proc->unixsocket->ptr); + } + + close(gw_fd); + + if (-1 == status) { + /* server is not up, spawn it */ + char_array env; + size_t i; + int dfd = -1; + + /* reopen socket */ + gw_fd = fdevent_socket_cloexec(proc->saddr->sa_family, SOCK_STREAM, 0); + if (-1 == gw_fd) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "socket failed:", strerror(errno)); + return -1; + } + + if (fdevent_set_so_reuseaddr(gw_fd, 1) < 0) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "socketsockopt failed:", strerror(errno)); + close(gw_fd); + return -1; + } + + /* create socket */ + if (-1 == bind(gw_fd, proc->saddr, proc->saddrlen)) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "bind failed for:", + proc->connection_name, + strerror(errno)); + close(gw_fd); + return -1; + } + + if (-1 == listen(gw_fd, host->listen_backlog)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "listen failed:", strerror(errno)); + close(gw_fd); + return -1; + } + + { + /* create environment */ + env.ptr = NULL; + env.size = 0; + env.used = 0; + + /* build clean environment */ + if (host->bin_env_copy->used) { + for (i = 0; i < host->bin_env_copy->used; ++i) { + data_string *ds=(data_string *)host->bin_env_copy->data[i]; + char *ge; + + if (NULL != (ge = getenv(ds->value->ptr))) { + env_add(&env, CONST_BUF_LEN(ds->value), ge, strlen(ge)); + } + } + } else { + char ** const e = fdevent_environ(); + for (i = 0; e[i]; ++i) { + char *eq; + + if (NULL != (eq = strchr(e[i], '='))) { + env_add(&env, e[i], eq - e[i], eq+1, strlen(eq+1)); + } + } + } + + /* create environment */ + for (i = 0; i < host->bin_env->used; ++i) { + data_string *ds = (data_string *)host->bin_env->data[i]; + + env_add(&env, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); + } + + for (i = 0; i < env.used; ++i) { + /* search for PHP_FCGI_CHILDREN */ + if (0 == strncmp(env.ptr[i], "PHP_FCGI_CHILDREN=", + sizeof("PHP_FCGI_CHILDREN=")-1)) { + break; + } + } + + /* not found, add a default */ + if (i == env.used) { + env_add(&env, CONST_STR_LEN("PHP_FCGI_CHILDREN"), + CONST_STR_LEN("1")); + } + + env.ptr[env.used] = NULL; + } + + dfd = fdevent_open_dirname(host->args.ptr[0]); + if (-1 == dfd) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "open dirname failed:", strerror(errno), + host->args.ptr[0]); + } + + /*(FCGI_LISTENSOCK_FILENO == STDIN_FILENO == 0)*/ + proc->pid = (dfd >= 0) + ? fdevent_fork_execve(host->args.ptr[0], host->args.ptr, + env.ptr, gw_fd, -1, -1, dfd) + : -1; + + for (i = 0; i < env.used; ++i) free(env.ptr[i]); + free(env.ptr); + if (-1 != dfd) close(dfd); + close(gw_fd); + + if (-1 == proc->pid) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "gw-backend failed to start:", host->bin_path); + proc->pid = 0; + proc->disabled_until = srv->cur_ts; + return -1; + } + + /* register process */ + proc->last_used = srv->cur_ts; + proc->is_local = 1; + + /* wait */ + select(0, NULL, NULL, NULL, &tv); + + if (0 != gw_proc_waitpid(srv, host, proc)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "gw-backend failed to start:", host->bin_path); + log_error_write(srv, __FILE__, __LINE__, "s", + "If you're trying to run your app as a FastCGI backend, make " + "sure you're using the FastCGI-enabled version. If this is PHP " + "on Gentoo, add 'fastcgi' to the USE flags. If this is PHP, try " + "removing the bytecode caches for now and try again."); + return -1; + } + } else { + proc->is_local = 0; + proc->pid = 0; + + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "(debug) socket is already used; won't spawn:", + proc->connection_name); + } + } + + gw_proc_set_state(host, proc, PROC_STATE_RUNNING); + return 0; +} + +static void gw_proc_spawn(server *srv, gw_host *host, int debug) { + gw_proc *proc; + for (proc = host->unused_procs; proc; proc = proc->next) { + /* (proc->pid <= 0 indicates PROC_STATE_DIED, not PROC_STATE_KILLED) */ + if (proc->pid > 0) continue; + /* (do not attempt to spawn another proc if a proc just exited) */ + if (proc->disabled_until >= srv->cur_ts) return; + break; + } + if (proc) { + if (proc == host->unused_procs) + host->unused_procs = proc->next; + else + proc->prev->next = proc->next; + + if (proc->next) { + proc->next->prev = proc->prev; + proc->next = NULL; + } + + proc->prev = NULL; + } else { + proc = gw_proc_init(); + proc->id = host->max_id++; + } + + ++host->num_procs; + + if (buffer_string_is_empty(host->unixsocket)) { + proc->port = host->port + proc->id; + } else { + buffer_copy_buffer(proc->unixsocket, host->unixsocket); + buffer_append_string_len(proc->unixsocket, CONST_STR_LEN("-")); + buffer_append_int(proc->unixsocket, proc->id); + } + + if (0 != gw_proc_sockaddr_init(srv, host, proc)) { + /*(should not happen if host->host validated at startup, + * and translated from name to IP address at startup)*/ + log_error_write(srv, __FILE__, __LINE__, "s", + "ERROR: spawning backend failed."); + --host->num_procs; + if (proc->id == host->max_id-1) --host->max_id; + gw_proc_free(proc); + } else if (gw_spawn_connection(srv, host, proc, debug)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "ERROR: spawning backend failed."); + proc->next = host->unused_procs; + if (host->unused_procs) + host->unused_procs->prev = proc; + host->unused_procs = proc; + } else { + proc->next = host->first; + if (host->first) + host->first->prev = proc; + host->first = proc; + } +} + +static void gw_proc_kill(server *srv, gw_host *host, gw_proc *proc) { + UNUSED(srv); + if (proc->next) proc->next->prev = proc->prev; + if (proc->prev) proc->prev->next = proc->next; + + if (proc->prev == NULL) host->first = proc->next; + + proc->prev = NULL; + proc->next = host->unused_procs; + proc->disabled_until = 0; + + if (host->unused_procs) + host->unused_procs->prev = proc; + host->unused_procs = proc; + + kill(proc->pid, host->kill_signal); + + gw_proc_set_state(host, proc, PROC_STATE_KILLED); + + --host->num_procs; +} + +static gw_host * unixsocket_is_dup(gw_plugin_data *p, size_t used, buffer *unixsocket) { + for (size_t i = 0; i < used; ++i) { + gw_exts *exts = p->config_storage[i]->exts; + if (NULL == exts) continue; + for (size_t j = 0; j < exts->used; ++j) { + gw_extension *ex = exts->exts[j]; + for (size_t n = 0; n < ex->used; ++n) { + gw_host *host = ex->hosts[n]; + if (!buffer_string_is_empty(host->unixsocket) + && buffer_is_equal(host->unixsocket, unixsocket) + && !buffer_string_is_empty(host->bin_path)) + return host; + } + } + } + + return NULL; +} + +static int parse_binpath(char_array *env, buffer *b) { + char *start = b->ptr; + char c; + /* search for spaces */ + for (size_t i = 0; i < buffer_string_length(b); ++i) { + switch(b->ptr[i]) { + case ' ': + case '\t': + /* a WS, stop here and copy the argument */ + + if (env->size == 0) { + env->size = 16; + env->ptr = malloc(env->size * sizeof(*env->ptr)); + } else if (env->size == env->used) { + env->size += 16; + env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); + } + + c = b->ptr[i]; + b->ptr[i] = '\0'; + env->ptr[env->used++] = strdup(start); + b->ptr[i] = c; + + start = b->ptr + i + 1; + break; + default: + break; + } + } + + if (env->size == 0) { + env->size = 16; + env->ptr = malloc(env->size * sizeof(*env->ptr)); + } else if (env->size == env->used) { /*need one extra for terminating NULL*/ + env->size += 16; + env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); + } + + /* the rest */ + env->ptr[env->used++] = strdup(start); + + if (env->size == 0) { + env->size = 16; + env->ptr = malloc(env->size * sizeof(*env->ptr)); + } else if (env->size == env->used) { /*need one extra for terminating NULL*/ + env->size += 16; + env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr)); + } + + /* terminate */ + env->ptr[env->used++] = NULL; + + return 0; +} + +enum { + GW_BALANCE_LEAST_CONNECTION, + GW_BALANCE_RR, + GW_BALANCE_HASH, + GW_BALANCE_STICKY +}; + +static gw_host * gw_host_get(server *srv, connection *con, gw_extension *extension, int balance, int debug) { + gw_host *host; + unsigned long last_max = ULONG_MAX; + int max_usage = INT_MAX; + int ndx = -1; + size_t k; + + if (extension->used <= 1) { + if (1 == extension->used && extension->hosts[0]->active_procs > 0) { + ndx = 0; + } + } else switch(balance) { + case GW_BALANCE_HASH: + /* hash balancing */ + + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "proxy - used hash balancing, hosts:", + extension->used); + } + + for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) { + unsigned long cur_max; + host = extension->hosts[k]; + if (0 == host->active_procs) continue; + + cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path)) + + generate_crc32c(CONST_BUF_LEN(host->host)) /* cachable */ + + generate_crc32c(CONST_BUF_LEN(con->uri.authority)); + + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "sbbbd", + "proxy - election:", con->uri.path, + host->host, con->uri.authority, cur_max); + } + + if (last_max < cur_max || last_max == ULONG_MAX) { + last_max = cur_max; + ndx = k; + } + } + + break; + case GW_BALANCE_LEAST_CONNECTION: + /* fair balancing */ + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "s", + "proxy - used least connection"); + } + + for (k = 0, ndx = -1, max_usage = INT_MAX; k < extension->used; ++k) { + host = extension->hosts[k]; + if (0 == host->active_procs) continue; + + if (host->load < max_usage) { + max_usage = host->load; + ndx = k; + } + } + + break; + case GW_BALANCE_RR: + /* round robin */ + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "s", + "proxy - used round-robin balancing"); + } + + /* just to be sure */ + force_assert(extension->used < INT_MAX); + + host = extension->hosts[0]; + + /* Use last_used_ndx from first host in list */ + k = extension->last_used_ndx; + ndx = k + 1; /* use next host after the last one */ + if (ndx < 0) ndx = 0; + + /* Search first active host after last_used_ndx */ + while (ndx < (int) extension->used + && 0 == (host = extension->hosts[ndx])->active_procs) ++ndx; + + if (ndx >= (int) extension->used) { + /* didn't find a higher id, wrap to the start */ + for (ndx = 0; ndx <= (int) k; ++ndx) { + host = extension->hosts[ndx]; + if (0 != host->active_procs) break; + } + + /* No active host found */ + if (0 == host->active_procs) ndx = -1; + } + + /* Save new index for next round */ + extension->last_used_ndx = ndx; + + break; + case GW_BALANCE_STICKY: + /* source sticky balancing */ + + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "proxy - used sticky balancing, hosts:", + extension->used); + } + + for (k = 0, ndx = -1, last_max = ULONG_MAX; k < extension->used; ++k) { + unsigned long cur_max; + host = extension->hosts[k]; + + if (0 == host->active_procs) continue; + + cur_max = generate_crc32c(CONST_BUF_LEN(con->dst_addr_buf)) + + generate_crc32c(CONST_BUF_LEN(host->host)) + + host->port; + + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "sbbdd", + "proxy - election:", con->dst_addr_buf, + host->host, host->port, cur_max); + } + + if (last_max < cur_max || last_max == ULONG_MAX) { + last_max = cur_max; + ndx = k; + } + } + + break; + default: + break; + } + + if (-1 != ndx) { + /* found a server */ + host = extension->hosts[ndx]; + + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "sbd", + "gw - found a host", host->host, host->port); + } + + return host; + } else if (0 == srv->srvconf.max_worker) { + /* special-case adaptive spawning and 0 == host->min_procs */ + for (k = 0; k < extension->used; ++k) { + host = extension->hosts[k]; + if (0 == host->min_procs && 0 == host->num_procs + && !buffer_string_is_empty(host->bin_path)) { + gw_proc_spawn(srv, host, debug); + if (host->num_procs) return host; + } + } + } + + /* all hosts are down */ + /* sorry, we don't have a server alive for this ext */ + con->http_status = 503; /* Service Unavailable */ + con->mode = DIRECT; + + /* only send the 'no handler' once */ + if (!extension->note_is_sent) { + extension->note_is_sent = 1; + log_error_write(srv, __FILE__, __LINE__, "sBSbsbs", + "all handlers for", con->uri.path, "?", + con->uri.query, "on", extension->key, "are down."); + } + + return NULL; +} + +static int gw_establish_connection(server *srv, gw_host *host, gw_proc *proc, pid_t pid, int gw_fd, int debug) { + if (-1 == connect(gw_fd, proc->saddr, proc->saddrlen)) { + if (errno == EINPROGRESS || + errno == EALREADY || + errno == EINTR) { + if (debug > 2) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "connect delayed; will continue later:", + proc->connection_name); + } + + return 1; + } else { + gw_proc_connect_error(srv, host, proc, pid, errno, debug); + return -1; + } + } + + if (debug > 1) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connect succeeded: ", gw_fd); + } + + return 0; +} + +static void gw_restart_dead_procs(server *srv, gw_host *host, int debug, int trigger) { + for (gw_proc *proc = host->first; proc; proc = proc->next) { + if (debug > 2) { + log_error_write(srv, __FILE__, __LINE__, "sbdddd", + "proc:", proc->connection_name, proc->state, + proc->is_local, proc->load, proc->pid); + } + + switch (proc->state) { + case PROC_STATE_RUNNING: + break; + case PROC_STATE_OVERLOADED: + gw_proc_check_enable(srv, host, proc); + break; + case PROC_STATE_KILLED: + if (trigger && ++proc->disabled_until > 4) { + int sig = (proc->disabled_until <= 8) + ? host->kill_signal + : proc->disabled_until <= 16 ? SIGTERM : SIGKILL; + kill(proc->pid, sig); + } + break; + case PROC_STATE_DIED_WAIT_FOR_PID: + /*(state should not happen in workers if server.max-worker > 0)*/ + /*(if PROC_STATE_DIED_WAIT_FOR_PID is used in future, might want + * to save proc->disabled_until before gw_proc_waitpid() since + * gw_proc_waitpid will set proc->disabled_until to srv->cur_ts, + * and so process will not be restarted below until one sec later)*/ + if (0 == gw_proc_waitpid(srv, host, proc)) { + gw_proc_check_enable(srv, host, proc); + } + + if (proc->state != PROC_STATE_DIED) break; + /* fall through *//*(we have a dead proc now)*/ + + case PROC_STATE_DIED: + /* local procs get restarted by us, + * remote ones hopefully by the admin */ + + if (!buffer_string_is_empty(host->bin_path)) { + /* we still have connections bound to this proc, + * let them terminate first */ + if (proc->load != 0) break; + + /* avoid spinning if child exits too quickly */ + if (proc->disabled_until >= srv->cur_ts) break; + + /* restart the child */ + + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "ssbsdsd", + "--- gw spawning", + "\n\tsocket", proc->connection_name, + "\n\tcurrent:", 1, "/", host->max_procs); + } + + if (gw_spawn_connection(srv, host, proc, debug)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "ERROR: spawning gw failed."); + } + } else { + gw_proc_check_enable(srv, host, proc); + } + break; + } + } +} + + + + +#include "base.h" +#include "connections.h" +#include "joblist.h" +#include "response.h" + + +/* ok, we need a prototype */ +static handler_t gw_handle_fdevent(server *srv, void *ctx, int revents); + + +static gw_handler_ctx * handler_ctx_init(size_t sz) { + gw_handler_ctx *hctx = calloc(1, 0 == sz ? sizeof(*hctx) : sz); + force_assert(hctx); + + hctx->fde_ndx = -1; + + /*hctx->response = chunk_buffer_acquire();*//*(allocated when needed)*/ + + hctx->request_id = 0; + hctx->gw_mode = GW_RESPONDER; + hctx->state = GW_STATE_INIT; + hctx->proc = NULL; + + hctx->fd = -1; + + hctx->reconnects = 0; + hctx->send_content_body = 1; + + /*hctx->rb = chunkqueue_init();*//*(allocated when needed)*/ + hctx->wb = chunkqueue_init(); + hctx->wb_reqlen = 0; + + return hctx; +} + +static void handler_ctx_free(gw_handler_ctx *hctx) { + /* caller MUST have called gw_backend_close(srv, hctx) if necessary */ + if (hctx->handler_ctx_free) hctx->handler_ctx_free(hctx); + chunk_buffer_release(hctx->response); + + chunkqueue_free(hctx->rb); + chunkqueue_free(hctx->wb); + + free(hctx); +} + +static void handler_ctx_clear(gw_handler_ctx *hctx) { + /* caller MUST have called gw_backend_close(srv, hctx) if necessary */ + + hctx->proc = NULL; + hctx->host = NULL; + hctx->ext = NULL; + /*hctx->ext_auth is intentionally preserved to flag prior authorizer*/ + + hctx->gw_mode = GW_RESPONDER; + hctx->state = GW_STATE_INIT; + /*hctx->state_timestamp = 0;*//*(unused; left as-is)*/ + + if (hctx->rb) chunkqueue_reset(hctx->rb); + if (hctx->wb) chunkqueue_reset(hctx->wb); + hctx->wb_reqlen = 0; + + if (hctx->response) buffer_clear(hctx->response); + + hctx->fd = -1; + hctx->fde_ndx = -1; + hctx->reconnects = 0; + hctx->request_id = 0; + hctx->send_content_body = 1; + + /*plugin_config conf;*//*(no need to reset for same request)*/ + + /*hctx->remote_conn = NULL;*//*(no need to reset for same request)*/ + /*hctx->plugin_data = NULL;*//*(no need to reset for same request)*/ +} + + +void * gw_init(void) { + return calloc(1, sizeof(gw_plugin_data)); +} + + +void gw_plugin_config_free(gw_plugin_config *s) { + gw_exts *exts = s->exts; + if (exts) { + for (size_t j = 0; j < exts->used; ++j) { + gw_extension *ex = exts->exts[j]; + for (size_t n = 0; n < ex->used; ++n) { + gw_proc *proc; + gw_host *host = ex->hosts[n]; + + for (proc = host->first; proc; proc = proc->next) { + if (proc->pid > 0) { + kill(proc->pid, host->kill_signal); + } + + if (proc->is_local && + !buffer_string_is_empty(proc->unixsocket)) { + unlink(proc->unixsocket->ptr); + } + } + + for (proc = host->unused_procs; proc; proc = proc->next) { + if (proc->pid > 0) { + kill(proc->pid, host->kill_signal); + } + if (proc->is_local && + !buffer_string_is_empty(proc->unixsocket)) { + unlink(proc->unixsocket->ptr); + } + } + } + } + + gw_extensions_free(s->exts); + gw_extensions_free(s->exts_auth); + gw_extensions_free(s->exts_resp); + } + array_free(s->ext_mapping); + free(s); +} + +handler_t gw_free(server *srv, void *p_d) { + gw_plugin_data *p = p_d; + if (p->config_storage) { + for (size_t i = 0; i < srv->config_context->used; ++i) { + gw_plugin_config *s = p->config_storage[i]; + if (NULL == s) continue; + gw_plugin_config_free(s); + } + free(p->config_storage); + } + free(p); + return HANDLER_GO_ON; +} + +int gw_set_defaults_backend(server *srv, gw_plugin_data *p, data_unset *du, size_t i, int sh_exec) { + /* per-module plugin_config MUST have common "base class" gw_plugin_config*/ + /* per-module plugin_data MUST have pointer-compatible common "base class" + * with gw_plugin_data (stemming from gw_plugin_config compatibility) */ + + data_array *da = (data_array *)du; + gw_plugin_config *s = p->config_storage[i]; + buffer *gw_mode; + gw_host *host = NULL; + + if (NULL == da) return 1; + + if (da->type != TYPE_ARRAY || !array_is_kvarray(da->value)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for xxxxx.server; expected " + "( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))"); + return 0; + } + + p->srv_pid = srv->pid; + + gw_mode = buffer_init(); + + s->exts = gw_extensions_init(); + s->exts_auth = gw_extensions_init(); + s->exts_resp = gw_extensions_init(); + /*s->balance = GW_BALANCE_LEAST_CONNECTION;*//*(default)*/ + + /* + * gw.server = ( "<ext>" => ( ... ), + * "<ext>" => ( ... ) ) + */ + + for (size_t j = 0; j < da->value->used; ++j) { + data_array *da_ext = (data_array *)da->value->data[j]; + + /* + * da_ext->key == name of the extension + */ + + /* + * gw.server = ( "<ext>" => + * ( "<host>" => ( ... ), + * "<host>" => ( ... ) + * ), + * "<ext>" => ... ) + */ + + for (size_t n = 0; n < da_ext->value->used; ++n) { + data_array *da_host = (data_array *)da_ext->value->data[n]; + + config_values_t fcv[] = { + { "host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "docroot", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "mode", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "bin-path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + + { "check-local", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { "port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ + { "min-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ + { "max-procs", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ + { "max-load-per-proc", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ + { "idle-timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ + { "disable-time", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ + + { "bin-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ + { "bin-copy-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ + + { "broken-scriptfilename", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ + { "allow-x-send-file", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */ + { "strip-request-uri", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 16 */ + { "kill-signal", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 17 */ + { "fix-root-scriptname", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 18 */ + { "listen-backlog", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 19 */ + { "x-sendfile", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 20 */ + { "x-sendfile-docroot",NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 21 */ + { "tcp-fin-propagate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 22 */ + + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + unsigned short host_mode = GW_RESPONDER; + + if (da_host->type != TYPE_ARRAY || !array_is_kvany(da_host->value)){ + log_error_write(srv, __FILE__, __LINE__, "SBS", + "unexpected value for gw.server near [", + da_host->key, "](string); expected ( \"ext\" => ( \"backend-label\" => ( \"key\" => \"value\" )))"); + + goto error; + } + + host = gw_host_init(); + buffer_clear(gw_mode); + + buffer_copy_buffer(host->id, da_host->key); + + host->check_local = 1; + host->min_procs = 4; + host->max_procs = 4; + host->max_load_per_proc = 1; + host->idle_timeout = 60; + host->disable_time = 1; + host->break_scriptfilename_for_php = 0; + host->kill_signal = SIGTERM; + host->fix_root_path_name = 0; + host->listen_backlog = 1024; + host->xsendfile_allow = 0; + host->refcount = 0; + + fcv[0].destination = host->host; + fcv[1].destination = host->docroot; + fcv[2].destination = gw_mode; + fcv[3].destination = host->unixsocket; + fcv[4].destination = host->bin_path; + + fcv[5].destination = &(host->check_local); + fcv[6].destination = &(host->port); + fcv[7].destination = &(host->min_procs); + fcv[8].destination = &(host->max_procs); + fcv[9].destination = &(host->max_load_per_proc); + fcv[10].destination = &(host->idle_timeout); + fcv[11].destination = &(host->disable_time); + + fcv[12].destination = host->bin_env; + fcv[13].destination = host->bin_env_copy; + fcv[14].destination = &(host->break_scriptfilename_for_php); + fcv[15].destination = &(host->xsendfile_allow); + fcv[16].destination = host->strip_request_uri; + fcv[17].destination = &(host->kill_signal); + fcv[18].destination = &(host->fix_root_path_name); + fcv[19].destination = &(host->listen_backlog); + fcv[20].destination = &(host->xsendfile_allow); + fcv[21].destination = host->xsendfile_docroot; + fcv[22].destination = &(host->tcp_fin_propagate); + + if (0 != config_insert_values_internal(srv, da_host->value, fcv, T_CONFIG_SCOPE_CONNECTION)) { + goto error; + } + + for (size_t m = 0; m < da_host->value->used; ++m) { + if (NULL != strchr(da_host->value->data[m]->key->ptr, '_')) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "incorrect directive contains underscore ('_') instead of dash ('-'):", + da_host->value->data[m]->key); + } + } + + if ((!buffer_string_is_empty(host->host) || host->port) + && !buffer_string_is_empty(host->unixsocket)) { + log_error_write(srv, __FILE__, __LINE__, "sbsbsbs", + "either host/port or socket have to be set in:", + da->key, "= (", + da_ext->key, " => (", + da_host->key, " ( ..."); + + goto error; + } + + if (!buffer_string_is_empty(host->host) && *host->host->ptr == '/' + && buffer_string_is_empty(host->unixsocket)) { + buffer_copy_buffer(host->unixsocket, host->host); + } + + if (!buffer_string_is_empty(host->unixsocket)) { + /* unix domain socket */ + struct sockaddr_un un; + + if (buffer_string_length(host->unixsocket) + 1 > sizeof(un.sun_path) - 2) { + log_error_write(srv, __FILE__, __LINE__, "sbsbsbs", + "unixsocket is too long in:", + da->key, "= (", + da_ext->key, " => (", + da_host->key, " ( ..."); + + goto error; + } + + if (!buffer_string_is_empty(host->bin_path)) { + gw_host *duplicate = unixsocket_is_dup(p, i+1, host->unixsocket); + if (NULL != duplicate) { + if (!buffer_is_equal(host->bin_path, duplicate->bin_path)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "duplicate unixsocket path:", + host->unixsocket); + goto error; + } + gw_host_free(host); + host = duplicate; + ++host->refcount; + } + } + + host->family = AF_UNIX; + } else { + /* tcp/ip */ + + if (buffer_string_is_empty(host->host) && + buffer_string_is_empty(host->bin_path)) { + log_error_write(srv, __FILE__, __LINE__, "sbsbsbs", + "host or binpath have to be set in:", + da->key, "= (", + da_ext->key, " => (", + da_host->key, " ( ..."); + + goto error; + } else if (0 == host->port) { + host->port = 80; + } + + if (buffer_string_is_empty(host->host)) { + buffer_copy_string_len(host->host, + CONST_STR_LEN("127.0.0.1")); + } + + host->family = (NULL != strchr(host->host->ptr, ':')) + ? AF_INET6 + : AF_INET; + } + + if (host->refcount) { + /* already init'd; skip spawning */ + } else if (!buffer_string_is_empty(host->bin_path)) { + /* a local socket + self spawning */ + struct stat st; + parse_binpath(&host->args, host->bin_path); + if (0 != stat(host->args.ptr[0], &st) || !S_ISREG(st.st_mode) + || !(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { + log_error_write(srv, __FILE__, __LINE__, "SSs", + "invalid \"bin-path\" => \"", host->bin_path->ptr, + "\" (check that file exists, is regular file, " + "and is executable by lighttpd)"); + } + + if (sh_exec) { + /*(preserve prior behavior for SCGI exec of command)*/ + /*(admin should really prefer to put + * any complex command into a script)*/ + for (size_t m = 0; m < host->args.used; ++m) + free(host->args.ptr[m]); + free(host->args.ptr); + + host->args.ptr = calloc(4, sizeof(char *)); + force_assert(host->args.ptr); + host->args.used = 3; + host->args.size = 4; + host->args.ptr[0] = malloc(sizeof("/bin/sh")); + force_assert(host->args.ptr[0]); + memcpy(host->args.ptr[0], "/bin/sh", sizeof("/bin/sh")); + host->args.ptr[1] = malloc(sizeof("-c")); + force_assert(host->args.ptr[1]); + memcpy(host->args.ptr[1], "-c", sizeof("-c")); + host->args.ptr[2] = + malloc(sizeof("exec ")-1 + + buffer_string_length(host->bin_path) + 1); + force_assert(host->args.ptr[2]); + memcpy(host->args.ptr[2], "exec ", sizeof("exec ")-1); + memcpy(host->args.ptr[2]+sizeof("exec ")-1, + host->bin_path->ptr, + buffer_string_length(host->bin_path)+1); + host->args.ptr[3] = NULL; + } + + if (host->min_procs > host->max_procs) + host->min_procs = host->max_procs; + if (host->min_procs!= host->max_procs + && 0 != srv->srvconf.max_worker) { + host->min_procs = host->max_procs; + log_error_write(srv, __FILE__, __LINE__, "s", + "adaptive backend spawning disabled " + "(server.max_worker is non-zero)"); + } + if (host->max_load_per_proc < 1) + host->max_load_per_proc = 0; + + if (s->debug) { + log_error_write(srv, __FILE__, __LINE__, "ssbsdsbsdsd", + "--- gw spawning local", + "\n\tproc:", host->bin_path, + "\n\tport:", host->port, + "\n\tsocket", host->unixsocket, + "\n\tmin-procs:", host->min_procs, + "\n\tmax-procs:", host->max_procs); + } + + for (size_t pno = 0; pno < host->min_procs; ++pno) { + gw_proc *proc = gw_proc_init(); + proc->id = host->num_procs++; + host->max_id++; + + if (buffer_string_is_empty(host->unixsocket)) { + proc->port = host->port + pno; + } else { + buffer_copy_buffer(proc->unixsocket, host->unixsocket); + buffer_append_string_len(proc->unixsocket, + CONST_STR_LEN("-")); + buffer_append_int(proc->unixsocket, pno); + } + + if (s->debug) { + log_error_write(srv, __FILE__, __LINE__, "ssdsbsdsd", + "--- gw spawning", + "\n\tport:", host->port, + "\n\tsocket", host->unixsocket, + "\n\tcurrent:", pno, "/", host->max_procs); + } + + if (0 != gw_proc_sockaddr_init(srv, host, proc)) { + gw_proc_free(proc); + goto error; + } + + if (!srv->srvconf.preflight_check + && gw_spawn_connection(srv, host, proc, s->debug)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "[ERROR]: spawning gw failed."); + gw_proc_free(proc); + goto error; + } + + gw_status_init(srv, host, proc); + + proc->next = host->first; + if (host->first) host->first->prev = proc; + + host->first = proc; + } + } else { + gw_proc *proc; + + proc = gw_proc_init(); + proc->id = host->num_procs++; + host->max_id++; + gw_proc_set_state(host, proc, PROC_STATE_RUNNING); + + if (buffer_string_is_empty(host->unixsocket)) { + proc->port = host->port; + } else { + buffer_copy_buffer(proc->unixsocket, host->unixsocket); + } + + gw_status_init(srv, host, proc); + + host->first = proc; + + host->min_procs = 1; + host->max_procs = 1; + + if (0 != gw_proc_sockaddr_init(srv, host, proc)) goto error; + } + + if (!buffer_string_is_empty(gw_mode)) { + if (strcmp(gw_mode->ptr, "responder") == 0) { + host_mode = GW_RESPONDER; + } else if (strcmp(gw_mode->ptr, "authorizer") == 0) { + host_mode = GW_AUTHORIZER; + } else { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "WARNING: unknown gw mode:", + gw_mode,"(ignored, mode set to responder)"); + } + } + + if (host->xsendfile_docroot->used) { + size_t k; + for (k = 0; k < host->xsendfile_docroot->used; ++k) { + data_string *ds = (data_string *)host->xsendfile_docroot->data[k]; + if (ds->type != TYPE_STRING) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected type for x-sendfile-docroot; expected: \"x-sendfile-docroot\" => ( \"/allowed/path\", ... )"); + goto error; + } + if (ds->value->ptr[0] != '/') { + log_error_write(srv, __FILE__, __LINE__, "SBs", + "x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\""); + goto error; + } + buffer_path_simplify(ds->value, ds->value); + buffer_append_slash(ds->value); + } + } + + /* s->exts is list of exts -> hosts + * s->exts now used as combined list + * of authorizer and responder hosts (for backend maintenance) + * s->exts_auth is list of exts -> authorizer hosts + * s->exts_resp is list of exts -> responder hosts + * For each path/extension: + * there may be an independent GW_AUTHORIZER and GW_RESPONDER + * (The GW_AUTHORIZER and GW_RESPONDER could be handled by the same + * host, and an admin might want to do that for large uploads, + * since GW_AUTHORIZER runs prior to receiving (potentially large) + * request body from client and can authorizer or deny request + * prior to receiving the full upload) + */ + gw_extension_insert(s->exts, da_ext->key, host); + + if (host_mode == GW_AUTHORIZER) { + ++host->refcount; + gw_extension_insert(s->exts_auth, da_ext->key, host); + } else if (host_mode == GW_RESPONDER) { + ++host->refcount; + gw_extension_insert(s->exts_resp, da_ext->key, host); + } /*(else should have been rejected above)*/ + + host = NULL; + } + } + + buffer_free(gw_mode); + return 1; + +error: + if (NULL != host) gw_host_free(host); + buffer_free(gw_mode); + return 0; +} + +int gw_set_defaults_balance(server *srv, gw_plugin_config *s, data_unset *du) { + buffer *b; + if (NULL == du) { + b = NULL; + } else if (du->type == TYPE_STRING) { + b = ((data_string *)du)->value; + } else { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected type for xxxxx.balance; expected string"); + return 0; + } + if (buffer_string_is_empty(b)) { + s->balance = GW_BALANCE_LEAST_CONNECTION; + } else if (buffer_is_equal_string(b, CONST_STR_LEN("fair"))) { + s->balance = GW_BALANCE_LEAST_CONNECTION; + } else if (buffer_is_equal_string(b, CONST_STR_LEN("least-connection"))) { + s->balance = GW_BALANCE_LEAST_CONNECTION; + } else if (buffer_is_equal_string(b, CONST_STR_LEN("round-robin"))) { + s->balance = GW_BALANCE_RR; + } else if (buffer_is_equal_string(b, CONST_STR_LEN("hash"))) { + s->balance = GW_BALANCE_HASH; + } else if (buffer_is_equal_string(b, CONST_STR_LEN("sticky"))) { + s->balance = GW_BALANCE_STICKY; + } else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "xxxxx.balance has to be one of: " + "least-connection, round-robin, hash, sticky, but not:", + b); + return 0; + } + return 1; +} + +static void gw_set_state(server *srv, gw_handler_ctx *hctx, gw_connection_state_t state) { + hctx->state = state; + hctx->state_timestamp = srv->cur_ts; +} + + +void gw_set_transparent(server *srv, gw_handler_ctx *hctx) { + if (AF_UNIX != hctx->host->family) { + if (-1 == fdevent_set_tcp_nodelay(hctx->fd, 1)) { + /*(error, but not critical)*/ + } + } + hctx->wb_reqlen = -1; + gw_set_state(srv, hctx, GW_STATE_WRITE); +} + + +static void gw_backend_close(server *srv, gw_handler_ctx *hctx) { + if (hctx->fd >= 0) { + fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); + /*fdevent_unregister(srv->ev, hctx->fd);*//*(handled below)*/ + fdevent_sched_close(srv->ev, hctx->fd, 1); + hctx->fd = -1; + hctx->fde_ndx = -1; + } + + if (hctx->host) { + if (hctx->proc) { + gw_proc_release(srv, hctx->host, hctx->proc, hctx->conf.debug); + hctx->proc = NULL; + } + + gw_host_reset(srv, hctx->host); + hctx->host = NULL; + } +} + +static void gw_connection_close(server *srv, gw_handler_ctx *hctx) { + gw_plugin_data *p = hctx->plugin_data; + connection *con = hctx->remote_conn; + + gw_backend_close(srv, hctx); + handler_ctx_free(hctx); + con->plugin_ctx[p->id] = NULL; + + if (con->mode == p->id) { + http_response_backend_done(srv, con); + } +} + +static handler_t gw_reconnect(server *srv, gw_handler_ctx *hctx) { + gw_backend_close(srv, hctx); + + hctx->host = gw_host_get(srv, hctx->remote_conn, hctx->ext, + hctx->conf.balance, hctx->conf.debug); + if (NULL == hctx->host) return HANDLER_FINISHED; + + gw_host_assign(srv, hctx->host); + hctx->request_id = 0; + hctx->opts.xsendfile_allow = hctx->host->xsendfile_allow; + hctx->opts.xsendfile_docroot = hctx->host->xsendfile_docroot; + gw_set_state(srv, hctx, GW_STATE_INIT); + return HANDLER_COMEBACK; +} + + +handler_t gw_connection_reset(server *srv, connection *con, void *p_d) { + gw_plugin_data *p = p_d; + gw_handler_ctx *hctx = con->plugin_ctx[p->id]; + if (hctx) gw_connection_close(srv, hctx); + + return HANDLER_GO_ON; +} + + +static void gw_conditional_tcp_fin(server *srv, gw_handler_ctx *hctx) { + connection *con = hctx->remote_conn; + /*assert(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN);*/ + if (!chunkqueue_is_empty(hctx->wb)) return; + if (!hctx->host->tcp_fin_propagate) return; + if (hctx->gw_mode == GW_AUTHORIZER) return; + if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BACKEND_SHUT_WR) + return; + + /* propagate shutdown SHUT_WR to backend if TCP half-close on con->fd */ + con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_BACKEND_SHUT_WR; + con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; + con->is_readable = 0; + shutdown(hctx->fd, SHUT_WR); + fdevent_event_clr(srv->ev, &hctx->fde_ndx, hctx->fd, FDEVENT_OUT); +} + +static handler_t gw_write_request(server *srv, gw_handler_ctx *hctx) { + switch(hctx->state) { + case GW_STATE_INIT: + /* do we have a running process for this host (max-procs) ? */ + hctx->proc = NULL; + + for (gw_proc *proc = hctx->host->first; proc; proc = proc->next) { + if (proc->state == PROC_STATE_RUNNING) { + hctx->proc = proc; + break; + } + } + + /* all children are dead */ + if (hctx->proc == NULL) { + return HANDLER_ERROR; + } + + /* check the other procs if they have a lower load */ + for (gw_proc *proc = hctx->proc->next; proc; proc = proc->next) { + if (proc->state != PROC_STATE_RUNNING) continue; + if (proc->load < hctx->proc->load) hctx->proc = proc; + } + + gw_proc_load_inc(srv, hctx->host, hctx->proc); + + hctx->fd = fdevent_socket_nb_cloexec(hctx->host->family,SOCK_STREAM,0); + if (-1 == hctx->fd) { + if (errno == EMFILE || errno == EINTR) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "wait for fd at connection:", + hctx->remote_conn->fd); + return HANDLER_WAIT_FOR_FD; + } + + log_error_write(srv, __FILE__, __LINE__, "ssdd", + "socket failed:", strerror(errno), + srv->cur_fds, srv->max_fds); + return HANDLER_ERROR; + } + + srv->cur_fds++; + + fdevent_register(srv->ev, hctx->fd, gw_handle_fdevent, hctx); + + if (hctx->proc->is_local) { + hctx->pid = hctx->proc->pid; + } + + switch (gw_establish_connection(srv, hctx->host, hctx->proc, hctx->pid, + hctx->fd, hctx->conf.debug)) { + case 1: /* connection is in progress */ + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + gw_set_state(srv, hctx, GW_STATE_CONNECT_DELAYED); + return HANDLER_WAIT_FOR_EVENT; + case -1:/* connection error */ + return HANDLER_ERROR; + case 0: /* everything is ok, go on */ + hctx->reconnects = 0; + break; + } + /* fall through */ + case GW_STATE_CONNECT_DELAYED: + if (hctx->state == GW_STATE_CONNECT_DELAYED) { /*(not GW_STATE_INIT)*/ + int socket_error = fdevent_connect_status(hctx->fd); + if (socket_error != 0) { + gw_proc_connect_error(srv, hctx->host, hctx->proc, hctx->pid, + socket_error, hctx->conf.debug); + return HANDLER_ERROR; + } + /* go on with preparing the request */ + } + + gw_proc_connect_success(srv, hctx->host, hctx->proc, hctx->conf.debug); + + gw_set_state(srv, hctx, GW_STATE_PREPARE_WRITE); + /* fall through */ + case GW_STATE_PREPARE_WRITE: + /* ok, we have the connection */ + + { + handler_t rc = hctx->create_env(srv, hctx); + if (HANDLER_GO_ON != rc) { + if (HANDLER_FINISHED != rc && HANDLER_ERROR != rc) + fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, + FDEVENT_OUT); + return rc; + } + } + + /*(disable Nagle algorithm if streaming and content-length unknown)*/ + if (AF_UNIX != hctx->host->family) { + connection *con = hctx->remote_conn; + if (con->request.content_length < 0) { + if (-1 == fdevent_set_tcp_nodelay(hctx->fd, 1)) { + /*(error, but not critical)*/ + } + } + } + + fdevent_event_add(srv->ev, &hctx->fde_ndx, hctx->fd, + FDEVENT_IN | FDEVENT_RDHUP); + gw_set_state(srv, hctx, GW_STATE_WRITE); + /* fall through */ + case GW_STATE_WRITE: + if (!chunkqueue_is_empty(hctx->wb)) { + int ret; + #if 0 + if (hctx->conf.debug > 1) { + log_error_write(srv, __FILE__, __LINE__, "sdsx", + "send data to backend ( fd =", hctx->fd, + "), size =", chunkqueue_length(hctx->wb)); + } + #endif + ret = srv->network_backend_write(srv, hctx->fd, hctx->wb, + MAX_WRITE_LIMIT); + + chunkqueue_remove_finished_chunks(hctx->wb); + + if (ret < 0) { + switch(errno) { + case EPIPE: + case ENOTCONN: + case ECONNRESET: + /* the connection got dropped after accept() + * we don't care about that -- + * if you accept() it, you have to handle it. + */ + log_error_write(srv, __FILE__, __LINE__, "ssosb", + "connection was dropped after accept() " + "(perhaps the gw process died),", + "write-offset:", hctx->wb->bytes_out, + "socket:", hctx->proc->connection_name); + return HANDLER_ERROR; + default: + log_error_write(srv, __FILE__, __LINE__, "ssd", + "write failed:", strerror(errno), errno); + return HANDLER_ERROR; + } + } + } + + if (hctx->wb->bytes_out == hctx->wb_reqlen) { + fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_OUT); + gw_set_state(srv, hctx, GW_STATE_READ); + } else { + off_t wblen = hctx->wb->bytes_in - hctx->wb->bytes_out; + if ((hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0) + && wblen < 65536 - 16384) { + connection *con = hctx->remote_conn; + /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ + if (!(con->conf.stream_request_body + & FDEVENT_STREAM_REQUEST_POLLIN)) { + con->conf.stream_request_body |= + FDEVENT_STREAM_REQUEST_POLLIN; + con->is_readable = 1;/*trigger optimistic read from client*/ + } + } + if (0 == wblen) { + fdevent_event_clr(srv->ev,&hctx->fde_ndx,hctx->fd,FDEVENT_OUT); + } else { + fdevent_event_add(srv->ev,&hctx->fde_ndx,hctx->fd,FDEVENT_OUT); + } + } + + if (hctx->remote_conn->conf.stream_request_body + & FDEVENT_STREAM_REQUEST_TCP_FIN) + gw_conditional_tcp_fin(srv, hctx); + + return HANDLER_WAIT_FOR_EVENT; + case GW_STATE_READ: + /* waiting for a response */ + return HANDLER_WAIT_FOR_EVENT; + default: + log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state"); + return HANDLER_ERROR; + } +} + +static handler_t gw_write_error(server *srv, gw_handler_ctx *hctx) { + connection *con = hctx->remote_conn; + int status = con->http_status; + + if (hctx->state == GW_STATE_INIT || + hctx->state == GW_STATE_CONNECT_DELAYED) { + + /* (optimization to detect backend process exit while processing a + * large number of ready events; (this block could be removed)) */ + if (0 == srv->srvconf.max_worker) + gw_restart_dead_procs(srv, hctx->host, hctx->conf.debug, 0); + + /* cleanup this request and let request handler start request again */ + if (hctx->reconnects++ < 5) return gw_reconnect(srv, hctx); + } + + if (hctx->backend_error) hctx->backend_error(hctx); + gw_connection_close(srv, hctx); + con->http_status = (status == 400) ? 400 : 503; + return HANDLER_FINISHED; +} + +static handler_t gw_send_request(server *srv, gw_handler_ctx *hctx) { + handler_t rc = gw_write_request(srv, hctx); + return (HANDLER_ERROR != rc) ? rc : gw_write_error(srv, hctx); +} + + +static handler_t gw_recv_response(server *srv, gw_handler_ctx *hctx); + + +handler_t gw_handle_subrequest(server *srv, connection *con, void *p_d) { + gw_plugin_data *p = p_d; + gw_handler_ctx *hctx = con->plugin_ctx[p->id]; + if (NULL == hctx) return HANDLER_GO_ON; + if (con->mode != p->id) return HANDLER_GO_ON; /* not my job */ + + if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) + && con->file_started) { + if (chunkqueue_length(con->write_queue) > 65536 - 4096) { + fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + } + else if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN)){ + /* optimistic read from backend */ + handler_t rc; + rc = gw_recv_response(srv, hctx); /*(might invalidate hctx)*/ + if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ + fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + } + } + + /* (do not receive request body before GW_AUTHORIZER has run or else + * the request body is discarded with handler_ctx_clear() after running + * the FastCGI Authorizer) */ + + if (hctx->gw_mode != GW_AUTHORIZER + && (0 == hctx->wb->bytes_in + ? (con->state == CON_STATE_READ_POST || -1 == hctx->wb_reqlen) + : (hctx->wb->bytes_in < hctx->wb_reqlen || hctx->wb_reqlen < 0))) { + /* leave excess data in con->request_content_queue, which is + * buffered to disk if too large and backend can not keep up */ + /*(64k - 4k to attempt to avoid temporary files + * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ + if (hctx->wb->bytes_in - hctx->wb->bytes_out > 65536 - 4096) { + if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN) { + con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; + } + if (0 != hctx->wb->bytes_in) return HANDLER_WAIT_FOR_EVENT; + } + else { + handler_t r = connection_handle_read_post_state(srv, con); + chunkqueue *req_cq = con->request_content_queue; + #if 0 /*(not reached since we send 411 Length Required below)*/ + if (hctx->wb_reqlen < -1 && con->request.content_length >= 0) { + /* (completed receiving Transfer-Encoding: chunked) */ + hctx->wb_reqlen= -hctx->wb_reqlen + con->request.content_length; + if (hctx->stdin_append) { + handler_t rc = hctx->stdin_append(srv, hctx); + if (HANDLER_GO_ON != rc) return rc; + } + } + #endif + if ((0 != hctx->wb->bytes_in || -1 == hctx->wb_reqlen) + && !chunkqueue_is_empty(req_cq)) { + if (hctx->stdin_append) { + handler_t rc = hctx->stdin_append(srv, hctx); + if (HANDLER_GO_ON != rc) return rc; + } + else + chunkqueue_append_chunkqueue(hctx->wb, req_cq); + if (fdevent_event_get_interest(srv->ev,hctx->fd) & FDEVENT_OUT){ + return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r; + } + } + if (r != HANDLER_GO_ON) return r; + + + /* XXX: create configurable flag */ + /* CGI environment requires that Content-Length be set. + * Send 411 Length Required if Content-Length missing. + * (occurs here if client sends Transfer-Encoding: chunked + * and module is flagged to stream request body to backend) */ + /* proxy currently sends HTTP/1.0 request and ideally should send + * Content-Length with request if request body is present, so + * send 411 Length Required if Content-Length missing. */ + if (-1 == con->request.content_length) { + return connection_handle_read_post_error(srv, con, 411); + } + } + } + + { + handler_t rc =((0==hctx->wb->bytes_in || !chunkqueue_is_empty(hctx->wb)) + && hctx->state != GW_STATE_CONNECT_DELAYED) + ? gw_send_request(srv, hctx) + : HANDLER_WAIT_FOR_EVENT; + if (HANDLER_WAIT_FOR_EVENT != rc) return rc; + } + + if (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_TCP_FIN) + gw_conditional_tcp_fin(srv, hctx); + + return HANDLER_WAIT_FOR_EVENT; +} + + +static handler_t gw_recv_response(server *srv, gw_handler_ctx *hctx) { + connection *con = hctx->remote_conn; + gw_proc *proc = hctx->proc; + gw_host *host = hctx->host; + /*(XXX: make this a configurable flag for other protocols)*/ + buffer *b = hctx->opts.backend == BACKEND_FASTCGI + ? chunk_buffer_acquire() + : hctx->response; + + handler_t rc = http_response_read(srv, hctx->remote_conn, &hctx->opts, + b, hctx->fd, &hctx->fde_ndx); + + if (b != hctx->response) chunk_buffer_release(b); + + switch (rc) { + default: + return HANDLER_GO_ON; + case HANDLER_FINISHED: + if (hctx->gw_mode == GW_AUTHORIZER + && (200 == con->http_status || 0 == con->http_status)) { + /* + * If we are here in AUTHORIZER mode then a request for authorizer + * was processed already, and status 200 has been returned. We need + * now to handle authorized request. + */ + buffer *physpath = NULL; + + if (!buffer_string_is_empty(host->docroot)) { + buffer_copy_buffer(con->physical.doc_root, host->docroot); + buffer_copy_buffer(con->physical.basedir, host->docroot); + + buffer_copy_buffer(con->physical.path, host->docroot); + buffer_append_string_buffer(con->physical.path, con->uri.path); + physpath = con->physical.path; + } + + proc->last_used = srv->cur_ts; + gw_backend_close(srv, hctx); + handler_ctx_clear(hctx); + + /* don't do more than 6 loops here; normally shouldn't happen */ + if (++con->loops_per_request > 5) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "too many loops while processing request:", + con->request.orig_uri); + con->http_status = 500; /* Internal Server Error */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + + /* restart the request so other handlers can process it */ + + if (physpath) con->physical.path = NULL; + connection_response_reset(srv,con);/*(includes con->http_status=0)*/ + /* preserve con->physical.path with modified docroot */ + if (physpath) con->physical.path = physpath; + + /*(FYI: if multiple FastCGI authorizers were to be supported, + * next one could be started here instead of restarting request)*/ + + con->mode = DIRECT; + return HANDLER_COMEBACK; + } else { + /* we are done */ + gw_connection_close(srv, hctx); + } + + return HANDLER_FINISHED; + case HANDLER_COMEBACK: /*(not expected; treat as error)*/ + case HANDLER_ERROR: + /* (optimization to detect backend process exit while processing a + * large number of ready events; (this block could be removed)) */ + if (proc->is_local && 1 == proc->load && proc->pid == hctx->pid + && proc->state != PROC_STATE_DIED && 0 == srv->srvconf.max_worker) { + /* intentionally check proc->disabed_until before gw_proc_waitpid */ + if (proc->disabled_until < srv->cur_ts + && 0 != gw_proc_waitpid(srv, host, proc)) { + if (hctx->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "ssbsdsd", + "--- gw spawning", + "\n\tsocket", proc->connection_name, + "\n\tcurrent:", 1, "/", host->num_procs); + } + + if (gw_spawn_connection(srv, host, proc, hctx->conf.debug)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "respawning failed, will retry later"); + } + } + } + + if (con->file_started == 0) { + /* nothing has been sent out yet, try to use another child */ + + if (hctx->wb->bytes_out == 0 && + hctx->reconnects++ < 5) { + + log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs", + "response not received, request not sent", + "on socket:", proc->connection_name, + "for", con->uri.path, "?", con->uri.query, ", reconnecting"); + + return gw_reconnect(srv, hctx); + } + + log_error_write(srv, __FILE__, __LINE__, "sosbsBSBs", + "response not received, request sent:", hctx->wb->bytes_out, + "on socket:", proc->connection_name, "for", + con->uri.path, "?", con->uri.query, ", closing connection"); + } else { + log_error_write(srv, __FILE__, __LINE__, "ssbsBSBs", + "response already sent out, but backend returned error", + "on socket:", proc->connection_name, "for", + con->uri.path, "?", con->uri.query, ", terminating connection"); + } + + if (hctx->backend_error) hctx->backend_error(hctx); + http_response_backend_error(srv, con); + gw_connection_close(srv, hctx); + return HANDLER_FINISHED; + } +} + + +static handler_t gw_handle_fdevent(server *srv, void *ctx, int revents) { + gw_handler_ctx *hctx = ctx; + connection *con = hctx->remote_conn; + + joblist_append(srv, con); + + if (revents & FDEVENT_IN) { + handler_t rc = gw_recv_response(srv, hctx); /*(might invalidate hctx)*/ + if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ + } + + if (revents & FDEVENT_OUT) { + return gw_send_request(srv, hctx); /*(might invalidate hctx)*/ + } + + /* perhaps this issue is already handled */ + if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) { + if (hctx->state == GW_STATE_CONNECT_DELAYED) { + /* getoptsock will catch this one (right ?) + * + * if we are in connect we might get an EINPROGRESS + * in the first call and an FDEVENT_HUP in the + * second round + * + * FIXME: as it is a bit ugly. + * + */ + gw_send_request(srv, hctx); + } else if (con->file_started) { + /* drain any remaining data from kernel pipe buffers + * even if (con->conf.stream_response_body + * & FDEVENT_STREAM_RESPONSE_BUFMIN) + * since event loop will spin on fd FDEVENT_HUP event + * until unregistered. */ + handler_t rc; + const unsigned short flags = con->conf.stream_response_body; + con->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN; + con->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP; + do { + rc = gw_recv_response(srv,hctx); /*(might invalidate hctx)*/ + } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/ + con->conf.stream_response_body = flags; + return rc; /* HANDLER_FINISHED or HANDLER_ERROR */ + } else { + gw_proc *proc = hctx->proc; + log_error_write(srv, __FILE__, __LINE__, "sBSbsbsd", + "error: unexpected close of gw connection for", + con->uri.path, "?", con->uri.query, + "(no gw process on socket:", proc->connection_name, "?)", + hctx->state); + + gw_connection_close(srv, hctx); + } + } else if (revents & FDEVENT_ERR) { + log_error_write(srv, __FILE__, __LINE__, "s", + "gw: got a FDEVENT_ERR. Don't know why."); + + if (hctx->backend_error) hctx->backend_error(hctx); + http_response_backend_error(srv, con); + gw_connection_close(srv, hctx); + } + + return HANDLER_FINISHED; +} + +handler_t gw_check_extension(server *srv, connection *con, gw_plugin_data *p, int uri_path_handler, size_t hctx_sz) { + #if 0 /*(caller must handle)*/ + if (con->mode != DIRECT) return HANDLER_GO_ON; + gw_patch_connection(srv, con, p); + if (NULL == p->conf.exts) return HANDLER_GO_ON; + #endif + + buffer *fn = uri_path_handler ? con->uri.path : con->physical.path; + size_t s_len = buffer_string_length(fn); + gw_extension *extension = NULL; + gw_host *host = NULL; + gw_handler_ctx *hctx; + unsigned short gw_mode; + + if (0 == s_len) return HANDLER_GO_ON; /*(not expected)*/ + + /* check p->conf.exts_auth list and then p->conf.ext_resp list + * (skip p->conf.exts_auth if array is empty + * or if GW_AUTHORIZER already ran in this request) */ + hctx = con->plugin_ctx[p->id]; + /*(hctx not NULL if GW_AUTHORIZER ran; hctx->ext_auth check is redundant)*/ + gw_mode = (NULL == hctx || NULL == hctx->ext_auth) + ? 0 /*GW_AUTHORIZER p->conf.exts_auth will be searched next*/ + : GW_AUTHORIZER; /*GW_RESPONDER p->conf.exts_resp will be searched next*/ + + do { + + gw_exts *exts; + if (0 == gw_mode) { + gw_mode = GW_AUTHORIZER; + exts = p->conf.exts_auth; + } else { + gw_mode = GW_RESPONDER; + exts = p->conf.exts_resp; + } + + if (0 == exts->used) continue; + + /* gw.map-extensions maps extensions to existing gw.server entries + * + * gw.map-extensions = ( ".php3" => ".php" ) + * + * gw.server = ( ".php" => ... ) + * + * */ + + /* check if extension-mapping matches */ + if (p->conf.ext_mapping) { + data_string *ds = + (data_string *)array_match_key_suffix(p->conf.ext_mapping, fn); + if (NULL != ds) { + /* found a mapping */ + /* check if we know the extension */ + size_t k; + for (k = 0; k < exts->used; ++k) { + extension = exts->exts[k]; + + if (buffer_is_equal(ds->value, extension->key)) { + break; + } + } + + if (k == exts->used) { + /* found nothing */ + extension = NULL; + } + } + } + + if (extension == NULL) { + size_t uri_path_len = buffer_string_length(con->uri.path); + + /* check if extension matches */ + for (size_t k = 0; k < exts->used; ++k) { + gw_extension *ext = exts->exts[k]; + size_t ct_len = buffer_string_length(ext->key); + + /* check _url_ in the form "/gw_pattern" */ + if (ext->key->ptr[0] == '/') { + if (ct_len <= uri_path_len + && 0==memcmp(con->uri.path->ptr,ext->key->ptr,ct_len)){ + extension = ext; + break; + } + } else if (ct_len <= s_len + && 0 == memcmp(fn->ptr + s_len - ct_len, + ext->key->ptr, ct_len)) { + /* check extension in the form ".fcg" */ + extension = ext; + break; + } + } + } + + } while (NULL == extension && gw_mode != GW_RESPONDER); + + /* extension doesn't match */ + if (NULL == extension) { + return HANDLER_GO_ON; + } + + /* check if we have at least one server for this extension up and running */ + host = gw_host_get(srv, con, extension, p->conf.balance, p->conf.debug); + if (NULL == host) { + return HANDLER_FINISHED; + } + + /* a note about no handler is not sent yet */ + extension->note_is_sent = 0; + + /* + * if check-local is disabled, use the uri.path handler + * + */ + + /* init handler-context */ + if (uri_path_handler) { + if (host->check_local != 0) { + return HANDLER_GO_ON; + } else { + /* do not split path info for authorizer */ + if (gw_mode != GW_AUTHORIZER) { + /* the prefix is the SCRIPT_NAME, + * everything from start to the next slash + * this is important for check-local = "disable" + * + * if prefix = /admin.gw + * + * /admin.gw/foo/bar + * + * SCRIPT_NAME = /admin.gw + * PATH_INFO = /foo/bar + * + * if prefix = /cgi-bin/ + * + * /cgi-bin/foo/bar + * + * SCRIPT_NAME = /cgi-bin/foo + * PATH_INFO = /bar + * + * if prefix = /, and fix-root-path-name is enable + * + * /cgi-bin/foo/bar + * + * SCRIPT_NAME = /cgi-bin/foo + * PATH_INFO = /bar + * + */ + char *pathinfo; + + /* the rewrite is only done for /prefix/? matches */ + if (host->fix_root_path_name && extension->key->ptr[0] == '/' + && extension->key->ptr[1] == '\0'){ + buffer_copy_buffer(con->request.pathinfo, con->uri.path); + buffer_clear(con->uri.path); + } else if (extension->key->ptr[0] == '/' + && buffer_string_length(con->uri.path) + > buffer_string_length(extension->key) + && (pathinfo = + strchr(con->uri.path->ptr + + buffer_string_length(extension->key), + '/')) != NULL) { + /* rewrite uri.path and pathinfo */ + + buffer_copy_string(con->request.pathinfo, pathinfo); + buffer_string_set_length( + con->uri.path, + buffer_string_length(con->uri.path) + - buffer_string_length(con->request.pathinfo)); + } + } + } + } + + if (!hctx) hctx = handler_ctx_init(hctx_sz); + + hctx->remote_conn = con; + hctx->plugin_data = p; + hctx->host = host; + hctx->proc = NULL; + hctx->ext = extension; + gw_host_assign(srv, host); + + hctx->gw_mode = gw_mode; + if (gw_mode == GW_AUTHORIZER) { + hctx->ext_auth = hctx->ext; + } + + /*hctx->conf.exts = p->conf.exts;*/ + /*hctx->conf.exts_auth = p->conf.exts_auth;*/ + /*hctx->conf.exts_resp = p->conf.exts_resp;*/ + /*hctx->conf.ext_mapping = p->conf.ext_mapping;*/ + hctx->conf.balance = p->conf.balance; + hctx->conf.proto = p->conf.proto; + hctx->conf.debug = p->conf.debug; + + hctx->opts.fdfmt = S_IFSOCK; + hctx->opts.authorizer = (gw_mode == GW_AUTHORIZER); + hctx->opts.local_redir = 0; + hctx->opts.xsendfile_allow = host->xsendfile_allow; + hctx->opts.xsendfile_docroot = host->xsendfile_docroot; + + con->plugin_ctx[p->id] = hctx; + + con->mode = p->id; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "handling it in mod_gw"); + } + + return HANDLER_GO_ON; +} + +static void gw_handle_trigger_host(server *srv, gw_host *host, int debug) { + /* + * TODO: + * + * - add timeout for a connect to a non-gw process + * (use state_timestamp + state) + * + * perhaps we should kill a connect attempt after 10-15 seconds + * + * currently we wait for the TCP timeout which is 180 seconds on Linux + */ + + /* check each child proc to detect if proc exited */ + + gw_proc *proc; + time_t idle_timestamp; + int overload = 1; + + for (proc = host->first; proc; proc = proc->next) { + gw_proc_waitpid(srv, host, proc); + } + + gw_restart_dead_procs(srv, host, debug, 1); + + /* check if adaptive spawning enabled */ + if (host->min_procs == host->max_procs) return; + if (buffer_string_is_empty(host->bin_path)) return; + + for (proc = host->first; proc; proc = proc->next) { + if (proc->load <= host->max_load_per_proc) { + overload = 0; + break; + } + } + + if (overload && host->num_procs && host->num_procs < host->max_procs) { + /* overload, spawn new child */ + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "s", + "overload detected, spawning a new child"); + } + + gw_proc_spawn(srv, host, debug); + } + + idle_timestamp = srv->cur_ts - host->idle_timeout; + for (proc = host->first; proc; proc = proc->next) { + if (host->num_procs <= host->min_procs) break; + if (0 != proc->load) continue; + if (proc->pid <= 0) continue; + if (proc->last_used >= idle_timestamp) continue; + + /* terminate proc that has been idling for a long time */ + if (debug) { + log_error_write(srv, __FILE__, __LINE__, "ssbsd", + "idle-timeout reached, terminating child:", + "socket:", proc->unixsocket, "pid", proc->pid); + } + + gw_proc_kill(srv, host, proc); + + /* proc is now in unused, let next second handle next process */ + break; + } + + for (proc = host->unused_procs; proc; proc = proc->next) { + gw_proc_waitpid(srv, host, proc); + } +} + +static void gw_handle_trigger_exts(server *srv, gw_exts *exts, int debug) { + for (size_t j = 0; j < exts->used; ++j) { + gw_extension *ex = exts->exts[j]; + for (size_t n = 0; n < ex->used; ++n) { + gw_handle_trigger_host(srv, ex->hosts[n], debug); + } + } +} + +static void gw_handle_trigger_exts_wkr(server *srv, gw_exts *exts) { + for (size_t j = 0; j < exts->used; ++j) { + gw_extension * const ex = exts->exts[j]; + for (size_t n = 0; n < ex->used; ++n) { + gw_host * const host = ex->hosts[n]; + for (gw_proc *proc = host->first; proc; proc = proc->next) { + if (proc->state == PROC_STATE_OVERLOADED) + gw_proc_check_enable(srv, host, proc); + } + } + } +} + +handler_t gw_handle_trigger(server *srv, void *p_d) { + gw_plugin_data *p = p_d; + int wkr = (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid); + + for (size_t i = 0; i < srv->config_context->used; i++) { + gw_plugin_config *conf = p->config_storage[i]; + gw_exts *exts = conf->exts; + int debug = conf->debug ? conf->debug : p->config_storage[0]->debug; + if (NULL == exts) continue; + wkr + ? gw_handle_trigger_exts_wkr(srv, exts) + : gw_handle_trigger_exts(srv, exts, debug); + } + + return HANDLER_GO_ON; +} + +handler_t gw_handle_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) { + gw_plugin_data *p = p_d; + if (0 != srv->srvconf.max_worker && p->srv_pid != srv->pid) + return HANDLER_GO_ON; + + for (size_t i = 0; i < srv->config_context->used; ++i) { + gw_plugin_config *conf = p->config_storage[i]; + gw_exts *exts = conf->exts; + int debug = conf->debug ? conf->debug : p->config_storage[0]->debug; + if (NULL == exts) continue; + for (size_t j = 0; j < exts->used; ++j) { + gw_extension *ex = exts->exts[j]; + for (size_t n = 0; n < ex->used; ++n) { + gw_host *host = ex->hosts[n]; + gw_proc *proc; + for (proc = host->first; proc; proc = proc->next) { + if (!proc->is_local || proc->pid != pid) continue; + + gw_proc_waitpid_log(srv, host, proc, status); + gw_proc_set_state(host, proc, PROC_STATE_DIED); + proc->pid = 0; + + /* restart, but avoid spinning if child exits too quickly */ + if (proc->disabled_until < srv->cur_ts) { + if (proc->state != PROC_STATE_KILLED) + proc->disabled_until = srv->cur_ts; + if (gw_spawn_connection(srv, host, proc, debug)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "ERROR: spawning gw failed."); + } + } + + return HANDLER_FINISHED; + } + for (proc = host->unused_procs; proc; proc = proc->next) { + if (!proc->is_local || proc->pid != pid) continue; + + gw_proc_waitpid_log(srv, host, proc, status); + if (proc->state != PROC_STATE_KILLED) + proc->disabled_until = srv->cur_ts; + gw_proc_set_state(host, proc, PROC_STATE_DIED); + proc->pid = 0; + return HANDLER_FINISHED; + } + } + } + } + + return HANDLER_GO_ON; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/gw_backend.h b/data/lighttpd/lighttpd-1.4.53/src/gw_backend.h new file mode 100644 index 000000000..5b00705a5 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/gw_backend.h @@ -0,0 +1,348 @@ +#ifndef INCLUDED_GW_BACKEND_H +#define INCLUDED_GW_BACKEND_H + +#include "first.h" + +#include <sys/types.h> +#include "sys-socket.h" + +#include "array.h" +#include "buffer.h" + +typedef struct { + char **ptr; + + size_t size; + size_t used; +} char_array; + +typedef struct gw_proc { + size_t id; /* id will be between 1 and max_procs */ + buffer *unixsocket; /* config.socket + "-" + id */ + unsigned port; /* config.port + pno */ + socklen_t saddrlen; + struct sockaddr *saddr; + + /* either tcp:<host>:<port> or unix:<socket> for debugging purposes */ + buffer *connection_name; + + pid_t pid; /* PID of the spawned process (0 if not spawned locally) */ + + + size_t load; /* number of requests waiting on this process */ + + time_t last_used; /* see idle_timeout */ + size_t requests; /* see max_requests */ + struct gw_proc *prev, *next; /* see first */ + + time_t disabled_until; /* proc disabled until given time */ + + int is_local; + + enum { + PROC_STATE_RUNNING, /* alive */ + PROC_STATE_OVERLOADED, /* listen-queue is full */ + PROC_STATE_DIED_WAIT_FOR_PID, /* */ + PROC_STATE_DIED, /* marked as dead, should be restarted */ + PROC_STATE_KILLED /* killed (signal sent to proc) */ + } state; +} gw_proc; + +typedef struct { + /* the key that is used to reference this value */ + buffer *id; + + /* list of processes handling this extension + * sorted by lowest load + * + * whenever a job is done move it up in the list + * until it is sorted, move it down as soon as the + * job is started + */ + gw_proc *first; + gw_proc *unused_procs; + + /* + * spawn at least min_procs, at max_procs. + * + * as soon as the load of the first entry + * is max_load_per_proc we spawn a new one + * and add it to the first entry and give it + * the load + * + */ + + unsigned short min_procs; + unsigned short max_procs; + size_t num_procs; /* how many procs are started */ + size_t active_procs; /* how many procs in state PROC_STATE_RUNNING */ + + unsigned short max_load_per_proc; + + /* + * kick the process from the list if it was not + * used for idle_timeout until min_procs is + * reached. this helps to get the processlist + * small again we had a small peak load. + * + */ + + unsigned short idle_timeout; + + /* + * time after a disabled remote connection is tried to be re-enabled + * + * + */ + + unsigned short disable_time; + + /* + * some gw processes get a little bit larger + * than wanted. max_requests_per_proc kills a + * process after a number of handled requests. + * + */ + size_t max_requests_per_proc; + + + /* config */ + + /* + * host:port + * + * if host is one of the local IP adresses the + * whole connection is local + * + * if port is not 0, and host is not specified, + * "localhost" (INADDR_LOOPBACK) is assumed. + * + */ + buffer *host; + unsigned short port; + unsigned short family; /* sa_family_t */ + + /* + * Unix Domain Socket + * + * instead of TCP/IP we can use Unix Domain Sockets + * - more secure (you have fileperms to play with) + * - more control (on locally) + * - more speed (no extra overhead) + */ + buffer *unixsocket; + + /* if socket is local we can start the gw process ourself + * + * bin-path is the path to the binary + * + * check min_procs and max_procs for the number + * of process to start up + */ + buffer *bin_path; + + /* bin-path is set bin-environment is taken to + * create the environement before starting the + * FastCGI process + * + */ + array *bin_env; + + array *bin_env_copy; + + /* + * docroot-translation between URL->phys and the + * remote host + * + * reasons: + * - different dir-layout if remote + * - chroot if local + * + */ + buffer *docroot; + + /* + * check_local tells you if the phys file is stat()ed + * or not. FastCGI doesn't care if the service is + * remote. If the web-server side doesn't contain + * the FastCGI-files we should not stat() for them + * and say '404 not found'. + */ + unsigned short check_local; + + /* + * append PATH_INFO to SCRIPT_FILENAME + * + * php needs this if cgi.fix_pathinfo is provided + * + */ + + unsigned short break_scriptfilename_for_php; + + /* + * workaround for program when prefix="/" + * + * rule to build PATH_INFO is hardcoded for when check_local is disabled + * enable this option to use the workaround + * + */ + + unsigned short fix_root_path_name; + + /* + * If the backend includes X-Sendfile in the response + * we use the value as filename and ignore the content. + * + */ + unsigned short xsendfile_allow; + array *xsendfile_docroot; + + ssize_t load; + + size_t max_id; /* corresponds most of the time to num_procs */ + + buffer *strip_request_uri; + + unsigned short tcp_fin_propagate; + unsigned short kill_signal; /* we need a setting for this as libfcgi + applications prefer SIGUSR1 while the + rest of the world would use SIGTERM + *sigh* */ + + int listen_backlog; + int refcount; + + char_array args; +} gw_host; + +/* + * one extension can have multiple hosts assigned + * one host can spawn additional processes on the same + * socket (if we control it) + * + * ext -> host -> procs + * 1:n 1:n + * + * if the gw process is remote that whole goes down + * to + * + * ext -> host -> procs + * 1:n 1:1 + * + * in case of PHP and FCGI_CHILDREN we have again a procs + * but we don't control it directly. + * + */ + +typedef struct { + buffer *key; /* like .php */ + + int note_is_sent; + int last_used_ndx; + + gw_host **hosts; + + size_t used; + size_t size; +} gw_extension; + +typedef struct { + gw_extension **exts; + + size_t used; + size_t size; +} gw_exts; + + + + +#include "base_decls.h" +#include "chunk.h" +#include "plugin.h" +#include "response.h" + +typedef struct gw_plugin_config { + gw_exts *exts; + gw_exts *exts_auth; + gw_exts *exts_resp; + + array *ext_mapping; + + int balance; + int proto; + int debug; +} gw_plugin_config; + +/* generic plugin data, shared between all connections */ +typedef struct gw_plugin_data { + PLUGIN_DATA; + gw_plugin_config **config_storage; + + gw_plugin_config conf; /* used only as long as no gw_handler_ctx is setup */ + pid_t srv_pid; +} gw_plugin_data; + +/* connection specific data */ +typedef enum { + GW_STATE_INIT, + GW_STATE_CONNECT_DELAYED, + GW_STATE_PREPARE_WRITE, + GW_STATE_WRITE, + GW_STATE_READ +} gw_connection_state_t; + +#define GW_RESPONDER 1 +#define GW_AUTHORIZER 2 +#define GW_FILTER 3 /*(not implemented)*/ + +typedef struct gw_handler_ctx { + gw_proc *proc; + gw_host *host; + gw_extension *ext; + gw_extension *ext_auth; /* (future: might allow multiple authorizers)*/ + unsigned short gw_mode; /* mode: GW_AUTHORIZER or GW_RESPONDER */ + + gw_connection_state_t state; + time_t state_timestamp; + + chunkqueue *rb; /* read queue */ + chunkqueue *wb; /* write queue */ + off_t wb_reqlen; + + buffer *response; + + int fd; /* fd to the gw process */ + int fde_ndx; /* index into the fd-event buffer */ + + pid_t pid; + int reconnects; /* number of reconnect attempts */ + + int request_id; + int send_content_body; + + http_response_opts opts; + gw_plugin_config conf; + + connection *remote_conn; /* dumb pointer */ + gw_plugin_data *plugin_data; /* dumb pointer */ + handler_t(*stdin_append)(server *srv, struct gw_handler_ctx *hctx); + handler_t(*create_env)(server *srv, struct gw_handler_ctx *hctx); + void(*backend_error)(struct gw_handler_ctx *hctx); + void(*handler_ctx_free)(void *hctx); +} gw_handler_ctx; + + +void * gw_init(void); +void gw_plugin_config_free(gw_plugin_config *s); +handler_t gw_free(server *srv, void *p_d); +int gw_set_defaults_backend(server *srv, gw_plugin_data *p, data_unset *du, size_t i, int sh_exec); +int gw_set_defaults_balance(server *srv, gw_plugin_config *s, data_unset *du); +handler_t gw_check_extension(server *srv, connection *con, gw_plugin_data *p, int uri_path_handler, size_t hctx_sz); +handler_t gw_connection_reset(server *srv, connection *con, void *p_d); +handler_t gw_handle_subrequest(server *srv, connection *con, void *p_d); +handler_t gw_handle_trigger(server *srv, void *p_d); +handler_t gw_handle_waitpid_cb(server *srv, void *p_d, pid_t pid, int status); + +void gw_set_transparent(server *srv, gw_handler_ctx *hctx); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/http-header-glue.c b/data/lighttpd/lighttpd-1.4.53/src/http-header-glue.c new file mode 100644 index 000000000..6aae9728c --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/http-header-glue.c @@ -0,0 +1,1527 @@ +#include "first.h" + +#include "base.h" +#include "array.h" +#include "buffer.h" +#include "fdevent.h" +#include "log.h" +#include "etag.h" +#include "http_chunk.h" +#include "http_header.h" +#include "response.h" +#include "sock_addr.h" +#include "stat_cache.h" + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include <time.h> + +#include "sys-strings.h" +#include "sys-socket.h" +#include <unistd.h> + + +int http_response_buffer_append_authority(server *srv, connection *con, buffer *o) { + if (!buffer_string_is_empty(con->uri.authority)) { + buffer_append_string_buffer(o, con->uri.authority); + } else { + /* get the name of the currently connected socket */ + sock_addr our_addr; + socklen_t our_addr_len; + + our_addr.plain.sa_family = 0; + our_addr_len = sizeof(our_addr); + + if (-1 == getsockname(con->fd, (struct sockaddr *)&our_addr, &our_addr_len) + || our_addr_len > (socklen_t)sizeof(our_addr)) { + con->http_status = 500; + log_error_write(srv, __FILE__, __LINE__, "ss", + "can't get sockname", strerror(errno)); + return -1; + } + + if (our_addr.plain.sa_family == AF_INET + && our_addr.ipv4.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) { + static char lhost[32]; + static size_t lhost_len = 0; + if (0 != lhost_len) { + buffer_append_string_len(o, lhost, lhost_len); + } + else { + size_t olen = buffer_string_length(o); + if (0 == sock_addr_nameinfo_append_buffer(srv, o, &our_addr)) { + lhost_len = buffer_string_length(o) - olen; + if (lhost_len < sizeof(lhost)) { + memcpy(lhost, o->ptr+olen, lhost_len+1); /*(+1 for '\0')*/ + } + else { + lhost_len = 0; + } + } + else { + lhost_len = sizeof("localhost")-1; + memcpy(lhost, "localhost", lhost_len+1); /*(+1 for '\0')*/ + buffer_append_string_len(o, lhost, lhost_len); + } + } + } else if (!buffer_string_is_empty(con->server_name)) { + buffer_append_string_buffer(o, con->server_name); + } else + /* Lookup name: secondly try to get hostname for bind address */ + if (0 != sock_addr_nameinfo_append_buffer(srv, o, &our_addr)) { + con->http_status = 500; + return -1; + } + + { + unsigned short listen_port = sock_addr_get_port(&our_addr); + unsigned short default_port = 80; + if (buffer_is_equal_string(con->uri.scheme, CONST_STR_LEN("https"))) { + default_port = 443; + } + if (0 == listen_port) listen_port = srv->srvconf.port; + if (default_port != listen_port) { + buffer_append_string_len(o, CONST_STR_LEN(":")); + buffer_append_int(o, listen_port); + } + } + } + return 0; +} + +int http_response_redirect_to_directory(server *srv, connection *con) { + buffer *o = srv->tmp_buf; + buffer_copy_buffer(o, con->uri.scheme); + buffer_append_string_len(o, CONST_STR_LEN("://")); + if (0 != http_response_buffer_append_authority(srv, con, o)) { + return -1; + } + buffer_append_string_encoded(o, CONST_BUF_LEN(con->uri.path), ENCODING_REL_URI); + buffer_append_string_len(o, CONST_STR_LEN("/")); + if (!buffer_string_is_empty(con->uri.query)) { + buffer_append_string_len(o, CONST_STR_LEN("?")); + buffer_append_string_buffer(o, con->uri.query); + } + + http_header_response_set(con, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location"), CONST_BUF_LEN(o)); + + con->http_status = 301; + con->file_finished = 1; + return 0; +} + +buffer * strftime_cache_get(server *srv, time_t last_mod) { + static int i; + struct tm *tm; + + for (int j = 0; j < FILE_CACHE_MAX; ++j) { + if (srv->mtime_cache[j].mtime == last_mod) + return srv->mtime_cache[j].str; /* found cache-entry */ + } + + if (++i == FILE_CACHE_MAX) { + i = 0; + } + + srv->mtime_cache[i].mtime = last_mod; + tm = gmtime(&(srv->mtime_cache[i].mtime)); + buffer_clear(srv->mtime_cache[i].str); + buffer_append_strftime(srv->mtime_cache[i].str, "%a, %d %b %Y %H:%M:%S GMT", tm); + + return srv->mtime_cache[i].str; +} + + +int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) { + buffer *vb; + int head_or_get = + ( HTTP_METHOD_GET == con->request.http_method + || HTTP_METHOD_HEAD == con->request.http_method); + UNUSED(srv); + + /* + * 14.26 If-None-Match + * [...] + * If none of the entity tags match, then the server MAY perform the + * requested method as if the If-None-Match header field did not exist, + * but MUST also ignore any If-Modified-Since header field(s) in the + * request. That is, if no entity tags match, then the server MUST NOT + * return a 304 (Not Modified) response. + */ + + if ((vb = http_header_request_get(con, HTTP_HEADER_IF_NONE_MATCH, CONST_STR_LEN("If-None-Match")))) { + /* use strong etag checking for now: weak comparison must not be used + * for ranged requests + */ + if (etag_is_equal(con->physical.etag, vb->ptr, 0)) { + if (head_or_get) { + con->http_status = 304; + return HANDLER_FINISHED; + } else { + con->http_status = 412; + con->mode = DIRECT; + return HANDLER_FINISHED; + } + } + } else if (head_or_get + && (vb = http_header_request_get(con, HTTP_HEADER_IF_MODIFIED_SINCE, CONST_STR_LEN("If-Modified-Since")))) { + /* last-modified handling */ + size_t used_len; + char *semicolon; + + if (NULL == (semicolon = strchr(vb->ptr, ';'))) { + used_len = buffer_string_length(vb); + } else { + used_len = semicolon - vb->ptr; + } + + if (buffer_is_equal_string(mtime, vb->ptr, used_len)) { + if ('\0' == mtime->ptr[used_len]) con->http_status = 304; + return HANDLER_FINISHED; + } else { + char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")]; + time_t t_header, t_file; + struct tm tm; + + /* convert to timestamp */ + if (used_len >= sizeof(buf)) return HANDLER_GO_ON; + + memcpy(buf, vb->ptr, used_len); + buf[used_len] = '\0'; + + if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) { + /** + * parsing failed, let's get out of here + */ + return HANDLER_GO_ON; + } + tm.tm_isdst = 0; + t_header = mktime(&tm); + + strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm); + tm.tm_isdst = 0; + t_file = mktime(&tm); + + if (t_file > t_header) return HANDLER_GO_ON; + + con->http_status = 304; + return HANDLER_FINISHED; + } + } + + return HANDLER_GO_ON; +} + + +void http_response_body_clear (connection *con, int preserve_length) { + con->response.send_chunked = 0; + if (con->response.htags & HTTP_HEADER_TRANSFER_ENCODING) { + http_header_response_unset(con, HTTP_HEADER_TRANSFER_ENCODING, CONST_STR_LEN("Transfer-Encoding")); + } + if (!preserve_length) { /* preserve for HEAD responses and no-content responses (204, 205, 304) */ + con->response.content_length = -1; + if (con->response.htags & HTTP_HEADER_CONTENT_LENGTH) { + http_header_response_unset(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); + } + } + chunkqueue_reset(con->write_queue); +} + + +static int http_response_parse_range(server *srv, connection *con, buffer *path, stat_cache_entry *sce, const char *range) { + int multipart = 0; + int error; + off_t start, end; + const char *s, *minus; + static const char boundary[] = "fkj49sn38dcn3"; + buffer *content_type = http_header_response_get(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type")); + + start = 0; + end = sce->st.st_size - 1; + + con->response.content_length = 0; + + for (s = range, error = 0; + !error && *s && NULL != (minus = strchr(s, '-')); ) { + char *err; + off_t la = 0, le; + *((const char **)&err) = s; /*(quiet clang --analyze)*/ + + if (s != minus) { + la = strtoll(s, &err, 10); + if (err != minus) { + /* should not have multiple range-unit in Range, but + * handle just in case multiple Range headers merged */ + while (*s == ' ' || *s == '\t') ++s; + if (0 != strncmp(s, "bytes=", 6)) return -1; + s += 6; + if (s != minus) { + la = strtoll(s, &err, 10); + if (err != minus) return -1; + } + } + } + + if (s == minus) { + /* -<stop> */ + + le = strtoll(s, &err, 10); + + if (le == 0) { + /* RFC 2616 - 14.35.1 */ + + con->http_status = 416; + error = 1; + } else if (*err == '\0') { + /* end */ + s = err; + + end = sce->st.st_size - 1; + start = sce->st.st_size + le; + } else if (*err == ',') { + multipart = 1; + s = err + 1; + + end = sce->st.st_size - 1; + start = sce->st.st_size + le; + } else { + error = 1; + } + + } else if (*(minus+1) == '\0' || *(minus+1) == ',') { + /* <start>- */ + + /* ok */ + + if (*(err + 1) == '\0') { + s = err + 1; + + end = sce->st.st_size - 1; + start = la; + + } else if (*(err + 1) == ',') { + multipart = 1; + s = err + 2; + + end = sce->st.st_size - 1; + start = la; + } else { + error = 1; + } + } else { + /* <start>-<stop> */ + + le = strtoll(minus+1, &err, 10); + + /* RFC 2616 - 14.35.1 */ + if (la > le) { + error = 1; + } + + if (*err == '\0') { + /* ok, end*/ + s = err; + + end = le; + start = la; + } else if (*err == ',') { + multipart = 1; + s = err + 1; + + end = le; + start = la; + } else { + /* error */ + + error = 1; + } + } + + if (!error) { + if (start < 0) start = 0; + + /* RFC 2616 - 14.35.1 */ + if (end > sce->st.st_size - 1) end = sce->st.st_size - 1; + + if (start > sce->st.st_size - 1) { + error = 1; + + con->http_status = 416; + } + } + + if (!error) { + if (multipart) { + /* write boundary-header */ + buffer *b = srv->tmp_buf; + buffer_copy_string_len(b, CONST_STR_LEN("\r\n--")); + buffer_append_string_len(b, boundary, sizeof(boundary)-1); + + /* write Content-Range */ + buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes ")); + buffer_append_int(b, start); + buffer_append_string_len(b, CONST_STR_LEN("-")); + buffer_append_int(b, end); + buffer_append_string_len(b, CONST_STR_LEN("/")); + buffer_append_int(b, sce->st.st_size); + + if (content_type) { + buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Type: ")); + buffer_append_string_buffer(b, content_type); + } + + /* write END-OF-HEADER */ + buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); + + con->response.content_length += buffer_string_length(b); + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(b)); + } + + chunkqueue_append_file(con->write_queue, path, start, end - start + 1); + con->response.content_length += end - start + 1; + } + } + + /* something went wrong */ + if (error) return -1; + + if (multipart) { + /* add boundary end */ + buffer *b = srv->tmp_buf; + buffer_copy_string_len(b, "\r\n--", 4); + buffer_append_string_len(b, boundary, sizeof(boundary)-1); + buffer_append_string_len(b, "--\r\n", 4); + + con->response.content_length += buffer_string_length(b); + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(b)); + + /* set header-fields */ + + buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("multipart/byteranges; boundary=")); + buffer_append_string_len(srv->tmp_buf, boundary, sizeof(boundary)-1); + + /* overwrite content-type */ + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(srv->tmp_buf)); + } else { + /* add Content-Range-header */ + + buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("bytes ")); + buffer_append_int(srv->tmp_buf, start); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("-")); + buffer_append_int(srv->tmp_buf, end); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/")); + buffer_append_int(srv->tmp_buf, sce->st.st_size); + + http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(srv->tmp_buf)); + } + + /* ok, the file is set-up */ + return 0; +} + + +void http_response_send_file (server *srv, connection *con, buffer *path) { + stat_cache_entry *sce = NULL; + buffer *mtime = NULL; + buffer *vb; + int allow_caching = (0 == con->http_status || 200 == con->http_status); + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, path, &sce)) { + con->http_status = (errno == ENOENT) ? 404 : 403; + + log_error_write(srv, __FILE__, __LINE__, "sbsb", + "not a regular file:", con->uri.path, + "->", path); + + return; + } + + /* we only handline regular files */ +#ifdef HAVE_LSTAT + if ((sce->is_symlink == 1) && !con->conf.follow_symlink) { + con->http_status = 403; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", path); + } + + return; + } +#endif + if (!S_ISREG(sce->st.st_mode)) { + con->http_status = 403; + + if (con->conf.log_file_not_found) { + log_error_write(srv, __FILE__, __LINE__, "sbsb", + "not a regular file:", con->uri.path, + "->", sce->name); + } + + return; + } + + /* mod_compress might set several data directly, don't overwrite them */ + + /* set response content-type, if not set already */ + + if (NULL == http_header_response_get(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"))) { + stat_cache_content_type_get(srv, con, path, sce); + if (buffer_string_is_empty(sce->content_type)) { + /* we are setting application/octet-stream, but also announce that + * this header field might change in the seconds few requests + * + * This should fix the aggressive caching of FF and the script download + * seen by the first installations + */ + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream")); + + allow_caching = 0; + } else { + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + } + } + + if (con->conf.range_requests) { + http_header_response_append(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes")); + } + + if (allow_caching) { + if (con->etag_flags != 0 && !buffer_string_is_empty(stat_cache_etag_get(sce, con->etag_flags))) { + if (NULL == http_header_response_get(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"))) { + /* generate e-tag */ + etag_mutate(con->physical.etag, sce->etag); + + http_header_response_set(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + } + } + + /* prepare header */ + if (NULL == (mtime = http_header_response_get(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified")))) { + mtime = strftime_cache_get(srv, sce->st.st_mtime); + http_header_response_set(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); + } + + if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { + return; + } + } + + if (con->conf.range_requests + && (200 == con->http_status || 0 == con->http_status) + && NULL != (vb = http_header_request_get(con, HTTP_HEADER_RANGE, CONST_STR_LEN("Range"))) + && NULL == http_header_response_get(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"))) { + buffer *range = vb; + int do_range_request = 1; + /* check if we have a conditional GET */ + + if (NULL != (vb = http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("If-Range")))) { + /* if the value is the same as our ETag, we do a Range-request, + * otherwise a full 200 */ + + if (vb->ptr[0] == '"') { + /** + * client wants a ETag + */ + if (!con->physical.etag) { + do_range_request = 0; + } else if (!buffer_is_equal(vb, con->physical.etag)) { + do_range_request = 0; + } + } else if (!mtime) { + /** + * we don't have a Last-Modified and can match the If-Range: + * + * sending all + */ + do_range_request = 0; + } else if (!buffer_is_equal(vb, mtime)) { + do_range_request = 0; + } + } + + if (do_range_request + && !buffer_string_is_empty(range) + && 0 == strncmp(range->ptr, "bytes=", 6)) { + /* support only "bytes" byte-unit */ + /* content prepared, I'm done */ + con->file_finished = 1; + + if (0 == http_response_parse_range(srv, con, path, sce, range->ptr+6)) { + con->http_status = 206; + } + return; + } + } + + /* if we are still here, prepare body */ + + /* we add it here for all requests + * the HEAD request will drop it afterwards again + */ + if (0 == sce->st.st_size || 0 == http_chunk_append_file(srv, con, path)) { + con->http_status = 200; + con->file_finished = 1; + } else { + con->http_status = 403; + } +} + + +static void http_response_xsendfile (server *srv, connection *con, buffer *path, const array *xdocroot) { + const int status = con->http_status; + int valid = 1; + + /* reset Content-Length, if set by backend + * Content-Length might later be set to size of X-Sendfile static file, + * determined by open(), fstat() to reduces race conditions if the file + * is modified between stat() (stat_cache_get_entry()) and open(). */ + if (con->response.htags & HTTP_HEADER_CONTENT_LENGTH) { + http_header_response_unset(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); + con->response.content_length = -1; + } + + buffer_urldecode_path(path); + if (!buffer_is_valid_UTF8(path)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "X-Sendfile invalid UTF-8 after url-decode:", path); + if (con->http_status < 400) { + con->http_status = 502; + con->mode = DIRECT; + } + return; + } + buffer_path_simplify(path, path); + if (con->conf.force_lowercase_filenames) { + buffer_to_lower(path); + } + + /* check that path is under xdocroot(s) + * - xdocroot should have trailing slash appended at config time + * - con->conf.force_lowercase_filenames is not a server-wide setting, + * and so can not be definitively applied to xdocroot at config time*/ + if (xdocroot->used) { + size_t i, xlen = buffer_string_length(path); + for (i = 0; i < xdocroot->used; ++i) { + data_string *ds = (data_string *)xdocroot->data[i]; + size_t dlen = buffer_string_length(ds->value); + if (dlen <= xlen + && (!con->conf.force_lowercase_filenames + ? 0 == memcmp(path->ptr, ds->value->ptr, dlen) + : 0 == strncasecmp(path->ptr, ds->value->ptr, dlen))) { + break; + } + } + if (i == xdocroot->used) { + log_error_write(srv, __FILE__, __LINE__, "SBs", + "X-Sendfile (", path, + ") not under configured x-sendfile-docroot(s)"); + con->http_status = 403; + valid = 0; + } + } + + if (valid) http_response_send_file(srv, con, path); + + if (con->http_status >= 400 && status < 300) { + con->mode = DIRECT; + } else if (0 != status && 200 != status) { + con->http_status = status; + } +} + + +static void http_response_xsendfile2(server *srv, connection *con, const buffer *value, const array *xdocroot) { + const char *pos = value->ptr; + buffer *b = srv->tmp_buf; + const int status = con->http_status; + + /* reset Content-Length, if set by backend */ + if (con->response.htags & HTTP_HEADER_CONTENT_LENGTH) { + http_header_response_unset(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); + con->response.content_length = -1; + } + + while (*pos) { + const char *filename, *range; + stat_cache_entry *sce; + off_t begin_range, end_range, range_len; + + while (' ' == *pos) pos++; + if (!*pos) break; + + filename = pos; + if (NULL == (range = strchr(pos, ' '))) { + /* missing range */ + log_error_write(srv, __FILE__, __LINE__, "ss", + "Couldn't find range after filename:", filename); + con->http_status = 502; + break; + } + buffer_copy_string_len(b, filename, range - filename); + + /* find end of range */ + for (pos = ++range; *pos && *pos != ' ' && *pos != ','; pos++) ; + + buffer_urldecode_path(b); + if (!buffer_is_valid_UTF8(b)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "X-Sendfile2 invalid UTF-8 after url-decode:", b); + con->http_status = 502; + break; + } + buffer_path_simplify(b, b); + if (con->conf.force_lowercase_filenames) { + buffer_to_lower(b); + } + if (xdocroot->used) { + size_t i, xlen = buffer_string_length(b); + for (i = 0; i < xdocroot->used; ++i) { + data_string *ds = (data_string *)xdocroot->data[i]; + size_t dlen = buffer_string_length(ds->value); + if (dlen <= xlen + && (!con->conf.force_lowercase_filenames + ? 0 == memcmp(b->ptr, ds->value->ptr, dlen) + : 0 == strncasecmp(b->ptr, ds->value->ptr, dlen))) { + break; + } + } + if (i == xdocroot->used) { + log_error_write(srv, __FILE__, __LINE__, "SBs", + "X-Sendfile2 (", b, + ") not under configured x-sendfile-docroot(s)"); + con->http_status = 403; + break; + } + } + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, b, &sce)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "send-file error: " + "couldn't get stat_cache entry for X-Sendfile2:", + b); + con->http_status = 404; + break; + } else if (!S_ISREG(sce->st.st_mode)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "send-file error: wrong filetype for X-Sendfile2:", + b); + con->http_status = 502; + break; + } + /* found the file */ + + /* parse range */ + end_range = sce->st.st_size - 1; + { + char *rpos = NULL; + errno = 0; + begin_range = strtoll(range, &rpos, 10); + if (errno != 0 || begin_range < 0 || rpos == range) + goto range_failed; + if ('-' != *rpos++) goto range_failed; + if (rpos != pos) { + range = rpos; + end_range = strtoll(range, &rpos, 10); + if (errno != 0 || end_range < 0 || rpos == range) + goto range_failed; + } + if (rpos != pos) goto range_failed; + + goto range_success; + +range_failed: + log_error_write(srv, __FILE__, __LINE__, "ss", + "Couldn't decode range after filename:", filename); + con->http_status = 502; + break; + +range_success: ; + } + + /* no parameters accepted */ + + while (*pos == ' ') pos++; + if (*pos != '\0' && *pos != ',') { + con->http_status = 502; + break; + } + + range_len = end_range - begin_range + 1; + if (range_len < 0) { + con->http_status = 502; + break; + } + if (range_len != 0) { + if (0 != http_chunk_append_file_range(srv, con, b, + begin_range, range_len)) { + con->http_status = 502; + break; + } + } + + if (*pos == ',') pos++; + } + + if (con->http_status >= 400 && status < 300) { + con->mode = DIRECT; + } else if (0 != status && 200 != status) { + con->http_status = status; + } +} + + +void http_response_backend_error (server *srv, connection *con) { + UNUSED(srv); + if (con->file_started) { + /*(response might have been already started, kill the connection)*/ + /*(mode == DIRECT to avoid later call to http_response_backend_done())*/ + con->mode = DIRECT; /*(avoid sending final chunked block)*/ + con->keep_alive = 0; /*(no keep-alive; final chunked block not sent)*/ + con->file_finished = 1; + } /*(else error status set later by http_response_backend_done())*/ +} + +void http_response_backend_done (server *srv, connection *con) { + /* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END, + * i.e. not called from handle_connection_close or connection_reset + * hooks, except maybe from errdoc handler, which later resets state)*/ + switch (con->state) { + case CON_STATE_HANDLE_REQUEST: + case CON_STATE_READ_POST: + if (!con->file_started) { + /* Send an error if we haven't sent any data yet */ + con->http_status = 500; + con->mode = DIRECT; + break; + } /* else fall through */ + case CON_STATE_WRITE: + if (!con->file_finished) { + http_chunk_close(srv, con); + con->file_finished = 1; + } + default: + break; + } +} + + +void http_response_upgrade_read_body_unknown(server *srv, connection *con) { + /* act as transparent proxy */ + UNUSED(srv); + if (!(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)) + con->conf.stream_request_body |= + (FDEVENT_STREAM_REQUEST_BUFMIN | FDEVENT_STREAM_REQUEST); + if (!(con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE)) + con->conf.stream_response_body |= + (FDEVENT_STREAM_RESPONSE_BUFMIN | FDEVENT_STREAM_RESPONSE); + con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; + con->request.content_length = -2; + con->keep_alive = 0; +} + + +static handler_t http_response_process_local_redir(server *srv, connection *con, size_t blen) { + /* [RFC3875] The Common Gateway Interface (CGI) Version 1.1 + * [RFC3875] 6.2.2 Local Redirect Response + * + * The CGI script can return a URI path and query-string + * ('local-pathquery') for a local resource in a Location header field. + * This indicates to the server that it should reprocess the request + * using the path specified. + * + * local-redir-response = local-Location NL + * + * The script MUST NOT return any other header fields or a message-body, + * and the server MUST generate the response that it would have produced + * in response to a request containing the URL + * + * scheme "://" server-name ":" server-port local-pathquery + * + * (Might not have begun to receive body yet, but do skip local-redir + * if we already have started receiving a response body (blen > 0)) + * (Also, while not required by the RFC, do not send local-redir back + * to same URL, since CGI should have handled it internally if it + * really wanted to do that internally) + */ + + /* con->http_status >= 300 && con->http_status < 400) */ + size_t ulen = buffer_string_length(con->uri.path); + buffer *vb = http_header_response_get(con, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location")); + if (NULL != vb + && vb->ptr[0] == '/' + && (0 != strncmp(vb->ptr, con->uri.path->ptr, ulen) + || ( vb->ptr[ulen] != '\0' + && vb->ptr[ulen] != '/' + && vb->ptr[ulen] != '?')) + && 0 == blen + && !(con->response.htags & HTTP_HEADER_STATUS) /*no "Status" or NPH response*/ + && 1 == con->response.headers->used) { + if (++con->loops_per_request > 5) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "too many internal loops while processing request:", + con->request.orig_uri); + con->http_status = 500; /* Internal Server Error */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + + buffer_copy_buffer(con->request.uri, vb); + + if (con->request.content_length) { + if (con->request.content_length + != con->request_content_queue->bytes_in) { + con->keep_alive = 0; + } + con->request.content_length = 0; + chunkqueue_reset(con->request_content_queue); + } + + if (con->http_status != 307 && con->http_status != 308) { + /* Note: request body (if any) sent to initial dynamic handler + * and is not available to the internal redirect */ + con->request.http_method = HTTP_METHOD_GET; + } + + /*(caller must reset request as follows)*/ + /*connection_response_reset(srv, con);*/ /*(sets con->http_status = 0)*/ + /*plugins_call_connection_reset(srv, con);*/ + + return HANDLER_COMEBACK; + } + + return HANDLER_GO_ON; +} + + +static int http_response_process_headers(server *srv, connection *con, http_response_opts *opts, buffer *hdrs) { + char *ns; + const char *s; + int line = 0; + int status_is_set = 0; + + for (s = hdrs->ptr; NULL != (ns = strchr(s, '\n')); s = ns + 1, ++line) { + const char *key, *value; + int key_len; + enum http_header_e id; + + /* strip the \n */ + ns[0] = '\0'; + if (ns > s && ns[-1] == '\r') ns[-1] = '\0'; + + if (0 == line && 0 == strncmp(s, "HTTP/1.", 7)) { + /* non-parsed headers ... we parse them anyway */ + if ((s[7] == '1' || s[7] == '0') && s[8] == ' ') { + /* after the space should be a status code for us */ + int status = strtol(s+9, NULL, 10); + if (status >= 100 && status < 1000) { + status_is_set = 1; + con->response.htags |= HTTP_HEADER_STATUS; + con->http_status = status; + } /* else we expected 3 digits and didn't get them */ + } + + if (0 == con->http_status) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "invalid HTTP status line:", s); + con->http_status = 502; /* Bad Gateway */ + con->mode = DIRECT; + return -1; + } + + continue; + } + + /* parse the headers */ + key = s; + if (NULL == (value = strchr(s, ':'))) { + /* we expect: "<key>: <value>\r\n" */ + continue; + } + + key_len = value - key; + do { ++value; } while (*value == ' ' || *value == '\t'); /* skip LWS */ + id = http_header_hkey_get(key, key_len); + + if (opts->authorizer) { + if (0 == con->http_status || 200 == con->http_status) { + if (id == HTTP_HEADER_STATUS) { + int status = strtol(value, NULL, 10); + if (status >= 100 && status < 1000) { + con->http_status = status; + } else { + con->http_status = 502; /* Bad Gateway */ + break; + } + } else if (id == HTTP_HEADER_OTHER && key_len > 9 + && 0==strncasecmp(key, CONST_STR_LEN("Variable-"))) { + http_header_env_append(con, key + 9, key_len - 9, value, strlen(value)); + } + continue; + } + } + + switch (id) { + case HTTP_HEADER_STATUS: + { + int status; + if (opts->backend == BACKEND_PROXY) break; /*(pass w/o parse)*/ + status = strtol(value, NULL, 10); + if (status >= 100 && status < 1000) { + con->http_status = status; + status_is_set = 1; + } else { + con->http_status = 502; + con->mode = DIRECT; + } + continue; /* do not send Status to client */ + } + break; + case HTTP_HEADER_UPGRADE: + /*(technically, should also verify Connection: upgrade)*/ + /*(flag only for mod_proxy and mod_cgi (for now))*/ + if (opts->backend != BACKEND_PROXY + && opts->backend != BACKEND_CGI) { + id = HTTP_HEADER_OTHER; + } + break; + case HTTP_HEADER_CONNECTION: + if (opts->backend == BACKEND_PROXY) continue; + con->response.keep_alive = + (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0; + break; + case HTTP_HEADER_CONTENT_LENGTH: + con->response.content_length = strtoul(value, NULL, 10); + break; + case HTTP_HEADER_TRANSFER_ENCODING: + if (opts->backend == BACKEND_PROXY) { + log_error_write(srv, __FILE__, __LINE__, "s", + "proxy backend sent invalid response header " + "(Transfer-Encoding) to HTTP/1.0 request"); + con->http_status = 502; /* Bad Gateway */ + con->mode = DIRECT; + return -1; + } + break; + default: + break; + } + + http_header_response_insert(con, id, key, key_len, value, strlen(value)); + } + + /* CGI/1.1 rev 03 - 7.2.1.2 */ + /* (proxy requires Status-Line, so never true for proxy)*/ + if (!status_is_set && (con->response.htags & HTTP_HEADER_LOCATION)) { + con->http_status = 302; + } + + return 0; +} + + +handler_t http_response_parse_headers(server *srv, connection *con, http_response_opts *opts, buffer *b) { + /** + * possible formats of response headers: + * + * proxy or NPH (non-parsed headers): + * + * HTTP/1.0 200 Ok\n + * Header: Value\n + * \n + * + * CGI: + * + * Header: Value\n + * Status: 200\n + * \n + * + * and different mixes of \n and \r\n combinations + * + * Some users also forget about CGI and just send a response + * and hope we handle it. No headers, no header-content separator + */ + + int is_nph = (0 == strncmp(b->ptr, "HTTP/1.", 7)); /*nph (non-parsed hdrs)*/ + int is_header_end = 0; + size_t last_eol = 0; + size_t i = 0, header_len = buffer_string_length(b); + const char *bstart; + size_t blen; + + if (b->ptr[0] == '\n' || (b->ptr[0] == '\r' && b->ptr[1] == '\n')) { + /* no HTTP headers */ + i = (b->ptr[0] == '\n') ? 0 : 1; + is_header_end = 1; + } else if (is_nph || b->ptr[(i = strcspn(b->ptr, ":\n"))] == ':') { + /* HTTP headers */ + ++i; + for (char *c; NULL != (c = strchr(b->ptr+i, '\n')); ++i) { + i = (uintptr_t)(c - b->ptr); + /** + * check if we saw a \n(\r)?\n sequence + */ + if (last_eol > 0 && + ((i - last_eol == 1) || + (i - last_eol == 2 && b->ptr[i - 1] == '\r'))) { + is_header_end = 1; + break; + } + + last_eol = i; + } + } else if (i == header_len) { /* (no newline yet; partial header line?) */ + } else if (opts->backend == BACKEND_CGI) { + /* no HTTP headers, but a body (special-case for CGI compat) */ + /* no colon found; does not appear to be HTTP headers */ + if (0 != http_chunk_append_buffer(srv, con, b)) { + return HANDLER_ERROR; + } + con->http_status = 200; /* OK */ + con->file_started = 1; + return HANDLER_GO_ON; + } else { + /* invalid response headers */ + con->http_status = 502; /* Bad Gateway */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + + if (!is_header_end) { + /*(reuse MAX_HTTP_REQUEST_HEADER as max size + * for response headers from backends)*/ + if (header_len > MAX_HTTP_REQUEST_HEADER) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "response headers too large for", con->uri.path); + con->http_status = 502; /* Bad Gateway */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + return HANDLER_GO_ON; + } + + /* the body starts after the EOL */ + bstart = b->ptr + (i + 1); + blen = header_len - (i + 1); + + /* strip the last \r?\n */ + if (i > 0 && (b->ptr[i - 1] == '\r')) { + i--; + } + + buffer_string_set_length(b, i); + + if (opts->backend == BACKEND_PROXY && !is_nph) { + /* invalid response Status-Line from HTTP proxy */ + con->http_status = 502; /* Bad Gateway */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + + if (0 != http_response_process_headers(srv, con, opts, b)) { + return HANDLER_ERROR; + } + + con->file_started = 1; + + if (opts->authorizer + && (con->http_status == 0 || con->http_status == 200)) { + return HANDLER_GO_ON; + } + + if (con->mode == DIRECT) { + return HANDLER_FINISHED; + } + + if (opts->local_redir && con->http_status >= 300 && con->http_status < 400){ + /*(con->response.htags & HTTP_HEADER_LOCATION)*/ + handler_t rc = http_response_process_local_redir(srv, con, blen); + if (con->mode == DIRECT) con->file_started = 0; + if (rc != HANDLER_GO_ON) return rc; + } + + if (opts->xsendfile_allow) { + buffer *vb; + /* X-Sendfile2 is deprecated; historical for fastcgi */ + if (opts->backend == BACKEND_FASTCGI + && NULL != (vb = http_header_response_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("X-Sendfile2")))) { + http_response_xsendfile2(srv, con, vb, opts->xsendfile_docroot); + /* http_header_response_unset() shortcut for HTTP_HEADER_OTHER */ + buffer_clear(vb); /*(do not send to client)*/ + if (con->mode == DIRECT) con->file_started = 0; + return HANDLER_FINISHED; + } else if (NULL != (vb = http_header_response_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("X-Sendfile"))) + || (opts->backend == BACKEND_FASTCGI /* X-LIGHTTPD-send-file is deprecated; historical for fastcgi */ + && NULL != (vb = http_header_response_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("X-LIGHTTPD-send-file"))))) { + http_response_xsendfile(srv, con, vb, opts->xsendfile_docroot); + /* http_header_response_unset() shortcut for HTTP_HEADER_OTHER */ + buffer_clear(vb); /*(do not send to client)*/ + if (con->mode == DIRECT) con->file_started = 0; + return HANDLER_FINISHED; + } + } + + if (blen > 0) { + if (0 != http_chunk_append_mem(srv, con, bstart, blen)) { + return HANDLER_ERROR; + } + } + + /* (callback for response headers complete) */ + return (opts->headers) ? opts->headers(srv, con, opts) : HANDLER_GO_ON; +} + + +handler_t http_response_read(server *srv, connection *con, http_response_opts *opts, buffer *b, int fd, int *fde_ndx) { + while (1) { + ssize_t n; + size_t avail = buffer_string_space(b); + unsigned int toread = 0; + + if (0 == fdevent_ioctl_fionread(fd, opts->fdfmt, (int *)&toread)) { + if (avail < toread) { + size_t blen = buffer_string_length(b); + if (toread + blen < 4096) + toread = 4095 - blen; + else if (toread > MAX_READ_LIMIT) + toread = MAX_READ_LIMIT; + } + else if (0 == toread) { + #if 0 + return (fdevent_event_get_interest(srv->ev, fd) & FDEVENT_IN) + ? HANDLER_FINISHED /* read finished */ + : HANDLER_GO_ON; /* optimistic read; data not ready */ + #else + if (!(fdevent_event_get_interest(srv->ev, fd) & FDEVENT_IN)) { + if (!(con->conf.stream_response_body + & FDEVENT_STREAM_RESPONSE_POLLRDHUP)) + return HANDLER_GO_ON;/*optimistic read; data not ready*/ + } + if (0 == avail) /* let read() below indicate if EOF or EAGAIN */ + toread = 1024; + #endif + } + } + else if (avail < 1024) { + toread = 4095 - avail; + } + + if (con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) { + off_t cqlen = chunkqueue_length(con->write_queue); + if (cqlen + (off_t)toread > 65536 - 4096) { + if (!con->is_writable) { + /*(defer removal of FDEVENT_IN interest since + * connection_state_machine() might be able to send data + * immediately, unless !con->is_writable, where + * connection_state_machine() might not loop back to call + * mod_proxy_handle_subrequest())*/ + fdevent_event_clr(srv->ev, fde_ndx, fd, FDEVENT_IN); + } + if (cqlen >= 65536-1) return HANDLER_GO_ON; + toread = 65536 - 1 - (unsigned int)cqlen; + /* Note: heuristic is fuzzy in that it limits how much to read + * from backend based on how much is pending to write to client. + * Modules where data from backend is framed (e.g. FastCGI) may + * want to limit how much is buffered from backend while waiting + * for a complete data frame or data packet from backend. */ + } + } + + if (avail < toread) { + /*(add avail+toread to reduce allocations when ioctl EOPNOTSUPP)*/ + avail = avail ? avail - 1 + toread : toread; + buffer_string_prepare_append(b, avail); + } + + n = read(fd, b->ptr+buffer_string_length(b), avail); + + if (n < 0) { + switch (errno) { + case EAGAIN: + #ifdef EWOULDBLOCK + #if EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: + #endif + #endif + case EINTR: + return HANDLER_GO_ON; + default: + log_error_write(srv, __FILE__, __LINE__, "ssdd", + "read():", strerror(errno), con->fd, fd); + return HANDLER_ERROR; + } + } + + buffer_commit(b, (size_t)n); + + if (NULL != opts->parse) { + handler_t rc = opts->parse(srv, con, opts, b, (size_t)n); + if (rc != HANDLER_GO_ON) return rc; + } else if (0 == n) { + /* note: no further data is sent to backend after read EOF on socket + * (not checking for half-closed TCP socket) + * (backend should read all data desired prior to closing socket, + * though might send app-level close data frame, if applicable) */ + return HANDLER_FINISHED; /* read finished */ + } else if (0 == con->file_started) { + /* split header from body */ + handler_t rc = http_response_parse_headers(srv, con, opts, b); + if (rc != HANDLER_GO_ON) return rc; + /* accumulate response in b until headers completed (or error) */ + if (con->file_started) buffer_clear(b); + } else { + if (0 != http_chunk_append_buffer(srv, con, b)) { + /* error writing to tempfile; + * truncate response or send 500 if nothing sent yet */ + return HANDLER_ERROR; + } + buffer_clear(b); + } + + if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) + && chunkqueue_length(con->write_queue) > 65536 - 4096) { + if (!con->is_writable) { + /*(defer removal of FDEVENT_IN interest since + * connection_state_machine() might be able to send + * data immediately, unless !con->is_writable, where + * connection_state_machine() might not loop back to + * call the subrequest handler)*/ + fdevent_event_clr(srv->ev, fde_ndx, fd, FDEVENT_IN); + } + break; + } + + if ((size_t)n < avail) + break; /* emptied kernel read buffer or partial read */ + } + + return HANDLER_GO_ON; +} + + +int http_cgi_headers (server *srv, connection *con, http_cgi_opts *opts, http_cgi_header_append_cb cb, void *vdata) { + + /* CGI-SPEC 6.1.2, FastCGI spec 6.3 and SCGI spec */ + + int rc = 0; + server_socket *srv_sock = con->srv_socket; + const char *s; + size_t n; + char buf[LI_ITOSTRING_LENGTH]; + sock_addr *addr; + sock_addr addrbuf; + char b2[INET6_ADDRSTRLEN + 1]; + + /* (CONTENT_LENGTH must be first for SCGI) */ + if (!opts->authorizer) { + li_itostrn(buf, sizeof(buf), con->request.content_length); + rc |= cb(vdata, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); + } + + if (!buffer_string_is_empty(con->uri.query)) { + rc |= cb(vdata, CONST_STR_LEN("QUERY_STRING"), + CONST_BUF_LEN(con->uri.query)); + } else { + rc |= cb(vdata, CONST_STR_LEN("QUERY_STRING"), + CONST_STR_LEN("")); + } + if (!buffer_string_is_empty(opts->strip_request_uri)) { + /** + * /app1/index/list + * + * stripping /app1 or /app1/ should lead to + * + * /index/list + * + */ + size_t len = buffer_string_length(opts->strip_request_uri); + if ('/' == opts->strip_request_uri->ptr[len-1]) { + --len; + } + + if (buffer_string_length(con->request.orig_uri) >= len + && 0 == memcmp(con->request.orig_uri->ptr, + opts->strip_request_uri->ptr, len) + && con->request.orig_uri->ptr[len] == '/') { + rc |= cb(vdata, CONST_STR_LEN("REQUEST_URI"), + con->request.orig_uri->ptr+len, + buffer_string_length(con->request.orig_uri) - len); + } else { + rc |= cb(vdata, CONST_STR_LEN("REQUEST_URI"), + CONST_BUF_LEN(con->request.orig_uri)); + } + } else { + rc |= cb(vdata, CONST_STR_LEN("REQUEST_URI"), + CONST_BUF_LEN(con->request.orig_uri)); + } + if (!buffer_is_equal(con->request.uri, con->request.orig_uri)) { + rc |= cb(vdata, CONST_STR_LEN("REDIRECT_URI"), + CONST_BUF_LEN(con->request.uri)); + } + /* set REDIRECT_STATUS for php compiled with --force-redirect + * (if REDIRECT_STATUS has not already been set by error handler) */ + if (0 == con->error_handler_saved_status) { + rc |= cb(vdata, CONST_STR_LEN("REDIRECT_STATUS"), + CONST_STR_LEN("200")); + } + + /* + * SCRIPT_NAME, PATH_INFO and PATH_TRANSLATED according to + * http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html + * (6.1.14, 6.1.6, 6.1.7) + */ + if (!opts->authorizer) { + rc |= cb(vdata, CONST_STR_LEN("SCRIPT_NAME"), + CONST_BUF_LEN(con->uri.path)); + if (!buffer_string_is_empty(con->request.pathinfo)) { + rc |= cb(vdata, CONST_STR_LEN("PATH_INFO"), + CONST_BUF_LEN(con->request.pathinfo)); + /* PATH_TRANSLATED is only defined if PATH_INFO is set */ + if (!buffer_string_is_empty(opts->docroot)) { + buffer_copy_buffer(srv->tmp_buf, opts->docroot); + } else { + buffer_copy_buffer(srv->tmp_buf, con->physical.basedir); + } + buffer_append_string_buffer(srv->tmp_buf, con->request.pathinfo); + rc |= cb(vdata, CONST_STR_LEN("PATH_TRANSLATED"), + CONST_BUF_LEN(srv->tmp_buf)); + } + } + + /* + * SCRIPT_FILENAME and DOCUMENT_ROOT for php + * The PHP manual http://www.php.net/manual/en/reserved.variables.php + * treatment of PATH_TRANSLATED is different from the one of CGI specs. + * (see php.ini cgi.fix_pathinfo = 1 config parameter) + */ + + if (!buffer_string_is_empty(opts->docroot)) { + /* alternate docroot, e.g. for remote FastCGI or SCGI server */ + buffer_copy_buffer(srv->tmp_buf, opts->docroot); + buffer_append_string_buffer(srv->tmp_buf, con->uri.path); + rc |= cb(vdata, CONST_STR_LEN("SCRIPT_FILENAME"), + CONST_BUF_LEN(srv->tmp_buf)); + rc |= cb(vdata, CONST_STR_LEN("DOCUMENT_ROOT"), + CONST_BUF_LEN(opts->docroot)); + } else { + if (opts->break_scriptfilename_for_php) { + /* php.ini config cgi.fix_pathinfo = 1 need a broken SCRIPT_FILENAME + * to find out what PATH_INFO is itself + * + * see src/sapi/cgi_main.c, init_request_info() + */ + buffer_copy_buffer(srv->tmp_buf, con->physical.path); + buffer_append_string_buffer(srv->tmp_buf, con->request.pathinfo); + rc |= cb(vdata, CONST_STR_LEN("SCRIPT_FILENAME"), + CONST_BUF_LEN(srv->tmp_buf)); + } else { + rc |= cb(vdata, CONST_STR_LEN("SCRIPT_FILENAME"), + CONST_BUF_LEN(con->physical.path)); + } + rc |= cb(vdata, CONST_STR_LEN("DOCUMENT_ROOT"), + CONST_BUF_LEN(con->physical.basedir)); + } + + s = get_http_method_name(con->request.http_method); + force_assert(s); + rc |= cb(vdata, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); + + s = get_http_version_name(con->request.http_version); + force_assert(s); + rc |= cb(vdata, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); + + rc |= cb(vdata, CONST_STR_LEN("SERVER_SOFTWARE"), + CONST_BUF_LEN(con->conf.server_tag)); + + rc |= cb(vdata, CONST_STR_LEN("GATEWAY_INTERFACE"), + CONST_STR_LEN("CGI/1.1")); + + rc |= cb(vdata, CONST_STR_LEN("REQUEST_SCHEME"), + CONST_BUF_LEN(con->uri.scheme)); + + if (buffer_is_equal_string(con->uri.scheme, CONST_STR_LEN("https"))) { + rc |= cb(vdata, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); + } + + addr = &srv_sock->addr; + li_utostrn(buf, sizeof(buf), sock_addr_get_port(addr)); + rc |= cb(vdata, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)); + + switch (addr->plain.sa_family) { + case AF_INET: + case AF_INET6: + if (sock_addr_is_addr_wildcard(addr)) { + socklen_t addrlen = sizeof(addrbuf); + if (0 == getsockname(con->fd,(struct sockaddr *)&addrbuf,&addrlen)){ + addr = &addrbuf; + } else { + s = ""; + break; + } + } + s = sock_addr_inet_ntop(addr, b2, sizeof(b2)-1); + if (NULL == s) s = ""; + break; + default: + s = ""; + break; + } + force_assert(s); + rc |= cb(vdata, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); + + if (!buffer_string_is_empty(con->server_name)) { + size_t len = buffer_string_length(con->server_name); + + if (con->server_name->ptr[0] == '[') { + const char *colon = strstr(con->server_name->ptr, "]:"); + if (colon) len = (colon + 1) - con->server_name->ptr; + } else { + const char *colon = strchr(con->server_name->ptr, ':'); + if (colon) len = colon - con->server_name->ptr; + } + + rc |= cb(vdata, CONST_STR_LEN("SERVER_NAME"), + con->server_name->ptr, len); + } else { + /* set to be same as SERVER_ADDR (above) */ + rc |= cb(vdata, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); + } + + rc |= cb(vdata, CONST_STR_LEN("REMOTE_ADDR"), + CONST_BUF_LEN(con->dst_addr_buf)); + + li_utostrn(buf, sizeof(buf), sock_addr_get_port(&con->dst_addr)); + rc |= cb(vdata, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)); + + for (n = 0; n < con->request.headers->used; n++) { + data_string *ds = (data_string *)con->request.headers->data[n]; + if (!buffer_string_is_empty(ds->value) && !buffer_is_empty(ds->key)) { + /* Security: Do not emit HTTP_PROXY in environment. + * Some executables use HTTP_PROXY to configure + * outgoing proxy. See also https://httpoxy.org/ */ + if (buffer_is_equal_caseless_string(ds->key, + CONST_STR_LEN("Proxy"))) { + continue; + } + buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, + CONST_BUF_LEN(ds->key), 1); + rc |= cb(vdata, CONST_BUF_LEN(srv->tmp_buf), + CONST_BUF_LEN(ds->value)); + } + } + + srv->request_env(srv, con); + + for (n = 0; n < con->environment->used; n++) { + data_string *ds = (data_string *)con->environment->data[n]; + if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) { + buffer_copy_string_encoded_cgi_varnames(srv->tmp_buf, + CONST_BUF_LEN(ds->key), 0); + rc |= cb(vdata, CONST_BUF_LEN(srv->tmp_buf), + CONST_BUF_LEN(ds->value)); + } + } + + return rc; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/http_auth.c b/data/lighttpd/lighttpd-1.4.53/src/http_auth.c new file mode 100644 index 000000000..484da8ff2 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/http_auth.c @@ -0,0 +1,171 @@ +#include "first.h" + +#include "http_auth.h" +#include "http_header.h" + +#include <stdlib.h> +#include <string.h> + + +static http_auth_scheme_t http_auth_schemes[8]; + +const http_auth_scheme_t * http_auth_scheme_get (const buffer *name) +{ + int i = 0; + while (NULL != http_auth_schemes[i].name + && 0 != strcmp(http_auth_schemes[i].name, name->ptr)) { + ++i; + } + return (NULL != http_auth_schemes[i].name) ? http_auth_schemes+i : NULL; +} + +void http_auth_scheme_set (const http_auth_scheme_t *scheme) +{ + unsigned int i = 0; + while (NULL != http_auth_schemes[i].name) ++i; + /*(must resize http_auth_schemes[] if too many different auth schemes)*/ + force_assert(i<(sizeof(http_auth_schemes)/sizeof(http_auth_scheme_t))-1); + memcpy(http_auth_schemes+i, scheme, sizeof(http_auth_scheme_t)); +} + + +static http_auth_backend_t http_auth_backends[12]; + +const http_auth_backend_t * http_auth_backend_get (const buffer *name) +{ + int i = 0; + while (NULL != http_auth_backends[i].name + && 0 != strcmp(http_auth_backends[i].name, name->ptr)) { + ++i; + } + return (NULL != http_auth_backends[i].name) ? http_auth_backends+i : NULL; +} + +void http_auth_backend_set (const http_auth_backend_t *backend) +{ + unsigned int i = 0; + while (NULL != http_auth_backends[i].name) ++i; + /*(must resize http_auth_backends[] if too many different auth backends)*/ + force_assert(i<(sizeof(http_auth_backends)/sizeof(http_auth_backend_t))-1); + memcpy(http_auth_backends+i, backend, sizeof(http_auth_backend_t)); +} + + +int http_auth_const_time_memeq (const char *a, const size_t alen, const char *b, const size_t blen) +{ + /* constant time memory compare, unless compiler figures it out + * (similar to mod_secdownload.c:const_time_memeq()) */ + /* round to next multiple of 64 to avoid potentially leaking exact + * password length when subject to high precision timing attacks) */ + size_t lim = ((alen >= blen ? alen : blen) + 0x3F) & ~0x3F; + int diff = 0; + for (size_t i = 0, j = 0; lim; --lim) { + diff |= (a[i] ^ b[j]); + i += (i < alen); + j += (j < blen); + } + return (0 == diff); +} + + +void http_auth_dumbdata_reset (void) +{ + memset(http_auth_schemes, 0, sizeof(http_auth_schemes)); + memset(http_auth_backends, 0, sizeof(http_auth_backends)); +} + + +http_auth_require_t * http_auth_require_init (void) +{ + http_auth_require_t *require = calloc(1, sizeof(http_auth_require_t)); + force_assert(NULL != require); + + require->realm = buffer_init(); + require->valid_user = 0; + require->user = array_init(); + require->group = array_init(); + require->host = array_init(); + + return require; +} + +void http_auth_require_free (http_auth_require_t * const require) +{ + buffer_free(require->realm); + array_free(require->user); + array_free(require->group); + array_free(require->host); + free(require); +} + +/* (case-sensitive version of array.c:array_get_index(), + * and common case expects small num of allowed tokens, + * so it is reasonably performant to simply walk the array) */ +static int http_auth_array_contains (const array * const a, const char * const k, const size_t klen) +{ + for (size_t i = 0, used = a->used; i < used; ++i) { + if (buffer_is_equal_string(a->data[i]->key, k, klen)) { + return 1; + } + } + return 0; +} + +int http_auth_match_rules (const http_auth_require_t * const require, const char * const user, const char * const group, const char * const host) +{ + if (NULL != user + && (require->valid_user + || http_auth_array_contains(require->user, user, strlen(user)))) { + return 1; /* match */ + } + + if (NULL != group + && http_auth_array_contains(require->group, group, strlen(group))) { + return 1; /* match */ + } + + if (NULL != host + && http_auth_array_contains(require->host, host, strlen(host))) { + return 1; /* match */ + } + + return 0; /* no match */ +} + +void http_auth_setenv(connection *con, const char *username, size_t ulen, const char *auth_type, size_t alen) { + http_header_env_set(con, CONST_STR_LEN("REMOTE_USER"), username, ulen); + http_header_env_set(con, CONST_STR_LEN("AUTH_TYPE"), auth_type, alen); +} + +int http_auth_md5_hex2bin (const char *md5hex, size_t len, unsigned char md5bin[16]) +{ + /* validate and transform 32-byte MD5 hex string to 16-byte binary MD5 */ + if (32 != len) return -1; /*(Note: char *md5hex must be a 32-char string)*/ + for (int i = 0; i < 32; i+=2) { + int hi = md5hex[i]; + int lo = md5hex[i+1]; + if ('0' <= hi && hi <= '9') hi -= '0'; + else if ((hi |= 0x20), 'a' <= hi && hi <= 'f') hi += -'a' + 10; + else return -1; + if ('0' <= lo && lo <= '9') lo -= '0'; + else if ((lo |= 0x20), 'a' <= lo && lo <= 'f') lo += -'a' + 10; + else return -1; + md5bin[(i >> 1)] = (unsigned char)((hi << 4) | lo); + } + return 0; +} + +#if 0 +int http_auth_md5_hex2lc (char *md5hex) +{ + /* validate and transform 32-byte MD5 hex string to lowercase */ + int i; + for (i = 0; md5hex[i]; ++i) { + int c = md5hex[i]; + if ('0' <= c && c <= '9') continue; + else if ((c |= 0x20), 'a' <= c && c <= 'f') md5hex[i] = c; + else return -1; + } + return (32 == i) ? 0 : -1; /*(Note: char *md5hex must be a 32-char string)*/ +} +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/http_auth.h b/data/lighttpd/lighttpd-1.4.53/src/http_auth.h new file mode 100644 index 000000000..e652d71bd --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/http_auth.h @@ -0,0 +1,52 @@ +#ifndef _HTTP_AUTH_H_ +#define _HTTP_AUTH_H_ +#include "first.h" + +#include "base_decls.h" +#include "buffer.h" +#include "array.h" + +void http_auth_dumbdata_reset (void); + +struct http_auth_scheme_t; +struct http_auth_require_t; +struct http_auth_backend_t; + +typedef struct http_auth_require_t { + const struct http_auth_scheme_t *scheme; + buffer *realm; + int valid_user; + array *user; + array *group; + array *host; +} http_auth_require_t; + +http_auth_require_t * http_auth_require_init (void); +void http_auth_require_free (http_auth_require_t *require); +int http_auth_match_rules (const http_auth_require_t *require, const char *user, const char *group, const char *host); + +typedef struct http_auth_backend_t { + const char *name; + handler_t(*basic)(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); + handler_t(*digest)(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]); + void *p_d; +} http_auth_backend_t; + +typedef struct http_auth_scheme_t { + const char *name; + handler_t(*checkfn)(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); + /*(backend is arg only because auth.backend is separate config directive)*/ + void *p_d; +} http_auth_scheme_t; + +const http_auth_scheme_t * http_auth_scheme_get (const buffer *name); +void http_auth_scheme_set (const http_auth_scheme_t *scheme); +const http_auth_backend_t * http_auth_backend_get (const buffer *name); +void http_auth_backend_set (const http_auth_backend_t *backend); +int http_auth_const_time_memeq (const char *a, size_t alen, const char *b, size_t blen); + +void http_auth_setenv(connection *con, const char *username, size_t ulen, const char *auth_type, size_t alen); + +int http_auth_md5_hex2bin (const char *md5hex, size_t len, unsigned char md5bin[16]); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/http_chunk.c b/data/lighttpd/lighttpd-1.4.53/src/http_chunk.c new file mode 100644 index 000000000..1bdf2fa89 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/http_chunk.c @@ -0,0 +1,236 @@ +#include "first.h" + +/** + * the HTTP chunk-API + * + * + */ + +#include "base.h" +#include "chunk.h" +#include "http_chunk.h" +#include "stat_cache.h" +#include "fdevent.h" +#include "log.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdlib.h> +#include <unistd.h> + +#include <errno.h> +#include <string.h> + +static buffer * http_chunk_header(buffer *b, uintmax_t len) { + buffer_clear(b); + buffer_append_uint_hex(b, len); + buffer_append_string_len(b, CONST_STR_LEN("\r\n")); + return b; +} + +static void http_chunk_append_len(server *srv, connection *con, uintmax_t len) { + buffer *b = http_chunk_header(srv->tmp_chunk_len, len); + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(b)); +} + +static int http_chunk_append_file_open_fstat(server *srv, connection *con, buffer *fn, struct stat *st) { + if (!con->conf.follow_symlink) { + /*(preserve existing stat_cache symlink checks)*/ + stat_cache_entry *sce; + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, fn, &sce)) return -1; + } + + return stat_cache_open_rdonly_fstat(srv, con, fn, st); +} + +static void http_chunk_append_file_fd_range(server *srv, connection *con, buffer *fn, int fd, off_t offset, off_t len) { + chunkqueue *cq = con->write_queue; + + if (con->response.send_chunked) { + http_chunk_append_len(srv, con, (uintmax_t)len); + } + + chunkqueue_append_file_fd(cq, fn, fd, offset, len); + + if (con->response.send_chunked) { + chunkqueue_append_mem(cq, CONST_STR_LEN("\r\n")); + } +} + +int http_chunk_append_file_range(server *srv, connection *con, buffer *fn, off_t offset, off_t len) { + struct stat st; + const int fd = http_chunk_append_file_open_fstat(srv, con, fn, &st); + if (fd < 0) return -1; + + if (-1 == len) { + if (offset >= st.st_size) { + close(fd); + return (offset == st.st_size) ? 0 : -1; + } + len = st.st_size - offset; + } else if (st.st_size - offset < len) { + close(fd); + return -1; + } + + http_chunk_append_file_fd_range(srv, con, fn, fd, offset, len); + return 0; +} + +int http_chunk_append_file(server *srv, connection *con, buffer *fn) { + struct stat st; + const int fd = http_chunk_append_file_open_fstat(srv, con, fn, &st); + if (fd < 0) return -1; + + if (0 != st.st_size) { + http_chunk_append_file_fd_range(srv, con, fn, fd, 0, st.st_size); + } else { + close(fd); + } + return 0; +} + +static int http_chunk_append_to_tempfile(server *srv, connection *con, const char * mem, size_t len) { + chunkqueue * const cq = con->write_queue; + + if (con->response.send_chunked) { + buffer *b = http_chunk_header(srv->tmp_chunk_len, len); + if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, CONST_BUF_LEN(b))) { + return -1; + } + } + + if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, mem, len)) { + return -1; + } + + if (con->response.send_chunked) { + if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, CONST_STR_LEN("\r\n"))) { + return -1; + } + } + + return 0; +} + +static int http_chunk_append_cq_to_tempfile(server *srv, connection *con, chunkqueue *src, size_t len) { + chunkqueue * const cq = con->write_queue; + + if (con->response.send_chunked) { + buffer *b = http_chunk_header(srv->tmp_chunk_len, len); + if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, CONST_BUF_LEN(b))) { + return -1; + } + } + + if (0 != chunkqueue_steal_with_tempfiles(srv, cq, src, len)) { + return -1; + } + + if (con->response.send_chunked) { + if (0!=chunkqueue_append_mem_to_tempfile(srv,cq,CONST_STR_LEN("\r\n"))){ + return -1; + } + } + + return 0; +} + +static int http_chunk_uses_tempfile(server *srv, connection *con, size_t len) { + chunkqueue * const cq = con->write_queue; + chunk *c = cq->last; + UNUSED(srv); + + /* current usage does not append_mem or append_buffer after appending + * file, so not checking if users of this interface have appended large + * (references to) files to chunkqueue, which would not be in memory + * (but included in calculation for whether or not to use temp file) */ + + /*(allow slightly larger mem use if FDEVENT_STREAM_RESPONSE_BUFMIN + * to reduce creation of temp files when backend producer will be + * blocked until more data is sent to network to client)*/ + + if ((c && c->type == FILE_CHUNK && c->file.is_temp) + || cq->bytes_in - cq->bytes_out + len + > 1024 * ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) ? 128 : 64)) { + return 1; + } + + return 0; +} + +int http_chunk_append_buffer(server *srv, connection *con, buffer *mem) { + chunkqueue * const cq = con->write_queue; + size_t len = buffer_string_length(mem); + if (0 == len) return 0; + + if (http_chunk_uses_tempfile(srv, con, len)) { + return http_chunk_append_to_tempfile(srv, con, mem->ptr, len); + } + + if (con->response.send_chunked) { + http_chunk_append_len(srv, con, len); + } + + /*(chunkqueue_append_buffer() might steal buffer contents)*/ + chunkqueue_append_buffer(cq, mem); + + if (con->response.send_chunked) { + chunkqueue_append_mem(cq, CONST_STR_LEN("\r\n")); + } + + return 0; +} + +int http_chunk_append_mem(server *srv, connection *con, const char * mem, size_t len) { + chunkqueue * const cq = con->write_queue; + if (0 == len) return 0; + force_assert(NULL != mem); + + if (http_chunk_uses_tempfile(srv, con, len)) { + return http_chunk_append_to_tempfile(srv, con, mem, len); + } + + if (con->response.send_chunked) { + http_chunk_append_len(srv, con, len); + } + + chunkqueue_append_mem(cq, mem, len); + + if (con->response.send_chunked) { + chunkqueue_append_mem(cq, CONST_STR_LEN("\r\n")); + } + + return 0; +} + +int http_chunk_transfer_cqlen(server *srv, connection *con, chunkqueue *src, size_t len) { + chunkqueue * const cq = con->write_queue; + if (0 == len) return 0; + + if (http_chunk_uses_tempfile(srv, con, len)) { + return http_chunk_append_cq_to_tempfile(srv, con, src, len); + } + + if (con->response.send_chunked) { + http_chunk_append_len(srv, con, len); + } + + chunkqueue_steal(cq, src, len); + + if (con->response.send_chunked) { + chunkqueue_append_mem(cq, CONST_STR_LEN("\r\n")); + } + + return 0; +} + +void http_chunk_close(server *srv, connection *con) { + UNUSED(srv); + force_assert(NULL != con); + + if (con->response.send_chunked) { + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("0\r\n\r\n")); + } +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/http_chunk.h b/data/lighttpd/lighttpd-1.4.53/src/http_chunk.h new file mode 100644 index 000000000..ab335257a --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/http_chunk.h @@ -0,0 +1,14 @@ +#ifndef _HTTP_CHUNK_H_ +#define _HTTP_CHUNK_H_ +#include "first.h" + +#include "base_decls.h" + +int http_chunk_append_mem(server *srv, connection *con, const char * mem, size_t len); /* copies memory */ +int http_chunk_append_buffer(server *srv, connection *con, buffer *mem); /* may reset "mem" */ +int http_chunk_transfer_cqlen(server *srv, connection *con, chunkqueue *src, size_t len); +int http_chunk_append_file(server *srv, connection *con, buffer *fn); /* copies "fn" */ +int http_chunk_append_file_range(server *srv, connection *con, buffer *fn, off_t offset, off_t len); /* copies "fn" */ +void http_chunk_close(server *srv, connection *con); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/http_header.c b/data/lighttpd/lighttpd-1.4.53/src/http_header.c new file mode 100644 index 000000000..22e0d70c5 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/http_header.c @@ -0,0 +1,190 @@ +#include "first.h" + +#include "http_header.h" +#include "base.h" +#include "array.h" +#include "buffer.h" + + +typedef struct keyvlenvalue { + const int key; + const char * const value; + const size_t vlen; +} keyvlenvalue; + +/* Note: must be sorted by length */ +/* Note: must be kept in sync with http_header.h enum http_header_e */ +static const keyvlenvalue http_headers[] = { + { HTTP_HEADER_HOST, CONST_STR_LEN("Host") } + ,{ HTTP_HEADER_DATE, CONST_STR_LEN("Date") } + ,{ HTTP_HEADER_ETAG, CONST_STR_LEN("ETag") } + ,{ HTTP_HEADER_VARY, CONST_STR_LEN("Vary") } + ,{ HTTP_HEADER_RANGE, CONST_STR_LEN("Range") } + ,{ HTTP_HEADER_COOKIE, CONST_STR_LEN("Cookie") } + ,{ HTTP_HEADER_EXPECT, CONST_STR_LEN("Expect") } + ,{ HTTP_HEADER_STATUS, CONST_STR_LEN("Status") } + ,{ HTTP_HEADER_SERVER, CONST_STR_LEN("Server") } + ,{ HTTP_HEADER_UPGRADE, CONST_STR_LEN("Upgrade") } + ,{ HTTP_HEADER_LOCATION, CONST_STR_LEN("Location") } + ,{ HTTP_HEADER_FORWARDED, CONST_STR_LEN("Forwarded") } + ,{ HTTP_HEADER_CONNECTION, CONST_STR_LEN("Connection") } + ,{ HTTP_HEADER_SET_COOKIE, CONST_STR_LEN("Set-Cookie") } + ,{ HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type") } + ,{ HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified") } + ,{ HTTP_HEADER_AUTHORIZATION, CONST_STR_LEN("Authorization") } + ,{ HTTP_HEADER_IF_NONE_MATCH, CONST_STR_LEN("If-None-Match") } + ,{ HTTP_HEADER_CACHE_CONTROL, CONST_STR_LEN("Cache-Control") } + ,{ HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length") } + ,{ HTTP_HEADER_ACCEPT_ENCODING, CONST_STR_LEN("Accept-Encoding") } + ,{ HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For") } + ,{ HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding") } + ,{ HTTP_HEADER_CONTENT_LOCATION, CONST_STR_LEN("Content-Location") } + ,{ HTTP_HEADER_IF_MODIFIED_SINCE, CONST_STR_LEN("If-Modified-Since") } + ,{ HTTP_HEADER_TRANSFER_ENCODING, CONST_STR_LEN("Transfer-Encoding") } + ,{ HTTP_HEADER_X_FORWARDED_PROTO, CONST_STR_LEN("X-Forwarded-Proto") } + ,{ HTTP_HEADER_OTHER, NULL, 0 } +}; + +enum http_header_e http_header_hkey_get(const char *s, size_t slen) { + const struct keyvlenvalue * const kv = http_headers; + for (int i = 0; kv[i].vlen && slen >= kv[i].vlen; ++i) { + if (slen == kv[i].vlen + && 0 == buffer_caseless_compare(s, slen, kv[i].value, kv[i].vlen)) + return (enum http_header_e)kv[i].key; + } + return HTTP_HEADER_OTHER; +} + + +buffer * http_header_response_get(connection *con, enum http_header_e id, const char *k, size_t klen) { + data_string * const ds = + (id <= HTTP_HEADER_OTHER || (con->response.htags & id)) + ? (data_string *)array_get_element_klen(con->response.headers, k, klen) + : NULL; + return ds && !buffer_string_is_empty(ds->value) ? ds->value : NULL; +} + +void http_header_response_unset(connection *con, enum http_header_e id, const char *k, size_t klen) { + if (id <= HTTP_HEADER_OTHER || (con->response.htags & id)) { + if (id > HTTP_HEADER_OTHER) con->response.htags &= ~id; + array_set_key_value(con->response.headers, k, klen, CONST_STR_LEN("")); + } +} + +void http_header_response_set(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) { + /* set value, including setting blank value if 0 == vlen + * (note: if 0 == vlen, header is still inserted with blank value, + * which is used to indicate a "removed" header) + */ + if (id > HTTP_HEADER_OTHER) + (vlen) ? (con->response.htags |= id) : (con->response.htags &= ~id); + array_set_key_value(con->response.headers, k, klen, v, vlen); +} + +void http_header_response_append(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) { + if (vlen) { + data_string *ds= (id <= HTTP_HEADER_OTHER || (con->response.htags & id)) + ? (data_string *)array_get_element_klen(con->response.headers,k,klen) + : NULL; + if (id > HTTP_HEADER_OTHER) con->response.htags |= id; + if (NULL == ds) { + array_insert_key_value(con->response.headers, k, klen, v, vlen); + } + else { /* append value */ + buffer *vb = ds->value; + if (!buffer_string_is_empty(vb)) + buffer_append_string_len(vb, CONST_STR_LEN(", ")); + buffer_append_string_len(vb, v, vlen); + } + } +} + +void http_header_response_insert(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) { + if (vlen) { + data_string *ds= (id <= HTTP_HEADER_OTHER || (con->response.htags & id)) + ? (data_string *)array_get_element_klen(con->response.headers,k,klen) + : NULL; + if (id > HTTP_HEADER_OTHER) con->response.htags |= id; + if (NULL == ds) { + array_insert_key_value(con->response.headers, k, klen, v, vlen); + } + else { /* append value */ + buffer *vb = ds->value; + if (!buffer_string_is_empty(vb)) { + buffer_append_string_len(vb, CONST_STR_LEN("\r\n")); + buffer_append_string_len(vb, k, klen); + buffer_append_string_len(vb, CONST_STR_LEN(": ")); + } + buffer_append_string_len(vb, v, vlen); + } + } +} + + +buffer * http_header_request_get(connection *con, enum http_header_e id, const char *k, size_t klen) { + data_string * const ds = + (id <= HTTP_HEADER_OTHER || (con->request.htags & id)) + ? (data_string *)array_get_element_klen(con->request.headers, k, klen) + : NULL; + return ds && !buffer_string_is_empty(ds->value) ? ds->value : NULL; +} + +void http_header_request_unset(connection *con, enum http_header_e id, const char *k, size_t klen) { + if (id <= HTTP_HEADER_OTHER || (con->request.htags & id)) { + if (id > HTTP_HEADER_OTHER) con->request.htags &= ~id; + array_set_key_value(con->request.headers, k, klen, CONST_STR_LEN("")); + } +} + +void http_header_request_set(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) { + /* set value, including setting blank value if 0 == vlen + * (note: if 0 == vlen, header is still inserted with blank value, + * which is used to indicate a "removed" header) + */ + if (id > HTTP_HEADER_OTHER) + (vlen) ? (con->request.htags |= id) : (con->request.htags &= ~id); + array_set_key_value(con->request.headers, k, klen, v, vlen); +} + +void http_header_request_append(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen) { + if (vlen) { + data_string *ds = (id <= HTTP_HEADER_OTHER || (con->request.htags & id)) + ? (data_string *)array_get_element_klen(con->request.headers, k, klen) + : NULL; + if (id > HTTP_HEADER_OTHER) con->request.htags |= id; + if (NULL == ds) { + array_insert_key_value(con->request.headers, k, klen, v, vlen); + } + else { /* append value */ + buffer *vb = ds->value; + if (!buffer_string_is_empty(vb)) + buffer_append_string_len(vb, CONST_STR_LEN(", ")); + buffer_append_string_len(vb, v, vlen); + } + } +} + + +buffer * http_header_env_get(connection *con, const char *k, size_t klen) { + data_string * const ds = + (data_string *)array_get_element_klen(con->environment, k, klen); + return ds && !buffer_string_is_empty(ds->value) ? ds->value : NULL; +} + +void http_header_env_set(connection *con, const char *k, size_t klen, const char *v, size_t vlen) { + array_set_key_value(con->environment, k, klen, v, vlen); +} + +void http_header_env_append(connection *con, const char *k, size_t klen, const char *v, size_t vlen) { + /*if (vlen)*/ /* skip check; permit env var w/ blank value to be appended */ + { + buffer * const vb = http_header_env_get(con, k, klen); + if (NULL == vb) { + array_insert_key_value(con->environment, k, klen, v, vlen); + } + else if (vlen) { /* append value */ + buffer_append_string_len(vb, CONST_STR_LEN(", ")); + buffer_append_string_len(vb, v, vlen); + } + } +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/http_header.h b/data/lighttpd/lighttpd-1.4.53/src/http_header.h new file mode 100644 index 000000000..f19d1d96e --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/http_header.h @@ -0,0 +1,59 @@ +#ifndef INCLUDED_HTTP_HEADER_H +#define INCLUDED_HTTP_HEADER_H +#include "first.h" + +#include "base_decls.h" +#include "buffer.h" + +/* Note: must be kept in sync with http_header.c http_headers[] */ +/* Note: when adding new items, must replace OTHER in existing code for item */ +enum http_header_e { + HTTP_HEADER_UNSPECIFIED = -1 + ,HTTP_HEADER_OTHER = 0x00000000 + ,HTTP_HEADER_ACCEPT_ENCODING = 0x00000001 + ,HTTP_HEADER_AUTHORIZATION = 0x00000002 + ,HTTP_HEADER_CACHE_CONTROL = 0x00000004 + ,HTTP_HEADER_CONNECTION = 0x00000008 + ,HTTP_HEADER_CONTENT_ENCODING = 0x00000010 + ,HTTP_HEADER_CONTENT_LENGTH = 0x00000020 + ,HTTP_HEADER_CONTENT_LOCATION = 0x00000040 + ,HTTP_HEADER_CONTENT_TYPE = 0x00000080 + ,HTTP_HEADER_COOKIE = 0x00000100 + ,HTTP_HEADER_DATE = 0x00000200 + ,HTTP_HEADER_ETAG = 0x00000400 + ,HTTP_HEADER_EXPECT = 0x00000800 + ,HTTP_HEADER_FORWARDED = 0x00001000 + ,HTTP_HEADER_HOST = 0x00002000 + ,HTTP_HEADER_IF_MODIFIED_SINCE = 0x00004000 + ,HTTP_HEADER_IF_NONE_MATCH = 0x00008000 + ,HTTP_HEADER_LAST_MODIFIED = 0x00010000 + ,HTTP_HEADER_LOCATION = 0x00020000 + ,HTTP_HEADER_RANGE = 0x00040000 + ,HTTP_HEADER_SERVER = 0x00080000 + ,HTTP_HEADER_SET_COOKIE = 0x00100000 + ,HTTP_HEADER_STATUS = 0x00200000 + ,HTTP_HEADER_TRANSFER_ENCODING = 0x00400000 + ,HTTP_HEADER_UPGRADE = 0x00800000 + ,HTTP_HEADER_VARY = 0x01000000 + ,HTTP_HEADER_X_FORWARDED_FOR = 0x02000000 + ,HTTP_HEADER_X_FORWARDED_PROTO = 0x04000000 +}; + +enum http_header_e http_header_hkey_get(const char *s, size_t slen); + +buffer * http_header_response_get(connection *con, enum http_header_e id, const char *k, size_t klen); +void http_header_response_unset(connection *con, enum http_header_e id, const char *k, size_t klen); +void http_header_response_set(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen); +void http_header_response_append(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen); +void http_header_response_insert(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen); + +buffer * http_header_request_get(connection *con, enum http_header_e id, const char *k, size_t klen); +void http_header_request_unset(connection *con, enum http_header_e id, const char *k, size_t klen); +void http_header_request_set(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen); +void http_header_request_append(connection *con, enum http_header_e id, const char *k, size_t klen, const char *v, size_t vlen); + +buffer * http_header_env_get(connection *con, const char *k, size_t klen); +void http_header_env_set(connection *con, const char *k, size_t klen, const char *v, size_t vlen); +void http_header_env_append(connection *con, const char *k, size_t klen, const char *v, size_t vlen); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/http_kv.c b/data/lighttpd/lighttpd-1.4.53/src/http_kv.c new file mode 100644 index 000000000..2ddbf7da3 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/http_kv.c @@ -0,0 +1,176 @@ +#include "first.h" + +#include "http_kv.h" +#include "buffer.h" + +#include <string.h> + +typedef struct { + int key; + const char *value; + size_t vlen; +} keyvalue; + +static const keyvalue http_versions[] = { + { HTTP_VERSION_1_1, CONST_STR_LEN("HTTP/1.1") }, + { HTTP_VERSION_1_0, CONST_STR_LEN("HTTP/1.0") }, + { HTTP_VERSION_UNSET, NULL, 0 } +}; + +static const keyvalue http_methods[] = { + { HTTP_METHOD_GET, CONST_STR_LEN("GET") }, + { HTTP_METHOD_HEAD, CONST_STR_LEN("HEAD") }, + { HTTP_METHOD_POST, CONST_STR_LEN("POST") }, + { HTTP_METHOD_PUT, CONST_STR_LEN("PUT") }, + { HTTP_METHOD_DELETE, CONST_STR_LEN("DELETE") }, + { HTTP_METHOD_CONNECT, CONST_STR_LEN("CONNECT") }, + { HTTP_METHOD_OPTIONS, CONST_STR_LEN("OPTIONS") }, + { HTTP_METHOD_TRACE, CONST_STR_LEN("TRACE") }, + { HTTP_METHOD_ACL, CONST_STR_LEN("ACL") }, + { HTTP_METHOD_BASELINE_CONTROL, CONST_STR_LEN("BASELINE-CONTROL") }, + { HTTP_METHOD_BIND, CONST_STR_LEN("BIND") }, + { HTTP_METHOD_CHECKIN, CONST_STR_LEN("CHECKIN") }, + { HTTP_METHOD_CHECKOUT, CONST_STR_LEN("CHECKOUT") }, + { HTTP_METHOD_COPY, CONST_STR_LEN("COPY") }, + { HTTP_METHOD_LABEL, CONST_STR_LEN("LABEL") }, + { HTTP_METHOD_LINK, CONST_STR_LEN("LINK") }, + { HTTP_METHOD_LOCK, CONST_STR_LEN("LOCK") }, + { HTTP_METHOD_MERGE, CONST_STR_LEN("MERGE") }, + { HTTP_METHOD_MKACTIVITY, CONST_STR_LEN("MKACTIVITY") }, + { HTTP_METHOD_MKCALENDAR, CONST_STR_LEN("MKCALENDAR") }, + { HTTP_METHOD_MKCOL, CONST_STR_LEN("MKCOL") }, + { HTTP_METHOD_MKREDIRECTREF, CONST_STR_LEN("MKREDIRECTREF") }, + { HTTP_METHOD_MKWORKSPACE, CONST_STR_LEN("MKWORKSPACE") }, + { HTTP_METHOD_MOVE, CONST_STR_LEN("MOVE") }, + { HTTP_METHOD_ORDERPATCH, CONST_STR_LEN("ORDERPATCH") }, + { HTTP_METHOD_PATCH, CONST_STR_LEN("PATCH") }, + { HTTP_METHOD_PROPFIND, CONST_STR_LEN("PROPFIND") }, + { HTTP_METHOD_PROPPATCH, CONST_STR_LEN("PROPPATCH") }, + { HTTP_METHOD_REBIND, CONST_STR_LEN("REBIND") }, + { HTTP_METHOD_REPORT, CONST_STR_LEN("REPORT") }, + { HTTP_METHOD_SEARCH, CONST_STR_LEN("SEARCH") }, + { HTTP_METHOD_UNBIND, CONST_STR_LEN("UNBIND") }, + { HTTP_METHOD_UNCHECKOUT, CONST_STR_LEN("UNCHECKOUT") }, + { HTTP_METHOD_UNLINK, CONST_STR_LEN("UNLINK") }, + { HTTP_METHOD_UNLOCK, CONST_STR_LEN("UNLOCK") }, + { HTTP_METHOD_UPDATE, CONST_STR_LEN("UPDATE") }, + { HTTP_METHOD_UPDATEREDIRECTREF, CONST_STR_LEN("UPDATEREDIRECTREF") }, + { HTTP_METHOD_VERSION_CONTROL, CONST_STR_LEN("VERSION-CONTROL") }, + + { HTTP_METHOD_UNSET, NULL, 0 } +}; + +static const keyvalue http_status[] = { + { 100, CONST_STR_LEN("100 Continue") }, + { 101, CONST_STR_LEN("101 Switching Protocols") }, + { 102, CONST_STR_LEN("102 Processing") }, /* WebDAV */ + { 200, CONST_STR_LEN("200 OK") }, + { 201, CONST_STR_LEN("201 Created") }, + { 202, CONST_STR_LEN("202 Accepted") }, + { 203, CONST_STR_LEN("203 Non-Authoritative Information") }, + { 204, CONST_STR_LEN("204 No Content") }, + { 205, CONST_STR_LEN("205 Reset Content") }, + { 206, CONST_STR_LEN("206 Partial Content") }, + { 207, CONST_STR_LEN("207 Multi-status") }, /* WebDAV */ + { 300, CONST_STR_LEN("300 Multiple Choices") }, + { 301, CONST_STR_LEN("301 Moved Permanently") }, + { 302, CONST_STR_LEN("302 Found") }, + { 303, CONST_STR_LEN("303 See Other") }, + { 304, CONST_STR_LEN("304 Not Modified") }, + { 305, CONST_STR_LEN("305 Use Proxy") }, + { 306, CONST_STR_LEN("306 (Unused)") }, + { 307, CONST_STR_LEN("307 Temporary Redirect") }, + { 308, CONST_STR_LEN("308 Permanent Redirect") }, + { 400, CONST_STR_LEN("400 Bad Request") }, + { 401, CONST_STR_LEN("401 Unauthorized") }, + { 402, CONST_STR_LEN("402 Payment Required") }, + { 403, CONST_STR_LEN("403 Forbidden") }, + { 404, CONST_STR_LEN("404 Not Found") }, + { 405, CONST_STR_LEN("405 Method Not Allowed") }, + { 406, CONST_STR_LEN("406 Not Acceptable") }, + { 407, CONST_STR_LEN("407 Proxy Authentication Required") }, + { 408, CONST_STR_LEN("408 Request Timeout") }, + { 409, CONST_STR_LEN("409 Conflict") }, + { 410, CONST_STR_LEN("410 Gone") }, + { 411, CONST_STR_LEN("411 Length Required") }, + { 412, CONST_STR_LEN("412 Precondition Failed") }, + { 413, CONST_STR_LEN("413 Request Entity Too Large") }, + { 414, CONST_STR_LEN("414 Request-URI Too Long") }, + { 415, CONST_STR_LEN("415 Unsupported Media Type") }, + { 416, CONST_STR_LEN("416 Requested Range Not Satisfiable") }, + { 417, CONST_STR_LEN("417 Expectation Failed") }, + { 422, CONST_STR_LEN("422 Unprocessable Entity") }, /* WebDAV */ + { 423, CONST_STR_LEN("423 Locked") }, /* WebDAV */ + { 424, CONST_STR_LEN("424 Failed Dependency") }, /* WebDAV */ + { 426, CONST_STR_LEN("426 Upgrade Required") }, /* TLS */ + { 500, CONST_STR_LEN("500 Internal Server Error") }, + { 501, CONST_STR_LEN("501 Not Implemented") }, + { 502, CONST_STR_LEN("502 Bad Gateway") }, + { 503, CONST_STR_LEN("503 Service Not Available") }, + { 504, CONST_STR_LEN("504 Gateway Timeout") }, + { 505, CONST_STR_LEN("505 HTTP Version Not Supported") }, + { 507, CONST_STR_LEN("507 Insufficient Storage") }, /* WebDAV */ + + { -1, NULL, 0 } +}; + + +static const char *keyvalue_get_value(const keyvalue *kv, int k) { + int i; + for (i = 0; kv[i].value; i++) { + if (kv[i].key == k) return kv[i].value; + } + return NULL; +} + +static int keyvalue_get_key(const keyvalue *kv, const char *s) { + int i; + for (i = 0; kv[i].value; i++) { + if (0 == strcmp(kv[i].value, s)) return kv[i].key; + } + return -1; +} + + +const char *get_http_version_name(int i) { + return keyvalue_get_value(http_versions, i); +} + +const char *get_http_status_name(int i) { + return keyvalue_get_value(http_status, i); +} + +const char *get_http_method_name(http_method_t i) { + return keyvalue_get_value(http_methods, i); +} + +int get_http_version_key(const char *s) { + return keyvalue_get_key(http_versions, s); +} + +http_method_t get_http_method_key(const char *s) { + return (http_method_t)keyvalue_get_key(http_methods, s); +} + + +void http_status_append(buffer * const b, const int status) { + const keyvalue * const kv = http_status; + int i; + for (i = 0; kv[i].key != status && kv[i].value; ++i) ; + if (kv[i].value) { + buffer_append_string_len(b, kv[i].value, kv[i].vlen); + } + else { + buffer_append_int(b, status); + buffer_append_string_len(b, CONST_STR_LEN(" ")); + } +} + +void http_method_append(buffer * const b, const http_method_t method) { + const keyvalue * const kv = http_methods; + int i; + for (i = 0; kv[i].key != method && kv[i].value; ++i) ; + if (kv[i].value) { + buffer_append_string_len(b, kv[i].value, kv[i].vlen); + } +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/http_kv.h b/data/lighttpd/lighttpd-1.4.53/src/http_kv.h new file mode 100644 index 000000000..41abb6aaf --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/http_kv.h @@ -0,0 +1,69 @@ +#ifndef INCLUDED_HTTP_KV_H +#define INCLUDED_HTTP_KV_H +#include "first.h" + +#include "buffer.h" + +/* sources: + * - [RFC2616], Section 9 + * (or http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-22) + * - http://tools.ietf.org/html/draft-ietf-httpbis-method-registrations-11, Appendix A + * + * http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-22, Section 8.1 defines + * a new registry (not available yet): + * http://www.iana.org/assignments/http-methods + */ + +typedef enum { + HTTP_METHOD_UNSET = -1, + HTTP_METHOD_GET, /* [RFC2616], Section 9.3 */ + HTTP_METHOD_HEAD, /* [RFC2616], Section 9.4 */ + HTTP_METHOD_POST, /* [RFC2616], Section 9.5 */ + HTTP_METHOD_PUT, /* [RFC2616], Section 9.6 */ + HTTP_METHOD_DELETE, /* [RFC2616], Section 9.7 */ + HTTP_METHOD_CONNECT, /* [RFC2616], Section 9.9 */ + HTTP_METHOD_OPTIONS, /* [RFC2616], Section 9.2 */ + HTTP_METHOD_TRACE, /* [RFC2616], Section 9.8 */ + HTTP_METHOD_ACL, /* [RFC3744], Section 8.1 */ + HTTP_METHOD_BASELINE_CONTROL, /* [RFC3253], Section 12.6 */ + HTTP_METHOD_BIND, /* [RFC5842], Section 4 */ + HTTP_METHOD_CHECKIN, /* [RFC3253], Section 4.4 and [RFC3253], Section 9.4 */ + HTTP_METHOD_CHECKOUT, /* [RFC3253], Section 4.3 and [RFC3253], Section 8.8 */ + HTTP_METHOD_COPY, /* [RFC4918], Section 9.8 */ + HTTP_METHOD_LABEL, /* [RFC3253], Section 8.2 */ + HTTP_METHOD_LINK, /* [RFC2068], Section 19.6.1.2 */ + HTTP_METHOD_LOCK, /* [RFC4918], Section 9.10 */ + HTTP_METHOD_MERGE, /* [RFC3253], Section 11.2 */ + HTTP_METHOD_MKACTIVITY, /* [RFC3253], Section 13.5 */ + HTTP_METHOD_MKCALENDAR, /* [RFC4791], Section 5.3.1 */ + HTTP_METHOD_MKCOL, /* [RFC4918], Section 9.3 */ + HTTP_METHOD_MKREDIRECTREF, /* [RFC4437], Section 6 */ + HTTP_METHOD_MKWORKSPACE, /* [RFC3253], Section 6.3 */ + HTTP_METHOD_MOVE, /* [RFC4918], Section 9.9 */ + HTTP_METHOD_ORDERPATCH, /* [RFC3648], Section 7 */ + HTTP_METHOD_PATCH, /* [RFC5789], Section 2 */ + HTTP_METHOD_PROPFIND, /* [RFC4918], Section 9.1 */ + HTTP_METHOD_PROPPATCH, /* [RFC4918], Section 9.2 */ + HTTP_METHOD_REBIND, /* [RFC5842], Section 6 */ + HTTP_METHOD_REPORT, /* [RFC3253], Section 3.6 */ + HTTP_METHOD_SEARCH, /* [RFC5323], Section 2 */ + HTTP_METHOD_UNBIND, /* [RFC5842], Section 5 */ + HTTP_METHOD_UNCHECKOUT, /* [RFC3253], Section 4.5 */ + HTTP_METHOD_UNLINK, /* [RFC2068], Section 19.6.1.3 */ + HTTP_METHOD_UNLOCK, /* [RFC4918], Section 9.11 */ + HTTP_METHOD_UPDATE, /* [RFC3253], Section 7.1 */ + HTTP_METHOD_UPDATEREDIRECTREF, /* [RFC4437], Section 7 */ + HTTP_METHOD_VERSION_CONTROL /* [RFC3253], Section 3.5 */ +} http_method_t; + +typedef enum { HTTP_VERSION_UNSET = -1, HTTP_VERSION_1_0, HTTP_VERSION_1_1 } http_version_t; + +const char *get_http_status_name(int i); +const char *get_http_version_name(int i); +const char *get_http_method_name(http_method_t i); +int get_http_version_key(const char *s); +http_method_t get_http_method_key(const char *s); +void http_status_append(buffer *b, int status); +void http_method_append(buffer *b, http_method_t method); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/http_vhostdb.c b/data/lighttpd/lighttpd-1.4.53/src/http_vhostdb.c new file mode 100644 index 000000000..db8871ce3 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/http_vhostdb.c @@ -0,0 +1,35 @@ +#include "first.h" + +#include "http_vhostdb.h" + +#include <string.h> + + +static http_vhostdb_backend_t http_vhostdb_backends[8]; + +void http_vhostdb_dumbdata_reset (void) +{ + memset(http_vhostdb_backends, 0, sizeof(http_vhostdb_backends)); +} + +const http_vhostdb_backend_t * http_vhostdb_backend_get (const buffer *name) +{ + int i = 0; + while (NULL != http_vhostdb_backends[i].name + && 0 != strcmp(http_vhostdb_backends[i].name, name->ptr)) { + ++i; + } + return (NULL != http_vhostdb_backends[i].name) + ? http_vhostdb_backends+i + : NULL; +} + +void http_vhostdb_backend_set (const http_vhostdb_backend_t *backend) +{ + unsigned int i = 0; + while (NULL != http_vhostdb_backends[i].name) ++i; + /*(must resize http_vhostdb_backends[] if too many different backends)*/ + force_assert( + i < (sizeof(http_vhostdb_backends)/sizeof(http_vhostdb_backend_t))-1); + memcpy(http_vhostdb_backends+i, backend, sizeof(http_vhostdb_backend_t)); +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/http_vhostdb.h b/data/lighttpd/lighttpd-1.4.53/src/http_vhostdb.h new file mode 100644 index 000000000..01036077b --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/http_vhostdb.h @@ -0,0 +1,21 @@ +#ifndef _HTTP_VHOST_H_ +#define _HTTP_VHOST_H_ +#include "first.h" + +#include "base_decls.h" +#include "buffer.h" + +void http_vhostdb_dumbdata_reset (void); + +struct http_vhostdb_backend_t; + +typedef struct http_vhostdb_backend_t { + const char *name; + int(*query)(server *srv, connection *con, void *p_d, buffer *result); + void *p_d; +} http_vhostdb_backend_t; + +const http_vhostdb_backend_t * http_vhostdb_backend_get (const buffer *name); +void http_vhostdb_backend_set (const http_vhostdb_backend_t *backend); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/inet_ntop_cache.c b/data/lighttpd/lighttpd-1.4.53/src/inet_ntop_cache.c new file mode 100644 index 000000000..868b8df2f --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/inet_ntop_cache.c @@ -0,0 +1,76 @@ +#include "first.h" + +#include "inet_ntop_cache.h" + +#include "sys-socket.h" +#include <sys/types.h> +#include <string.h> +#ifndef _WIN32 +#include <arpa/inet.h> +#endif + +#include "sock_addr.h" + + +const char * inet_ntop_cache_get_ip(server *srv, sock_addr *addr) { +#ifdef HAVE_IPV6 + typedef struct { + int family; + union { + struct in6_addr ipv6; + struct in_addr ipv4; + } addr; + char b2[INET6_ADDRSTRLEN + 1]; + } inet_ntop_cache_type; + #define INET_NTOP_CACHE_MAX 4 + static inet_ntop_cache_type inet_ntop_cache[INET_NTOP_CACHE_MAX]; + static int ndx; + + int i; + UNUSED(srv); + #ifdef HAVE_SYS_UN_H + if (addr->plain.sa_family == AF_UNIX) return addr->un.sun_path; + #endif + for (i = 0; i < INET_NTOP_CACHE_MAX; i++) { + if (inet_ntop_cache[i].family == addr->plain.sa_family) { + if (inet_ntop_cache[i].family == AF_INET6 && + 0 == memcmp(inet_ntop_cache[i].addr.ipv6.s6_addr, addr->ipv6.sin6_addr.s6_addr, 16)) { + /* IPv6 found in cache */ + break; + } else if (inet_ntop_cache[i].family == AF_INET && + inet_ntop_cache[i].addr.ipv4.s_addr == addr->ipv4.sin_addr.s_addr) { + /* IPv4 found in cache */ + break; + + } + } + } + + if (i == INET_NTOP_CACHE_MAX) { + /* not found in cache */ + const char *s; + + i = ndx; + if (++ndx >= INET_NTOP_CACHE_MAX) ndx = 0; + s = sock_addr_inet_ntop(addr, inet_ntop_cache[i].b2, INET6_ADDRSTRLEN); + if (NULL == s) return ""; + + inet_ntop_cache[i].family = addr->plain.sa_family; + + if (inet_ntop_cache[i].family == AF_INET) { + inet_ntop_cache[i].addr.ipv4.s_addr = addr->ipv4.sin_addr.s_addr; + } else if (inet_ntop_cache[i].family == AF_INET6) { + memcpy(inet_ntop_cache[i].addr.ipv6.s6_addr, addr->ipv6.sin6_addr.s6_addr, 16); + } + } + + return inet_ntop_cache[i].b2; +#else + UNUSED(srv); + if (addr->plain.sa_family == AF_INET) return inet_ntoa(addr->ipv4.sin_addr); + #ifdef HAVE_SYS_UN_H + if (addr->plain.sa_family == AF_UNIX) return addr->un.sun_path; + #endif + return ""; +#endif +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/inet_ntop_cache.h b/data/lighttpd/lighttpd-1.4.53/src/inet_ntop_cache.h new file mode 100644 index 000000000..027f40977 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/inet_ntop_cache.h @@ -0,0 +1,9 @@ +#ifndef _INET_NTOP_CACHE_H_ +#define _INET_NTOP_CACHE_H_ +#include "first.h" + +#include "base_decls.h" + +const char * inet_ntop_cache_get_ip(server *srv, sock_addr *addr); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/joblist.c b/data/lighttpd/lighttpd-1.4.53/src/joblist.c new file mode 100644 index 000000000..7c2102478 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/joblist.c @@ -0,0 +1,66 @@ +#include "first.h" + +#include "base.h" +#include "joblist.h" + +#include <stdlib.h> +#include <string.h> + +int joblist_append(server *srv, connection *con) { + if (srv->joblist->size == 0) { + srv->joblist->size = 16; + srv->joblist->ptr = malloc(sizeof(*srv->joblist->ptr) * srv->joblist->size); + force_assert(NULL != srv->joblist->ptr); + } else if (srv->joblist->used == srv->joblist->size) { + srv->joblist->size += 16; + srv->joblist->ptr = realloc(srv->joblist->ptr, sizeof(*srv->joblist->ptr) * srv->joblist->size); + force_assert(NULL != srv->joblist->ptr); + } + + srv->joblist->ptr[srv->joblist->used++] = con; + + return 0; +} + +void joblist_free(server *srv, connections *joblist) { + UNUSED(srv); + + free(joblist->ptr); + free(joblist); +} + +connection *fdwaitqueue_unshift(server *srv, connections *fdwaitqueue) { + connection *con; + UNUSED(srv); + + + if (fdwaitqueue->used == 0) return NULL; + + con = fdwaitqueue->ptr[0]; + + memmove(fdwaitqueue->ptr, &(fdwaitqueue->ptr[1]), --fdwaitqueue->used * sizeof(*(fdwaitqueue->ptr))); + + return con; +} + +int fdwaitqueue_append(server *srv, connection *con) { + if (srv->fdwaitqueue->size == 0) { + srv->fdwaitqueue->size = 16; + srv->fdwaitqueue->ptr = malloc(sizeof(*(srv->fdwaitqueue->ptr)) * srv->fdwaitqueue->size); + force_assert(NULL != srv->fdwaitqueue->ptr); + } else if (srv->fdwaitqueue->used == srv->fdwaitqueue->size) { + srv->fdwaitqueue->size += 16; + srv->fdwaitqueue->ptr = realloc(srv->fdwaitqueue->ptr, sizeof(*(srv->fdwaitqueue->ptr)) * srv->fdwaitqueue->size); + force_assert(NULL != srv->fdwaitqueue->ptr); + } + + srv->fdwaitqueue->ptr[srv->fdwaitqueue->used++] = con; + + return 0; +} + +void fdwaitqueue_free(server *srv, connections *fdwaitqueue) { + UNUSED(srv); + free(fdwaitqueue->ptr); + free(fdwaitqueue); +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/joblist.h b/data/lighttpd/lighttpd-1.4.53/src/joblist.h new file mode 100644 index 000000000..78c40e53b --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/joblist.h @@ -0,0 +1,14 @@ +#ifndef _JOB_LIST_H_ +#define _JOB_LIST_H_ +#include "first.h" + +#include "base_decls.h" + +int joblist_append(server *srv, connection *con); +void joblist_free(server *srv, connections *joblist); + +int fdwaitqueue_append(server *srv, connection *con); +void fdwaitqueue_free(server *srv, connections *fdwaitqueue); +connection *fdwaitqueue_unshift(server *srv, connections *fdwaitqueue); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/keyvalue.c b/data/lighttpd/lighttpd-1.4.53/src/keyvalue.c new file mode 100644 index 000000000..c1b6b019c --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/keyvalue.c @@ -0,0 +1,460 @@ +#include "first.h" + +#include "keyvalue.h" +#include "base.h" +#include "burl.h" +#include "log.h" + +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_PCRE_H +#include <pcre.h> +#endif + +typedef struct pcre_keyvalue { +#ifdef HAVE_PCRE_H + pcre *key; + pcre_extra *key_extra; +#endif + buffer *value; +} pcre_keyvalue; + +pcre_keyvalue_buffer *pcre_keyvalue_buffer_init(void) { + pcre_keyvalue_buffer *kvb; + + kvb = calloc(1, sizeof(*kvb)); + force_assert(NULL != kvb); + + return kvb; +} + +int pcre_keyvalue_buffer_append(server *srv, pcre_keyvalue_buffer *kvb, buffer *key, buffer *value) { +#ifdef HAVE_PCRE_H + size_t i; + const char *errptr; + int erroff; + pcre_keyvalue *kv; + + if (!key) return -1; + + if (kvb->size == 0) { + kvb->size = 4; + kvb->used = 0; + + kvb->kv = malloc(kvb->size * sizeof(*kvb->kv)); + force_assert(NULL != kvb->kv); + + for(i = 0; i < kvb->size; i++) { + kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); + force_assert(NULL != kvb->kv[i]); + } + } else if (kvb->used == kvb->size) { + kvb->size += 4; + + kvb->kv = realloc(kvb->kv, kvb->size * sizeof(*kvb->kv)); + force_assert(NULL != kvb->kv); + + for(i = kvb->used; i < kvb->size; i++) { + kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); + force_assert(NULL != kvb->kv[i]); + } + } + + kv = kvb->kv[kvb->used]; + if (NULL == (kv->key = pcre_compile(key->ptr, + 0, &errptr, &erroff, NULL))) { + + log_error_write(srv, __FILE__, __LINE__, "SS", + "rexexp compilation error at ", errptr); + return -1; + } + + if (NULL == (kv->key_extra = pcre_study(kv->key, 0, &errptr)) && + errptr != NULL) { + return -1; + } + + kv->value = buffer_init_buffer(value); + + kvb->used++; + +#else + static int logged_message = 0; + if (logged_message) return 0; + logged_message = 1; + log_error_write(srv, __FILE__, __LINE__, "s", + "pcre support is missing, please install libpcre and the headers"); + UNUSED(kvb); + UNUSED(key); + UNUSED(value); +#endif + + return 0; +} + +void pcre_keyvalue_buffer_free(pcre_keyvalue_buffer *kvb) { +#ifdef HAVE_PCRE_H + size_t i; + pcre_keyvalue *kv; + + for (i = 0; i < kvb->size; i++) { + kv = kvb->kv[i]; + if (kv->key) pcre_free(kv->key); + if (kv->key_extra) pcre_free(kv->key_extra); + if (kv->value) buffer_free(kv->value); + free(kv); + } + + if (kvb->kv) free(kvb->kv); +#endif + + free(kvb); +} + +#ifdef HAVE_PCRE_H +static void pcre_keyvalue_buffer_append_match(buffer *b, const char **list, int n, unsigned int num, int flags) { + if (num < (unsigned int)n) { /* n is always > 0 */ + burl_append(b, list[num], strlen(list[num]), flags); + } +} + +static void pcre_keyvalue_buffer_append_ctxmatch(buffer *b, pcre_keyvalue_ctx *ctx, unsigned int num, int flags) { + const struct cond_cache_t * const cache = ctx->cache; + if (!cache) return; /* no enclosing match context */ + if ((int)num < cache->patterncount) { + const int off = cache->matches[(num <<= 1)]; /*(num *= 2)*/ + const int len = cache->matches[num+1] - off; + burl_append(b, cache->comp_value->ptr + off, (size_t)len, flags); + } +} + +static int pcre_keyvalue_buffer_subst_ext(buffer *b, const char *pattern, const char **list, int n, pcre_keyvalue_ctx *ctx) { + const unsigned char *p = (unsigned char *)pattern+2;/* +2 past ${} or %{} */ + int flags = 0; + while (!light_isdigit(*p) && *p != '}' && *p != '\0') { + if (0) { + } + else if (p[0] == 'e' && p[1] == 's' && p[2] == 'c') { + p+=3; + if (p[0] == ':') { + flags |= BURL_ENCODE_ALL; + p+=1; + } + else if (0 == strncmp((const char *)p, "ape:", 4)) { + flags |= BURL_ENCODE_ALL; + p+=4; + } + else if (0 == strncmp((const char *)p, "nde:", 4)) { + flags |= BURL_ENCODE_NDE; + p+=4; + } + else if (0 == strncmp((const char *)p, "psnde:", 6)) { + flags |= BURL_ENCODE_PSNDE; + p+=6; + } + else { /* skip unrecognized esc... */ + p = (const unsigned char *)strchr((const char *)p, ':'); + if (NULL == p) return -1; + ++p; + } + } + else if (p[0] == 'n' && p[1] == 'o') { + p+=2; + if (0 == strncmp((const char *)p, "esc:", 4)) { + flags |= BURL_ENCODE_NONE; + p+=4; + } + else if (0 == strncmp((const char *)p, "escape:", 7)) { + flags |= BURL_ENCODE_NONE; + p+=7; + } + else { /* skip unrecognized no... */ + p = (const unsigned char *)strchr((const char *)p, ':'); + if (NULL == p) return -1; + ++p; + } + } + else if (p[0] == 't' && p[1] == 'o') { + p+=2; + if (0 == strncmp((const char *)p, "lower:", 6)) { + flags |= BURL_TOLOWER; + p+=6; + } + else if (0 == strncmp((const char *)p, "upper:", 6)) { + flags |= BURL_TOLOWER; + p+=6; + } + else { /* skip unrecognized to... */ + p = (const unsigned char *)strchr((const char *)p, ':'); + if (NULL == p) return -1; + ++p; + } + } + else if (p[0] == 'u' && p[1] == 'r' && p[2] == 'l' && p[3] == '.') { + p+=4; + if (0 == strncmp((const char *)p, "scheme}", 7)) { + burl_append(b, CONST_BUF_LEN(ctx->burl->scheme), flags); + p+=6; + } + else if (0 == strncmp((const char *)p, "authority}", 10)) { + burl_append(b, CONST_BUF_LEN(ctx->burl->authority), flags); + p+=9; + } + else if (0 == strncmp((const char *)p, "port}", 5)) { + buffer_append_int(b, (int)ctx->burl->port); + p+=4; + } + else if (0 == strncmp((const char *)p, "path}", 5)) { + burl_append(b, CONST_BUF_LEN(ctx->burl->path), flags); + p+=4; + } + else if (0 == strncmp((const char *)p, "query}", 6)) { + burl_append(b, CONST_BUF_LEN(ctx->burl->query), flags); + p+=5; + } + else { /* skip unrecognized url.* */ + p = (const unsigned char *)strchr((const char *)p, '}'); + if (NULL == p) return -1; + } + break; + } + else if (p[0] == 'q' && p[1] == 's' && p[2] == 'a' && p[3] == '}') { + const buffer *qs = ctx->burl->query; + if (!buffer_is_empty(qs)) { + if (NULL != strchr(b->ptr, '?')) { + if (!buffer_string_is_empty(qs)) + buffer_append_string_len(b, CONST_STR_LEN("&")); + } + else { + buffer_append_string_len(b, CONST_STR_LEN("?")); + } + burl_append(b, CONST_BUF_LEN(qs), flags); + } + p+=3; + break; + } + else if (p[0] == 'e' && p[1] == 'n' && p[2] == 'c' + && 0 == strncmp((const char *)p+3, "b64u:", 5)) { + flags |= BURL_ENCODE_B64U; + p+=8; + } + else if (p[0] == 'd' && p[1] == 'e' && p[2] == 'c' + && 0 == strncmp((const char *)p+3, "b64u:", 5)) { + flags |= BURL_DECODE_B64U; + p+=8; + } + else ++p; /* skip unrecognized char */ + } + if (*p == '\0') return -1; + if (*p != '}') { /* light_isdigit(*p) */ + unsigned int num = *p - '0'; + ++p; + if (light_isdigit(*p)) num = num * 10 + (*p++ - '0'); + if (*p != '}') { + p = (const unsigned char *)strchr((const char *)p, '}'); + if (NULL == p) return -1; + } + if (0 == flags) flags = BURL_ENCODE_PSNDE; /* default */ + pattern[0] == '$' /*(else '%')*/ + ? pcre_keyvalue_buffer_append_match(b, list, n, num, flags) + : pcre_keyvalue_buffer_append_ctxmatch(b, ctx, num, flags); + } + return (int)(p + 1 - (unsigned char *)pattern - 2); +} + +static void pcre_keyvalue_buffer_subst(buffer *b, const buffer *patternb, const char **list, int n, pcre_keyvalue_ctx *ctx) { + const char *pattern = patternb->ptr; + const size_t pattern_len = buffer_string_length(patternb); + size_t start = 0; + + /* search for $... or %... pattern substitutions */ + + buffer_clear(b); + + for (size_t k = 0; k + 1 < pattern_len; ++k) { + if (pattern[k] == '$' || pattern[k] == '%') { + + buffer_append_string_len(b, pattern + start, k - start); + + if (pattern[k + 1] == '{') { + int num = pcre_keyvalue_buffer_subst_ext(b, pattern+k, list, n, ctx); + if (num < 0) return; /* error; truncate result */ + k += (size_t)num; + } else if (light_isdigit(((unsigned char *)pattern)[k + 1])) { + unsigned int num = (unsigned int)pattern[k + 1] - '0'; + pattern[k] == '$' /*(else '%')*/ + ? pcre_keyvalue_buffer_append_match(b, list, n, num, 0) + : pcre_keyvalue_buffer_append_ctxmatch(b, ctx, num, 0); + } else { + /* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */ + buffer_append_string_len(b, pattern+k, pattern[k] == pattern[k+1] ? 1 : 2); + } + + k++; + start = k + 1; + } + } + + buffer_append_string_len(b, pattern + start, pattern_len - start); +} + +handler_t pcre_keyvalue_buffer_process(pcre_keyvalue_buffer *kvb, pcre_keyvalue_ctx *ctx, buffer *input, buffer *result) { + for (int i = 0, used = (int)kvb->used; i < used; ++i) { + pcre_keyvalue * const kv = kvb->kv[i]; + #define N 20 + int ovec[N * 3]; + #undef N + int n = pcre_exec(kv->key, kv->key_extra, CONST_BUF_LEN(input), + 0, 0, ovec, sizeof(ovec)/sizeof(int)); + if (n < 0) { + if (n != PCRE_ERROR_NOMATCH) { + return HANDLER_ERROR; + } + } + else if (buffer_string_is_empty(kv->value)) { + /* short-circuit if blank replacement pattern + * (do not attempt to match against remaining kvb rules) */ + ctx->m = i; + return HANDLER_GO_ON; + } + else { /* it matched */ + const char **list; + ctx->m = i; + pcre_get_substring_list(input->ptr, ovec, n, &list); + pcre_keyvalue_buffer_subst(result, kv->value, list, n, ctx); + pcre_free(list); + return HANDLER_FINISHED; + } + } + + return HANDLER_GO_ON; +} +#else +handler_t pcre_keyvalue_buffer_process(pcre_keyvalue_buffer *kvb, pcre_keyvalue_ctx *ctx, buffer *input, buffer *result) { + UNUSED(kvb); + UNUSED(ctx); + UNUSED(input); + UNUSED(result); + return HANDLER_GO_ON; +} +#endif + + +/* modified from burl_normalize_basic() to handle %% extra encoding layer */ + +/* c (char) and n (nibble) MUST be unsigned integer types */ +#define li_cton(c,n) \ + (((n) = (c) - '0') <= 9 || (((n) = ((c)&0xdf) - 'A') <= 5 ? ((n) += 10) : 0)) + +static void pcre_keyvalue_burl_percent_toupper (buffer *b) +{ + const unsigned char * const s = (unsigned char *)b->ptr; + const int used = (int)buffer_string_length(b); + unsigned int n1, n2; + for (int i = 0; i < used; ++i) { + if (s[i]=='%' && li_cton(s[i+1],n1) && li_cton(s[i+2],n2)) { + if (s[i+1] >= 'a') b->ptr[i+1] &= 0xdf; /* uppercase hex */ + if (s[i+2] >= 'a') b->ptr[i+2] &= 0xdf; /* uppercase hex */ + i+=2; + } + } +} + +static void pcre_keyvalue_burl_percent_percent_toupper (buffer *b) +{ + const unsigned char * const s = (unsigned char *)b->ptr; + const int used = (int)buffer_string_length(b); + unsigned int n1, n2; + for (int i = 0; i < used; ++i) { + if (s[i] == '%' && s[i+1]=='%' + && li_cton(s[i+2],n1) && li_cton(s[i+3],n2)) { + if (s[i+2] >= 'a') b->ptr[i+2] &= 0xdf; /* uppercase hex */ + if (s[i+3] >= 'a') b->ptr[i+3] &= 0xdf; /* uppercase hex */ + i+=3; + } + } +} + +static const char hex_chars_uc[] = "0123456789ABCDEF"; + +static void pcre_keyvalue_burl_percent_high_UTF8 (buffer *b, buffer *t) +{ + const unsigned char * const s = (unsigned char *)b->ptr; + unsigned char *p; + const int used = (int)buffer_string_length(b); + unsigned int count = 0, j = 0; + for (int i = 0; i < used; ++i) { + if (s[i] > 0x7F) ++count; + } + if (0 == count) return; + + p = (unsigned char *)buffer_string_prepare_copy(t, used+(count*2)); + for (int i = 0; i < used; ++i, ++j) { + if (s[i] <= 0x7F) + p[j] = s[i]; + else { + p[j] = '%'; + p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF]; + p[++j] = hex_chars_uc[s[i] & 0xF]; + } + } + buffer_commit(t, j); + buffer_copy_buffer(b, t); +} + +static void pcre_keyvalue_burl_percent_percent_high_UTF8 (buffer *b, buffer *t) +{ + const unsigned char * const s = (unsigned char *)b->ptr; + unsigned char *p; + const int used = (int)buffer_string_length(b); + unsigned int count = 0, j = 0; + for (int i = 0; i < used; ++i) { + if (s[i] > 0x7F) ++count; + } + if (0 == count) return; + + p = (unsigned char *)buffer_string_prepare_copy(t, used+(count*3)); + for (int i = 0; i < used; ++i, ++j) { + if (s[i] <= 0x7F) + p[j] = s[i]; + else { + p[j] = '%'; + p[++j] = '%'; + p[++j] = hex_chars_uc[(s[i] >> 4) & 0xF]; + p[++j] = hex_chars_uc[s[i] & 0xF]; + } + } + buffer_commit(t, j); + buffer_copy_buffer(b, t); +} + +/* Basic normalization of regex and regex replacement to mirror some of + * the normalizations performed on request URI (for better compatibility). + * Note: not currently attempting to replace unnecessary percent-encoding + * (would need to know if regex was intended to match url-path or + * query-string or both, and then would have to regex-escape if those + * chars where special regex chars such as . * + ? ( ) [ ] | and more) + * Not attempting to percent-encode chars which should be encoded, again + * since regex might target url-path, query-string, or both, and we would + * have to avoid percent-encoding special regex chars. + * Also not attempting to detect unnecessarily regex-escape in, e.g. %\x\x + * Preserve improper %-encoded sequences which are not %XX (using hex chars) + * Intentionally not performing path simplification (e.g. ./ ../) + * If regex-specific normalizations begin to be made to k here, + * must revisit callers, e.g. one configfile.c use on non-regex string. + * "%%" (percent_percent) is used in regex replacement strings since + * otherwise "%n" is used to indicate regex backreference where n is number. + */ + +void pcre_keyvalue_burl_normalize_key (buffer *k, buffer *t) +{ + pcre_keyvalue_burl_percent_toupper(k); + pcre_keyvalue_burl_percent_high_UTF8(k, t); +} + +void pcre_keyvalue_burl_normalize_value (buffer *v, buffer *t) +{ + pcre_keyvalue_burl_percent_percent_toupper(v); + pcre_keyvalue_burl_percent_percent_high_UTF8(v, t); +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/keyvalue.h b/data/lighttpd/lighttpd-1.4.53/src/keyvalue.h new file mode 100644 index 000000000..0a7854845 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/keyvalue.h @@ -0,0 +1,31 @@ +#ifndef _KEY_VALUE_H_ +#define _KEY_VALUE_H_ +#include "first.h" + +#include "base_decls.h" +#include "buffer.h" + +struct burl_parts_t; /* declaration */ +struct cond_cache_t; /* declaration */ +struct pcre_keyvalue; /* declaration */ + +typedef struct pcre_keyvalue_ctx { + struct cond_cache_t *cache; + struct burl_parts_t *burl; + int m; +} pcre_keyvalue_ctx; + +typedef struct { + struct pcre_keyvalue **kv; + size_t used; + size_t size; +} pcre_keyvalue_buffer; + +pcre_keyvalue_buffer *pcre_keyvalue_buffer_init(void); +int pcre_keyvalue_buffer_append(struct server *srv, pcre_keyvalue_buffer *kvb, buffer *key, buffer *value); +void pcre_keyvalue_buffer_free(pcre_keyvalue_buffer *kvb); +handler_t pcre_keyvalue_buffer_process(pcre_keyvalue_buffer *kvb, pcre_keyvalue_ctx *ctx, buffer *input, buffer *result); +void pcre_keyvalue_burl_normalize_key(buffer *k, buffer *t); +void pcre_keyvalue_burl_normalize_value(buffer *v, buffer *t); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/lemon.c b/data/lighttpd/lighttpd-1.4.53/src/lemon.c new file mode 100644 index 000000000..ad86cee2c --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/lemon.c @@ -0,0 +1,4436 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifdef __COVERITY__ +#define _Float128 long double +#define _Float64x long double +#define _Float64 double +#define _Float32x double +#define _Float32 float +#endif + +/* +** This file contains all sources (including headers) to the LEMON +** LALR(1) parser generator. The sources have been combined into a +** single file to make it easy to include LEMON in the source tree +** and Makefile of another program. +** +** The author of this program disclaims copyright. +*/ +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <inttypes.h> +#include <unistd.h> /* access() */ + +#define UNUSED(x) ( (void)(x) ) + +#ifndef __WIN32__ +# if defined(_WIN32) || defined(WIN32) +# define __WIN32__ +# endif +#endif + +#if __GNUC__ > 2 +#define NORETURN __attribute__ ((__noreturn__)) +#else +#define NORETURN +#endif + +/* #define PRIVATE static */ +#define PRIVATE static + +#ifdef TEST +#define MAXRHS 5 /* Set low to exercise exception code */ +#else +#define MAXRHS 1000 +#endif + +void *msort(void *list, void **next, int(*cmp)(void *, void *)); + +static void memory_error() NORETURN; + +/******** From the file "action.h" *************************************/ +struct action *Action_new(); +struct action *Action_sort(); +void Action_add(); + +/********* From the file "assert.h" ************************************/ +void myassert() NORETURN; +#ifndef NDEBUG +# define assert(X) if(!(X))myassert(__FILE__,__LINE__) +#else +# define assert(X) +#endif + +/********** From the file "build.h" ************************************/ +void FindRulePrecedences(); +void FindFirstSets(); +void FindStates(); +void FindLinks(); +void FindFollowSets(); +void FindActions(); + +/********* From the file "configlist.h" *********************************/ +void Configlist_init(/* void */); +struct config *Configlist_add(/* struct rule *, int */); +struct config *Configlist_addbasis(/* struct rule *, int */); +void Configlist_closure(/* void */); +void Configlist_sort(/* void */); +void Configlist_sortbasis(/* void */); +struct config *Configlist_return(/* void */); +struct config *Configlist_basis(/* void */); +void Configlist_eat(/* struct config * */); +void Configlist_reset(/* void */); + +/********* From the file "error.h" ***************************************/ +void ErrorMsg(const char *, int,const char *, ...); + +/****** From the file "option.h" ******************************************/ +struct s_options { + enum { OPT_FLAG=1, OPT_INT, OPT_DBL, OPT_STR, + OPT_FFLAG, OPT_FINT, OPT_FDBL, OPT_FSTR} type; + char *label; + void *arg; + const char *message; +}; +int OptInit(/* char**,struct s_options*,FILE* */); +int OptNArgs(/* void */); +char *OptArg(/* int */); +void OptErr(/* int */); +void OptPrint(/* void */); + +/******** From the file "parse.h" *****************************************/ +void Parse(/* struct lemon *lemp */); + +/********* From the file "plink.h" ***************************************/ +struct plink *Plink_new(/* void */); +void Plink_add(/* struct plink **, struct config * */); +void Plink_copy(/* struct plink **, struct plink * */); +void Plink_delete(/* struct plink * */); + +/********** From the file "report.h" *************************************/ +void Reprint(/* struct lemon * */); +void ReportOutput(/* struct lemon * */); +void ReportTable(/* struct lemon * */); +void ReportHeader(/* struct lemon * */); +void CompressTables(/* struct lemon * */); + +/********** From the file "set.h" ****************************************/ +void SetSize(/* int N */); /* All sets will be of size N */ +char *SetNew(/* void */); /* A new set for element 0..N */ +void SetFree(/* char* */); /* Deallocate a set */ + +int SetAdd(/* char*,int */); /* Add element to a set */ +int SetUnion(/* char *A,char *B */); /* A <- A U B, thru element N */ + +#define SetFind(X,Y) (X[Y]) /* True if Y is in set X */ + +/********** From the file "struct.h" *************************************/ +/* +** Principal data structures for the LEMON parser generator. +*/ + +typedef enum {Bo_FALSE=0, Bo_TRUE} Boolean; + +/* Symbols (terminals and nonterminals) of the grammar are stored +** in the following: */ +struct symbol { + char *name; /* Name of the symbol */ + int index; /* Index number for this symbol */ + enum { + TERMINAL, + NONTERMINAL + } type; /* Symbols are all either TERMINALS or NTs */ + struct rule *rule; /* Linked list of rules of this (if an NT) */ + struct symbol *fallback; /* fallback token in case this token doesn't parse */ + int prec; /* Precedence if defined (-1 otherwise) */ + enum e_assoc { + LEFT, + RIGHT, + NONE, + UNK + } assoc; /* Associativity if predecence is defined */ + char *firstset; /* First-set for all rules of this symbol */ + Boolean lambda; /* True if NT and can generate an empty string */ + char *destructor; /* Code which executes whenever this symbol is + ** popped from the stack during error processing */ + int destructorln; /* Line number of destructor code */ + char *datatype; /* The data type of information held by this + ** object. Only used if type==NONTERMINAL */ + int dtnum; /* The data type number. In the parser, the value + ** stack is a union. The .yy%d element of this + ** union is the correct data type for this object */ +}; + +/* Each production rule in the grammar is stored in the following +** structure. */ +struct rule { + struct symbol *lhs; /* Left-hand side of the rule */ + char *lhsalias; /* Alias for the LHS (NULL if none) */ + int ruleline; /* Line number for the rule */ + int nrhs; /* Number of RHS symbols */ + struct symbol **rhs; /* The RHS symbols */ + char **rhsalias; /* An alias for each RHS symbol (NULL if none) */ + int line; /* Line number at which code begins */ + char *code; /* The code executed when this rule is reduced */ + struct symbol *precsym; /* Precedence symbol for this rule */ + int index; /* An index number for this rule */ + Boolean canReduce; /* True if this rule is ever reduced */ + struct rule *nextlhs; /* Next rule with the same LHS */ + struct rule *next; /* Next rule in the global list */ +}; + +/* A configuration is a production rule of the grammar together with +** a mark (dot) showing how much of that rule has been processed so far. +** Configurations also contain a follow-set which is a list of terminal +** symbols which are allowed to immediately follow the end of the rule. +** Every configuration is recorded as an instance of the following: */ +struct config { + struct rule *rp; /* The rule upon which the configuration is based */ + int dot; /* The parse point */ + char *fws; /* Follow-set for this configuration only */ + struct plink *fplp; /* Follow-set forward propagation links */ + struct plink *bplp; /* Follow-set backwards propagation links */ + struct state *stp; /* Pointer to state which contains this */ + enum { + COMPLETE, /* The status is used during followset and */ + INCOMPLETE /* shift computations */ + } status; + struct config *next; /* Next configuration in the state */ + struct config *bp; /* The next basis configuration */ +}; + +/* Every shift or reduce operation is stored as one of the following */ +struct action { + struct symbol *sp; /* The look-ahead symbol */ + enum e_action { + SHIFT, + ACCEPT, + REDUCE, + ERROR, + CONFLICT, /* Was a reduce, but part of a conflict */ + SH_RESOLVED, /* Was a shift. Precedence resolved conflict */ + RD_RESOLVED, /* Was reduce. Precedence resolved conflict */ + NOT_USED /* Deleted by compression */ + } type; + union { + struct state *stp; /* The new state, if a shift */ + struct rule *rp; /* The rule, if a reduce */ + } x; + struct action *next; /* Next action for this state */ + struct action *collide; /* Next action with the same hash */ +}; + +/* Each state of the generated parser's finite state machine +** is encoded as an instance of the following structure. */ +struct state { + struct config *bp; /* The basis configurations for this state */ + struct config *cfp; /* All configurations in this set */ + int index; /* Sequencial number for this state */ + struct action *ap; /* Array of actions for this state */ + int nTknAct, nNtAct; /* Number of actions on terminals and nonterminals */ + int iTknOfst, iNtOfst; /* yy_action[] offset for terminals and nonterms */ + int iDflt; /* Default action */ +}; +#define NO_OFFSET (-2147483647) + +/* A followset propagation link indicates that the contents of one +** configuration followset should be propagated to another whenever +** the first changes. */ +struct plink { + struct config *cfp; /* The configuration to which linked */ + struct plink *next; /* The next propagate link */ +}; + +/* The state vector for the entire parser generator is recorded as +** follows. (LEMON uses no global variables and makes little use of +** static variables. Fields in the following structure can be thought +** of as begin global variables in the program.) */ +struct lemon { + struct state **sorted; /* Table of states sorted by state number */ + struct rule *rule; /* List of all rules */ + int nstate; /* Number of states */ + int nrule; /* Number of rules */ + int nsymbol; /* Number of terminal and nonterminal symbols */ + int nterminal; /* Number of terminal symbols */ + struct symbol **symbols; /* Sorted array of pointers to symbols */ + int errorcnt; /* Number of errors */ + struct symbol *errsym; /* The error symbol */ + char *name; /* Name of the generated parser */ + char *arg; /* Declaration of the 3th argument to parser */ + char *tokentype; /* Type of terminal symbols in the parser stack */ + char *vartype; /* The default type of non-terminal symbols */ + char *start; /* Name of the start symbol for the grammar */ + char *stacksize; /* Size of the parser stack */ + char *include; /* Code to put at the start of the C file */ + int includeln; /* Line number for start of include code */ + char *error; /* Code to execute when an error is seen */ + int errorln; /* Line number for start of error code */ + char *overflow; /* Code to execute on a stack overflow */ + int overflowln; /* Line number for start of overflow code */ + char *failure; /* Code to execute on parser failure */ + int failureln; /* Line number for start of failure code */ + char *accept; /* Code to execute when the parser excepts */ + int acceptln; /* Line number for the start of accept code */ + char *extracode; /* Code appended to the generated file */ + int extracodeln; /* Line number for the start of the extra code */ + char *tokendest; /* Code to execute to destroy token data */ + int tokendestln; /* Line number for token destroyer code */ + char *vardest; /* Code for the default non-terminal destructor */ + int vardestln; /* Line number for default non-term destructor code*/ + char *filename; /* Name of the input file */ + char *tmplname; /* Name of the template file */ + char *outname; /* Name of the current output file */ + char *tokenprefix; /* A prefix added to token names in the .h file */ + int nconflict; /* Number of parsing conflicts */ + int tablesize; /* Size of the parse tables */ + int basisflag; /* Print only basis configurations */ + int has_fallback; /* True if any %fallback is seen in the grammer */ + char *argv0; /* Name of the program */ +}; + +#define MemoryCheck(X) if((X)==0){ \ + memory_error(); \ +} + +/**************** From the file "table.h" *********************************/ +/* +** All code in this file has been automatically generated +** from a specification in the file +** "table.q" +** by the associative array code building program "aagen". +** Do not edit this file! Instead, edit the specification +** file, then rerun aagen. +*/ +/* +** Code for processing tables in the LEMON parser generator. +*/ + +/* Routines for handling a strings */ + +char *Strsafe(); + +void Strsafe_init(/* void */); +int Strsafe_insert(/* char * */); +char *Strsafe_find(/* char * */); + +/* Routines for handling symbols of the grammar */ + +struct symbol *Symbol_new(); +int Symbolcmpp(/* struct symbol **, struct symbol ** */); +void Symbol_init(/* void */); +int Symbol_insert(/* struct symbol *, char * */); +struct symbol *Symbol_find(/* char * */); +struct symbol *Symbol_Nth(/* int */); +int Symbol_count(/* */); +int State_count(void); +struct symbol **Symbol_arrayof(/* */); + +/* Routines to manage the state table */ + +int Configcmp(/* struct config *, struct config * */); +struct state *State_new(); +void State_init(/* void */); +int State_insert(/* struct state *, struct config * */); +struct state *State_find(/* struct config * */); +struct state **State_arrayof(/* */); + +/* Routines used for efficiency in Configlist_add */ + +void Configtable_init(/* void */); +int Configtable_insert(/* struct config * */); +struct config *Configtable_find(/* struct config * */); +void Configtable_clear(/* int(*)(struct config *) */); +/****************** From the file "action.c" *******************************/ +/* +** Routines processing parser actions in the LEMON parser generator. +*/ + +/* Allocate a new parser action */ +struct action *Action_new(){ + static struct action *freelist = NULL; + struct action *new; + + if( freelist==NULL ){ + int i; + int amt = 100; + freelist = (struct action *)malloc( sizeof(struct action)*amt ); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new parser action."); + exit(1); + } + for(i=0; i<amt-1; i++) freelist[i].next = &freelist[i+1]; + freelist[amt-1].next = 0; + } + new = freelist; + freelist = freelist->next; + return new; +} + +/* Compare two actions */ +static int actioncmp(ap1,ap2) +struct action *ap1; +struct action *ap2; +{ + int rc; + rc = ap1->sp->index - ap2->sp->index; + if( rc==0 ) rc = (int)ap1->type - (int)ap2->type; + if( rc==0 ){ + assert( ap1->type==REDUCE || ap1->type==RD_RESOLVED || ap1->type==CONFLICT); + assert( ap2->type==REDUCE || ap2->type==RD_RESOLVED || ap2->type==CONFLICT); + rc = ap1->x.rp->index - ap2->x.rp->index; + } + return rc; +} + +/* Sort parser actions */ +struct action *Action_sort(ap) +struct action *ap; +{ + ap = (struct action *)msort(ap,(void **)&ap->next,actioncmp); + return ap; +} + +void Action_add(app,type,sp,arg) +struct action **app; +enum e_action type; +struct symbol *sp; +void *arg; +{ + struct action *new; + new = Action_new(); + new->next = *app; + *app = new; + new->type = type; + new->sp = sp; + if( type==SHIFT ){ + new->x.stp = (struct state *)arg; + }else{ + new->x.rp = (struct rule *)arg; + } +} +/********************** New code to implement the "acttab" module ***********/ +/* +** This module implements routines use to construct the yy_action[] table. +*/ + +/* +** The state of the yy_action table under construction is an instance of +** the following structure +*/ +typedef struct acttab acttab; +struct acttab { + int nAction; /* Number of used slots in aAction[] */ + int nActionAlloc; /* Slots allocated for aAction[] */ + struct { + int lookahead; /* Value of the lookahead token */ + int action; /* Action to take on the given lookahead */ + } *aAction, /* The yy_action[] table under construction */ + *aLookahead; /* A single new transaction set */ + int mnLookahead; /* Minimum aLookahead[].lookahead */ + int mnAction; /* Action associated with mnLookahead */ + int mxLookahead; /* Maximum aLookahead[].lookahead */ + int nLookahead; /* Used slots in aLookahead[] */ + int nLookaheadAlloc; /* Slots allocated in aLookahead[] */ +}; + +/* Return the number of entries in the yy_action table */ +#define acttab_size(X) ((X)->nAction) + +/* The value for the N-th entry in yy_action */ +#define acttab_yyaction(X,N) ((X)->aAction[N].action) + +/* The value for the N-th entry in yy_lookahead */ +#define acttab_yylookahead(X,N) ((X)->aAction[N].lookahead) + +/* Free all memory associated with the given acttab */ +/* +PRIVATE void acttab_free(acttab *p){ + free( p->aAction ); + free( p->aLookahead ); + free( p ); +} +*/ + +/* Allocate a new acttab structure */ +PRIVATE acttab *acttab_alloc(void){ + acttab *p = malloc( sizeof(*p) ); + if( p==0 ){ + fprintf(stderr,"Unable to allocate memory for a new acttab."); + exit(1); + } + memset(p, 0, sizeof(*p)); + return p; +} + +/* Add a new action to the current transaction set +*/ +PRIVATE void acttab_action(acttab *p, int lookahead, int action){ + if( p->nLookahead>=p->nLookaheadAlloc ){ + p->nLookaheadAlloc += 25; + p->aLookahead = realloc( p->aLookahead, + sizeof(p->aLookahead[0])*p->nLookaheadAlloc ); + if( p->aLookahead==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + } + if( p->nLookahead==0 ){ + p->mxLookahead = lookahead; + p->mnLookahead = lookahead; + p->mnAction = action; + }else{ + if( p->mxLookahead<lookahead ) p->mxLookahead = lookahead; + if( p->mnLookahead>lookahead ){ + p->mnLookahead = lookahead; + p->mnAction = action; + } + } + p->aLookahead[p->nLookahead].lookahead = lookahead; + p->aLookahead[p->nLookahead].action = action; + p->nLookahead++; +} + +/* +** Add the transaction set built up with prior calls to acttab_action() +** into the current action table. Then reset the transaction set back +** to an empty set in preparation for a new round of acttab_action() calls. +** +** Return the offset into the action table of the new transaction. +*/ +PRIVATE int acttab_insert(acttab *p){ + int i, j, k, n; + assert( p->nLookahead>0 ); + + /* Make sure we have enough space to hold the expanded action table + ** in the worst case. The worst case occurs if the transaction set + ** must be appended to the current action table + */ + n = p->mxLookahead + 1; + if( p->nAction + n >= p->nActionAlloc ){ + int oldAlloc = p->nActionAlloc; + p->nActionAlloc = p->nAction + n + p->nActionAlloc + 20; + p->aAction = realloc( p->aAction, + sizeof(p->aAction[0])*p->nActionAlloc); + if( p->aAction==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=oldAlloc; i<p->nActionAlloc; i++){ + p->aAction[i].lookahead = -1; + p->aAction[i].action = -1; + } + } + + /* Scan the existing action table looking for an offset where we can + ** insert the current transaction set. Fall out of the loop when that + ** offset is found. In the worst case, we fall out of the loop when + ** i reaches p->nAction, which means we append the new transaction set. + ** + ** i is the index in p->aAction[] where p->mnLookahead is inserted. + */ + for(i=0; i<p->nAction+p->mnLookahead; i++){ + if( p->aAction[i].lookahead<0 ){ + for(j=0; j<p->nLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 ) break; + if( p->aAction[k].lookahead>=0 ) break; + } + if( j<p->nLookahead ) continue; + for(j=0; j<p->nAction; j++){ + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) break; + } + if( j==p->nAction ){ + break; /* Fits in empty slots */ + } + }else if( p->aAction[i].lookahead==p->mnLookahead ){ + if( p->aAction[i].action!=p->mnAction ) continue; + for(j=0; j<p->nLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + if( k<0 || k>=p->nAction ) break; + if( p->aLookahead[j].lookahead!=p->aAction[k].lookahead ) break; + if( p->aLookahead[j].action!=p->aAction[k].action ) break; + } + if( j<p->nLookahead ) continue; + n = 0; + for(j=0; j<p->nAction; j++){ + if( p->aAction[j].lookahead<0 ) continue; + if( p->aAction[j].lookahead==j+p->mnLookahead-i ) n++; + } + if( n==p->nLookahead ){ + break; /* Same as a prior transaction set */ + } + } + } + /* Insert transaction set at index i. */ + for(j=0; j<p->nLookahead; j++){ + k = p->aLookahead[j].lookahead - p->mnLookahead + i; + p->aAction[k] = p->aLookahead[j]; + if( k>=p->nAction ) p->nAction = k+1; + } + p->nLookahead = 0; + + /* Return the offset that is added to the lookahead in order to get the + ** index into yy_action of the action */ + return i - p->mnLookahead; +} + +/********************** From the file "assert.c" ****************************/ +/* +** A more efficient way of handling assertions. +*/ +void myassert(file,line) +char *file; +int line; +{ + fprintf(stderr,"Assertion failed on line %d of file \"%s\"\n",line,file); + exit(1); +} +/********************** From the file "build.c" *****************************/ +/* +** Routines to construction the finite state machine for the LEMON +** parser generator. +*/ + +/* Find a precedence symbol of every rule in the grammar. +** +** Those rules which have a precedence symbol coded in the input +** grammar using the "[symbol]" construct will already have the +** rp->precsym field filled. Other rules take as their precedence +** symbol the first RHS symbol with a defined precedence. If there +** are not RHS symbols with a defined precedence, the precedence +** symbol field is left blank. +*/ +void FindRulePrecedences(xp) +struct lemon *xp; +{ + struct rule *rp; + for(rp=xp->rule; rp; rp=rp->next){ + if( rp->precsym==0 ){ + int i; + for(i=0; i<rp->nrhs; i++){ + if( rp->rhs[i]->prec>=0 ){ + rp->precsym = rp->rhs[i]; + break; + } + } + } + } + return; +} + +/* Find all nonterminals which will generate the empty string. +** Then go back and compute the first sets of every nonterminal. +** The first set is the set of all terminal symbols which can begin +** a string generated by that nonterminal. +*/ +void FindFirstSets(lemp) +struct lemon *lemp; +{ + int i; + struct rule *rp; + int progress; + + for(i=0; i<lemp->nsymbol; i++){ + lemp->symbols[i]->lambda = Bo_FALSE; + } + for(i=lemp->nterminal; i<lemp->nsymbol; i++){ + lemp->symbols[i]->firstset = SetNew(); + } + + /* First compute all lambdas */ + do{ + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->lhs->lambda ) continue; + for(i=0; i<rp->nrhs; i++){ + if( rp->rhs[i]->lambda==Bo_FALSE ) break; + } + if( i==rp->nrhs ){ + rp->lhs->lambda = Bo_TRUE; + progress = 1; + } + } + }while( progress ); + + /* Now compute all first sets */ + do{ + struct symbol *s1, *s2; + progress = 0; + for(rp=lemp->rule; rp; rp=rp->next){ + s1 = rp->lhs; + for(i=0; i<rp->nrhs; i++){ + s2 = rp->rhs[i]; + if( s2->type==TERMINAL ){ + progress += SetAdd(s1->firstset,s2->index); + break; + }else if( s1==s2 ){ + if( s1->lambda==Bo_FALSE ) break; + }else{ + progress += SetUnion(s1->firstset,s2->firstset); + if( s2->lambda==Bo_FALSE ) break; + } + } + } + }while( progress ); + return; +} + +/* Compute all LR(0) states for the grammar. Links +** are added to between some states so that the LR(1) follow sets +** can be computed later. +*/ +PRIVATE struct state *getstate(/* struct lemon * */); /* forward reference */ +void FindStates(lemp) +struct lemon *lemp; +{ + struct symbol *sp; + struct rule *rp; + + Configlist_init(); + + /* Find the start symbol */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ){ + ErrorMsg(lemp->filename,0, +"The specified start symbol \"%s\" is not \ +in a nonterminal of the grammar. \"%s\" will be used as the start \ +symbol instead.",lemp->start,lemp->rule->lhs->name); + lemp->errorcnt++; + sp = lemp->rule->lhs; + } + }else{ + sp = lemp->rule->lhs; + } + + /* Make sure the start symbol doesn't occur on the right-hand side of + ** any rule. Report an error if it does. (YACC would generate a new + ** start symbol in this case.) */ + for(rp=lemp->rule; rp; rp=rp->next){ + int i; + for(i=0; i<rp->nrhs; i++){ + if( rp->rhs[i]==sp ){ + ErrorMsg(lemp->filename,0, +"The start symbol \"%s\" occurs on the \ +right-hand side of a rule. This will result in a parser which \ +does not work properly.",sp->name); + lemp->errorcnt++; + } + } + } + + /* The basis configuration set for the first state + ** is all rules which have the start symbol as their + ** left-hand side */ + for(rp=sp->rule; rp; rp=rp->nextlhs){ + struct config *newcfp; + newcfp = Configlist_addbasis(rp,0); + SetAdd(newcfp->fws,0); + } + + /* Compute the first state. All other states will be + ** computed automatically during the computation of the first one. + ** The returned pointer to the first state is not used. */ + (void)getstate(lemp); + return; +} + +/* Return a pointer to a state which is described by the configuration +** list which has been built from calls to Configlist_add. +*/ +PRIVATE void buildshifts(/* struct lemon *, struct state * */); /* Forwd ref */ +PRIVATE struct state *getstate(lemp) +struct lemon *lemp; +{ + struct config *cfp, *bp; + struct state *stp; + + /* Extract the sorted basis of the new state. The basis was constructed + ** by prior calls to "Configlist_addbasis()". */ + Configlist_sortbasis(); + bp = Configlist_basis(); + + /* Get a state with the same basis */ + stp = State_find(bp); + if( stp ){ + /* A state with the same basis already exists! Copy all the follow-set + ** propagation links from the state under construction into the + ** preexisting state, then return a pointer to the preexisting state */ + struct config *x, *y; + for(x=bp, y=stp->bp; x && y; x=x->bp, y=y->bp){ + Plink_copy(&y->bplp,x->bplp); + Plink_delete(x->fplp); + x->fplp = x->bplp = 0; + } + cfp = Configlist_return(); + Configlist_eat(cfp); + }else{ + /* This really is a new state. Construct all the details */ + Configlist_closure(lemp); /* Compute the configuration closure */ + Configlist_sort(); /* Sort the configuration closure */ + cfp = Configlist_return(); /* Get a pointer to the config list */ + stp = State_new(); /* A new state structure */ + MemoryCheck(stp); + stp->bp = bp; /* Remember the configuration basis */ + stp->cfp = cfp; /* Remember the configuration closure */ + stp->index = lemp->nstate++; /* Every state gets a sequence number */ + stp->ap = 0; /* No actions, yet. */ + State_insert(stp,stp->bp); /* Add to the state table */ + buildshifts(lemp,stp); /* Recursively compute successor states */ + } + return stp; +} + +/* Construct all successor states to the given state. A "successor" +** state is any state which can be reached by a shift action. +*/ +PRIVATE void buildshifts(lemp,stp) +struct lemon *lemp; +struct state *stp; /* The state from which successors are computed */ +{ + struct config *cfp; /* For looping thru the config closure of "stp" */ + struct config *bcfp; /* For the inner loop on config closure of "stp" */ + struct config *new; /* */ + struct symbol *sp; /* Symbol following the dot in configuration "cfp" */ + struct symbol *bsp; /* Symbol following the dot in configuration "bcfp" */ + struct state *newstp; /* A pointer to a successor state */ + + /* Each configuration becomes complete after it contibutes to a successor + ** state. Initially, all configurations are incomplete */ + for(cfp=stp->cfp; cfp; cfp=cfp->next) cfp->status = INCOMPLETE; + + /* Loop through all configurations of the state "stp" */ + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; /* Already used by inner loop */ + if( cfp->dot>=cfp->rp->nrhs ) continue; /* Can't shift this config */ + Configlist_reset(); /* Reset the new config set */ + sp = cfp->rp->rhs[cfp->dot]; /* Symbol after the dot */ + + /* For every configuration in the state "stp" which has the symbol "sp" + ** following its dot, add the same configuration to the basis set under + ** construction but with the dot shifted one symbol to the right. */ + for(bcfp=cfp; bcfp; bcfp=bcfp->next){ + if( bcfp->status==COMPLETE ) continue; /* Already used */ + if( bcfp->dot>=bcfp->rp->nrhs ) continue; /* Can't shift this one */ + bsp = bcfp->rp->rhs[bcfp->dot]; /* Get symbol after dot */ + if( bsp!=sp ) continue; /* Must be same as for "cfp" */ + bcfp->status = COMPLETE; /* Mark this config as used */ + new = Configlist_addbasis(bcfp->rp,bcfp->dot+1); + Plink_add(&new->bplp,bcfp); + } + + /* Get a pointer to the state described by the basis configuration set + ** constructed in the preceding loop */ + newstp = getstate(lemp); + + /* The state "newstp" is reached from the state "stp" by a shift action + ** on the symbol "sp" */ + Action_add(&stp->ap,SHIFT,sp,newstp); + } +} + +/* +** Construct the propagation links +*/ +void FindLinks(lemp) +struct lemon *lemp; +{ + int i; + struct config *cfp, *other; + struct state *stp; + struct plink *plp; + + /* Housekeeping detail: + ** Add to every propagate link a pointer back to the state to + ** which the link is attached. */ + for(i=0; i<lemp->nstate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + cfp->stp = stp; + } + } + + /* Convert all backlinks into forward links. Only the forward + ** links are used in the follow-set computation. */ + for(i=0; i<lemp->nstate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + for(plp=cfp->bplp; plp; plp=plp->next){ + other = plp->cfp; + Plink_add(&other->fplp,cfp); + } + } + } +} + +/* Compute all followsets. +** +** A followset is the set of all symbols which can come immediately +** after a configuration. +*/ +void FindFollowSets(lemp) +struct lemon *lemp; +{ + int i; + struct config *cfp; + struct state *stp; + struct plink *plp; + int progress; + int change; + + for(i=0; i<lemp->nstate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + cfp->status = INCOMPLETE; + } + } + + do{ + progress = 0; + for(i=0; i<lemp->nstate; i++){ + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ + if( cfp->status==COMPLETE ) continue; + for(plp=cfp->fplp; plp; plp=plp->next){ + change = SetUnion(plp->cfp->fws,cfp->fws); + if( change ){ + plp->cfp->status = INCOMPLETE; + progress = 1; + } + } + cfp->status = COMPLETE; + } + } + }while( progress ); +} + +static int resolve_conflict(); + +/* Compute the reduce actions, and resolve conflicts. +*/ +void FindActions(lemp) +struct lemon *lemp; +{ + int i,j; + struct config *cfp; + struct symbol *sp; + struct rule *rp; + + /* Add all of the reduce actions + ** A reduce action is added for each element of the followset of + ** a configuration which has its dot at the extreme right. + */ + for(i=0; i<lemp->nstate; i++){ /* Loop over all states */ + struct state *stp; + stp = lemp->sorted[i]; + for(cfp=stp->cfp; cfp; cfp=cfp->next){ /* Loop over all configurations */ + if( cfp->rp->nrhs==cfp->dot ){ /* Is dot at extreme right? */ + for(j=0; j<lemp->nterminal; j++){ + if( SetFind(cfp->fws,j) ){ + /* Add a reduce action to the state "stp" which will reduce by the + ** rule "cfp->rp" if the lookahead symbol is "lemp->symbols[j]" */ + Action_add(&stp->ap,REDUCE,lemp->symbols[j],cfp->rp); + } + } + } + } + } + + /* Add the accepting token */ + if( lemp->start ){ + sp = Symbol_find(lemp->start); + if( sp==0 ) sp = lemp->rule->lhs; + }else{ + sp = lemp->rule->lhs; + } + /* Add to the first state (which is always the starting state of the + ** finite state machine) an action to ACCEPT if the lookahead is the + ** start nonterminal. */ + if (lemp->nstate) { /*(should always be true)*/ + struct state *stp; + stp = lemp->sorted[0]; + Action_add(&stp->ap,ACCEPT,sp,0); + } + + /* Resolve conflicts */ + for(i=0; i<lemp->nstate; i++){ + struct action *ap, *nap; + struct state *stp; + stp = lemp->sorted[i]; + assert( stp->ap ); + stp->ap = Action_sort(stp->ap); + for(ap=stp->ap; ap && ap->next; ap=ap->next){ + for(nap=ap->next; nap && nap->sp==ap->sp; nap=nap->next){ + /* The two actions "ap" and "nap" have the same lookahead. + ** Figure out which one should be used */ + lemp->nconflict += resolve_conflict(ap,nap,lemp->errsym); + } + } + } + + /* Report an error for each rule that can never be reduced. */ + for(rp=lemp->rule; rp; rp=rp->next) rp->canReduce = Bo_FALSE; + for(i=0; i<lemp->nstate; i++){ + struct action *ap; + for(ap=lemp->sorted[i]->ap; ap; ap=ap->next){ + if( ap->type==REDUCE ) ap->x.rp->canReduce = Bo_TRUE; + } + } + for(rp=lemp->rule; rp; rp=rp->next){ + if( rp->canReduce ) continue; + ErrorMsg(lemp->filename,rp->ruleline,"This rule can not be reduced.\n"); + lemp->errorcnt++; + } +} + +/* Resolve a conflict between the two given actions. If the +** conflict can't be resolve, return non-zero. +** +** NO LONGER TRUE: +** To resolve a conflict, first look to see if either action +** is on an error rule. In that case, take the action which +** is not associated with the error rule. If neither or both +** actions are associated with an error rule, then try to +** use precedence to resolve the conflict. +** +** If either action is a SHIFT, then it must be apx. This +** function won't work if apx->type==REDUCE and apy->type==SHIFT. +*/ +static int resolve_conflict(apx,apy,errsym) +struct action *apx; +struct action *apy; +struct symbol *errsym; /* The error symbol (if defined. NULL otherwise) */ +{ + struct symbol *spx, *spy; + int errcnt = 0; + UNUSED(errsym); + assert( apx->sp==apy->sp ); /* Otherwise there would be no conflict */ + if( apx->type==SHIFT && apy->type==REDUCE ){ + spx = apx->sp; + spy = apy->x.rp->precsym; + if( spy==0 || spx->prec<0 || spy->prec<0 ){ + /* Not enough precedence information. */ + apy->type = CONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ /* Lower precedence wins */ + apy->type = RD_RESOLVED; + }else if( spx->prec<spy->prec ){ + apx->type = SH_RESOLVED; + }else if( spx->prec==spy->prec && spx->assoc==RIGHT ){ /* Use operator */ + apy->type = RD_RESOLVED; /* associativity */ + }else if( spx->prec==spy->prec && spx->assoc==LEFT ){ /* to break tie */ + apx->type = SH_RESOLVED; + }else{ + assert( spx->prec==spy->prec && spx->assoc==NONE ); + apy->type = CONFLICT; + errcnt++; + } + }else if( apx->type==REDUCE && apy->type==REDUCE ){ + spx = apx->x.rp->precsym; + spy = apy->x.rp->precsym; + if( spx==0 || spy==0 || spx->prec<0 || + spy->prec<0 || spx->prec==spy->prec ){ + apy->type = CONFLICT; + errcnt++; + }else if( spx->prec>spy->prec ){ + apy->type = RD_RESOLVED; + }else if( spx->prec<spy->prec ){ + apx->type = RD_RESOLVED; + } + }else{ + assert( + apx->type==SH_RESOLVED || + apx->type==RD_RESOLVED || + apx->type==CONFLICT || + apy->type==SH_RESOLVED || + apy->type==RD_RESOLVED || + apy->type==CONFLICT + ); + /* The REDUCE/SHIFT case cannot happen because SHIFTs come before + ** REDUCEs on the list. If we reach this point it must be because + ** the parser conflict had already been resolved. */ + } + return errcnt; +} +/********************* From the file "configlist.c" *************************/ +/* +** Routines to processing a configuration list and building a state +** in the LEMON parser generator. +*/ + +static struct config *freelist = 0; /* List of free configurations */ +static struct config *current = 0; /* Top of list of configurations */ +static struct config **currentend = 0; /* Last on list of configs */ +static struct config *basis = 0; /* Top of list of basis configs */ +static struct config **basisend = 0; /* End of list of basis configs */ + +/* Return a pointer to a new configuration */ +PRIVATE struct config *newconfig(){ + struct config *new; + if( freelist==0 ){ + int i; + int amt = 3; + freelist = (struct config *)malloc( sizeof(struct config)*amt ); + if( freelist==0 ){ + fprintf(stderr,"Unable to allocate memory for a new configuration."); + exit(1); + } + for(i=0; i<amt-1; i++) freelist[i].next = &freelist[i+1]; + freelist[amt-1].next = 0; + } + new = freelist; + freelist = freelist->next; + return new; +} + +/* The configuration "old" is no longer used */ +PRIVATE void deleteconfig(old) +struct config *old; +{ + old->next = freelist; + freelist = old; +} + +/* Initialized the configuration list builder */ +void Configlist_init(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_init(); + return; +} + +/* Initialized the configuration list builder */ +void Configlist_reset(){ + current = 0; + currentend = ¤t; + basis = 0; + basisend = &basis; + Configtable_clear(0); + return; +} + +/* Add another configuration to the configuration list */ +struct config *Configlist_add(rp,dot) +struct rule *rp; /* The rule */ +int dot; /* Index into the RHS of the rule where the dot goes */ +{ + struct config *cfp, model; + + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + Configtable_insert(cfp); + } + return cfp; +} + +/* Add a basis configuration to the configuration list */ +struct config *Configlist_addbasis(rp,dot) +struct rule *rp; +int dot; +{ + struct config *cfp, model; + + assert( basisend!=0 ); + assert( currentend!=0 ); + model.rp = rp; + model.dot = dot; + cfp = Configtable_find(&model); + if( cfp==0 ){ + cfp = newconfig(); + cfp->rp = rp; + cfp->dot = dot; + cfp->fws = SetNew(); + cfp->stp = 0; + cfp->fplp = cfp->bplp = 0; + cfp->next = 0; + cfp->bp = 0; + *currentend = cfp; + currentend = &cfp->next; + *basisend = cfp; + basisend = &cfp->bp; + Configtable_insert(cfp); + } + return cfp; +} + +/* Compute the closure of the configuration list */ +void Configlist_closure(lemp) +struct lemon *lemp; +{ + struct config *cfp, *newcfp; + struct rule *rp, *newrp; + struct symbol *sp, *xsp; + int i, dot; + + assert( currentend!=0 ); + for(cfp=current; cfp; cfp=cfp->next){ + rp = cfp->rp; + dot = cfp->dot; + if( dot>=rp->nrhs ) continue; + sp = rp->rhs[dot]; + if( sp->type==NONTERMINAL ){ + if( sp->rule==0 && sp!=lemp->errsym ){ + ErrorMsg(lemp->filename,rp->line,"Nonterminal \"%s\" has no rules.", + sp->name); + lemp->errorcnt++; + } + for(newrp=sp->rule; newrp; newrp=newrp->nextlhs){ + newcfp = Configlist_add(newrp,0); + for(i=dot+1; i<rp->nrhs; i++){ + xsp = rp->rhs[i]; + if( xsp->type==TERMINAL ){ + SetAdd(newcfp->fws,xsp->index); + break; + }else{ + SetUnion(newcfp->fws,xsp->firstset); + if( xsp->lambda==Bo_FALSE ) break; + } + } + if( i==rp->nrhs ) Plink_add(&cfp->fplp,newcfp); + } + } + } + return; +} + +/* Sort the configuration list */ +void Configlist_sort(){ + current = (struct config *)msort(current,(void **)&(current->next),Configcmp); + currentend = 0; + return; +} + +/* Sort the basis configuration list */ +void Configlist_sortbasis(){ + basis = (struct config *)msort(current,(void **)&(current->bp),Configcmp); + basisend = 0; + return; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_return(){ + struct config *old; + old = current; + current = 0; + currentend = 0; + return old; +} + +/* Return a pointer to the head of the configuration list and +** reset the list */ +struct config *Configlist_basis(){ + struct config *old; + old = basis; + basis = 0; + basisend = 0; + return old; +} + +/* Free all elements of the given configuration list */ +void Configlist_eat(cfp) +struct config *cfp; +{ + struct config *nextcfp; + for(; cfp; cfp=nextcfp){ + nextcfp = cfp->next; + assert( cfp->fplp==0 ); + assert( cfp->bplp==0 ); + if( cfp->fws ) SetFree(cfp->fws); + deleteconfig(cfp); + } + return; +} +/***************** From the file "error.c" *********************************/ +/* +** Code for printing error message. +*/ + +/* Find a good place to break "msg" so that its length is at least "min" +** but no more than "max". Make the point as close to max as possible. +*/ +static int findbreak(msg,min,max) +char *msg; +int min; +int max; +{ + int i,spot; + char c; + for(i=spot=min; i<=max; i++){ + c = msg[i]; + if( c=='\t' ) msg[i] = ' '; + if( c=='\n' ){ msg[i] = ' '; spot = i; break; } + if( c==0 ){ spot = i; break; } + if( c=='-' && i<max-1 ) spot = i+1; + if( c==' ' ) spot = i; + } + return spot; +} + +/* +** The error message is split across multiple lines if necessary. The +** splits occur at a space, if there is a space available near the end +** of the line. +*/ +#define ERRMSGSIZE 10000 /* Hope this is big enough. No way to error check */ +#define LINEWIDTH 79 /* Max width of any output line */ +#define PREFIXLIMIT 30 /* Max width of the prefix on each line */ +void ErrorMsg(const char *filename, int lineno, const char *format, ...){ + char errmsg[ERRMSGSIZE]; + char prefix[PREFIXLIMIT+10]; + int errmsgsize; + int prefixsize; + int availablewidth; + va_list ap; + int end, restart, base; + + va_start(ap, format); + /* Prepare a prefix to be prepended to every output line */ + if( lineno>0 ){ + sprintf(prefix,"%.*s:%d: ",PREFIXLIMIT-10,filename,lineno); + }else{ + sprintf(prefix,"%.*s: ",PREFIXLIMIT-10,filename); + } + prefixsize = strlen(prefix); + availablewidth = LINEWIDTH - prefixsize; + + /* Generate the error message */ + vsprintf(errmsg,format,ap); + va_end(ap); + errmsgsize = strlen(errmsg); + /* Remove trailing '\n's from the error message. */ + while( errmsgsize>0 && errmsg[errmsgsize-1]=='\n' ){ + errmsg[--errmsgsize] = 0; + } + + /* Print the error message */ + base = 0; + while( errmsg[base]!=0 ){ + end = restart = findbreak(&errmsg[base],0,availablewidth); + restart += base; + while( errmsg[restart]==' ' ) restart++; + fprintf(stdout,"%s%.*s\n",prefix,end,&errmsg[base]); + base = restart; + } +} +/**************** From the file "main.c" ************************************/ +/* +** Main program file for the LEMON parser generator. +*/ + +/* Report an out-of-memory condition and abort. This function +** is used mostly by the "MemoryCheck" macro in struct.h +*/ +void memory_error() { + fprintf(stderr,"Out of memory. Aborting...\n"); + exit(1); +} + +static const char* out_dir = "."; +/* The main program. Parse the command line and do it... */ +int main(argc,argv) +int argc; +char **argv; +{ + static int version = 0; + static int rpflag = 0; + static int basisflag = 0; + static int compress = 0; + static int quiet = 0; + static int statistics = 0; + static int mhflag = 0; + static struct s_options options[] = { + {OPT_FLAG, "b", (char*)&basisflag, "Print only the basis in report."}, + {OPT_FLAG, "c", (char*)&compress, "Don't compress the action table."}, + {OPT_FLAG, "g", (char*)&rpflag, "Print grammar without actions."}, + {OPT_FLAG, "m", (char*)&mhflag, "Output a makeheaders compatible file"}, + {OPT_FLAG, "q", (char*)&quiet, "(Quiet) Don't print the report file."}, + {OPT_FLAG, "s", (char*)&statistics, "Print parser stats to standard output."}, + {OPT_FLAG, "x", (char*)&version, "Print the version number."}, + {OPT_STR, "o", (char*)&out_dir, "Customize output directory."}, + {OPT_FLAG,0,0,0} + }; + int i; + struct lemon lem; + char *def_tmpl_name = "lempar.c"; + + UNUSED(argc); + OptInit(argv,options,stderr); + if( version ){ + printf("Lemon version 1.0\n"); + exit(0); + } + if( OptNArgs() < 1 ){ + fprintf(stderr,"Exactly one filename argument is required.\n"); + exit(1); + } + lem.errorcnt = 0; + + /* Initialize the machine */ + Strsafe_init(); + Symbol_init(); + State_init(); + lem.argv0 = argv[0]; + lem.filename = OptArg(0); + lem.tmplname = (OptNArgs() == 2) ? OptArg(1) : def_tmpl_name; + lem.basisflag = basisflag; + lem.has_fallback = 0; + lem.nconflict = 0; + lem.name = lem.include = lem.arg = lem.tokentype = lem.start = 0; + lem.vartype = 0; + lem.stacksize = 0; + lem.error = lem.overflow = lem.failure = lem.accept = lem.tokendest = + lem.tokenprefix = lem.outname = lem.extracode = 0; + lem.vardest = 0; + lem.tablesize = 0; + Symbol_new("$"); + lem.errsym = Symbol_new("error"); + + /* Parse the input file */ + Parse(&lem); + if( lem.errorcnt ) exit(lem.errorcnt); + if( lem.rule==0 ){ + fprintf(stderr,"Empty grammar.\n"); + exit(1); + } + + /* Count and index the symbols of the grammar */ + Symbol_new("{default}"); + lem.nsymbol = Symbol_count(); + lem.symbols = Symbol_arrayof(); + for(i=0; i<lem.nsymbol; i++) lem.symbols[i]->index = i; + qsort(lem.symbols,lem.nsymbol,sizeof(struct symbol*), + (int(*)())Symbolcmpp); + for(i=0; i<lem.nsymbol; i++) lem.symbols[i]->index = i; + for(i=1; i<lem.nsymbol && isupper(lem.symbols[i]->name[0]); i++); + lem.nsymbol--; /*(do not count "{default}")*/ + lem.nterminal = i; + + /* Generate a reprint of the grammar, if requested on the command line */ + if( rpflag ){ + Reprint(&lem); + }else{ + /* Initialize the size for all follow and first sets */ + SetSize(lem.nterminal); + + /* Find the precedence for every production rule (that has one) */ + FindRulePrecedences(&lem); + + /* Compute the lambda-nonterminals and the first-sets for every + ** nonterminal */ + FindFirstSets(&lem); + + /* Compute all LR(0) states. Also record follow-set propagation + ** links so that the follow-set can be computed later */ + lem.nstate = 0; + FindStates(&lem); + lem.nstate = State_count(); + lem.sorted = State_arrayof(); + + /* Tie up loose ends on the propagation links */ + FindLinks(&lem); + + /* Compute the follow set of every reducible configuration */ + FindFollowSets(&lem); + + /* Compute the action tables */ + FindActions(&lem); + + /* Compress the action tables */ + if( compress==0 ) CompressTables(&lem); + + /* Generate a report of the parser generated. (the "y.output" file) */ + if( !quiet ) ReportOutput(&lem); + + /* Generate the source code for the parser */ + ReportTable(&lem, mhflag); + + /* Produce a header file for use by the scanner. (This step is + ** omitted if the "-m" option is used because makeheaders will + ** generate the file for us.) */ + if( !mhflag ) ReportHeader(&lem); + } + if( statistics ){ + printf("Parser statistics: %d terminals, %d nonterminals, %d rules\n", + lem.nterminal, lem.nsymbol - lem.nterminal, lem.nrule); + printf(" %d states, %d parser table entries, %d conflicts\n", + lem.nstate, lem.tablesize, lem.nconflict); + } + if( lem.nconflict ){ + fprintf(stderr,"%d parsing conflicts.\n",lem.nconflict); + } + exit(lem.errorcnt + lem.nconflict); +} +/******************** From the file "msort.c" *******************************/ +/* +** A generic merge-sort program. +** +** USAGE: +** Let "ptr" be a pointer to some structure which is at the head of +** a null-terminated list. Then to sort the list call: +** +** ptr = msort(ptr,&(ptr->next),cmpfnc); +** +** In the above, "cmpfnc" is a pointer to a function which compares +** two instances of the structure and returns an integer, as in +** strcmp. The second argument is a pointer to the pointer to the +** second element of the linked list. This address is used to compute +** the offset to the "next" field within the structure. The offset to +** the "next" field must be constant for all structures in the list. +** +** The function returns a new pointer which is the head of the list +** after sorting. +** +** ALGORITHM: +** Merge-sort. +*/ + +/* +** Return a pointer to the next structure in the linked list. +*/ +#define NEXT(A) (*(char**)(((unsigned long)A)+offset)) + +/* +** Inputs: +** a: A sorted, null-terminated linked list. (May be null). +** b: A sorted, null-terminated linked list. (May be null). +** cmp: A pointer to the comparison function. +** offset: Offset in the structure to the "next" field. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** of both a and b. +** +** Side effects: +** The "next" pointers for elements in the lists a and b are +** changed. +*/ +static char *merge(a,b,cmp,offset) +char *a; +char *b; +int (*cmp)(); +int offset; +{ + char *ptr, *head; + + if( a==0 ){ + head = b; + }else if( b==0 ){ + head = a; + }else{ + if( (*cmp)(a,b)<0 ){ + ptr = a; + a = NEXT(a); + }else{ + ptr = b; + b = NEXT(b); + } + head = ptr; + while( a && b ){ + if( (*cmp)(a,b)<0 ){ + NEXT(ptr) = a; + ptr = a; + a = NEXT(a); + }else{ + NEXT(ptr) = b; + ptr = b; + b = NEXT(b); + } + } + if( a ) NEXT(ptr) = a; + else NEXT(ptr) = b; + } + return head; +} + +/* +** Inputs: +** list: Pointer to a singly-linked list of structures. +** next: Pointer to pointer to the second element of the list. +** cmp: A comparison function. +** +** Return Value: +** A pointer to the head of a sorted list containing the elements +** orginally in list. +** +** Side effects: +** The "next" pointers for elements in list are changed. +*/ +#define LISTSIZE 30 +void *msort(void *list, void **next, int(*cmp)(void *, void *)) +{ + unsigned long offset; + char *ep; + char *set[LISTSIZE]; + int i; + offset = (unsigned long)next - (unsigned long)list; + for(i=0; i<LISTSIZE; i++) set[i] = 0; + while( list ){ + ep = list; + list = NEXT(list); + NEXT(ep) = 0; + for(i=0; i<LISTSIZE-1 && set[i]!=0; i++){ + ep = merge(ep,set[i],cmp,offset); + set[i] = 0; + } + set[i] = ep; + } + ep = 0; + for(i=0; i<LISTSIZE; i++) if( set[i] ) ep = merge(ep,set[i],cmp,offset); + return ep; +} +/************************ From the file "option.c" **************************/ +static char **argv; +static struct s_options *op; +static FILE *errstream; + +#define ISOPT(X) ((X)[0]=='-'||(X)[0]=='+'||strchr((X),'=')!=0) + +/* +** Print the command line with a carrot pointing to the k-th character +** of the n-th field. +*/ +static void errline(n,k,err) +int n; +int k; +FILE *err; +{ + int spcnt = 0, i; + if( argv[0] ) { + fprintf(err,"%s",argv[0]); + spcnt += strlen(argv[0]) + 1; + } + for(i=1; i<n && argv[i]; i++){ + fprintf(err," %s",argv[i]); + spcnt += strlen(argv[i]) + 1; + } + spcnt += k; + for(; argv[i]; i++) fprintf(err," %s",argv[i]); + if( spcnt<20 ){ + fprintf(err,"\n%*s^-- here\n",spcnt,""); + }else{ + fprintf(err,"\n%*shere --^\n",spcnt-7,""); + } +} + +/* +** Return the index of the N-th non-switch argument. Return -1 +** if N is out of range. +*/ +static int argindex(n) +int n; +{ + int i; + int dashdash = 0; + if( argv!=0 && *argv!=0 ){ + for(i=1; argv[i]; i++){ + if( dashdash || !ISOPT(argv[i]) ){ + if( n==0 ) return i; + n--; + } + if( strcmp(argv[i],"--")==0 ) dashdash = 1; + } + } + return -1; +} + +static char emsg[] = "Command line syntax error: "; + +/* +** Process a flag command line argument. +*/ +static int handleflags(i,err) +int i; +FILE *err; +{ + int v; + int errcnt = 0; + int j; + for(j=0; op[j].label; j++){ + if( strcmp(&argv[i][1],op[j].label)==0 ) break; + } + v = argv[i][0]=='-' ? 1 : 0; + if( op[j].label==0 ){ + if( err ){ + fprintf(err,"%sundefined option.\n",emsg); + errline(i,1,err); + } + errcnt++; + }else if( op[j].type==OPT_FLAG ){ + *((int*)op[j].arg) = v; + }else if( op[j].type==OPT_FFLAG ){ + (*(void(*)())(intptr_t)(op[j].arg))(v); + }else{ + if( err ){ + fprintf(err,"%smissing argument on switch.\n",emsg); + errline(i,1,err); + } + errcnt++; + } + return errcnt; +} + +/* +** Process a command line switch which has an argument. +*/ +static int handleswitch(i,err) +int i; +FILE *err; +{ + int lv = 0; + double dv = 0.0; + char *sv = 0, *end; + char *cp; + int j; + int errcnt = 0; + cp = strchr(argv[i],'='); + *cp = 0; + for(j=0; op[j].label; j++){ + if( strcmp(argv[i],op[j].label)==0 ) break; + } + *cp = '='; + if( op[j].label==0 ){ + if( err ){ + fprintf(err,"%sundefined option.\n",emsg); + errline(i,0,err); + } + errcnt++; + }else{ + cp++; + switch( op[j].type ){ + case OPT_FLAG: + case OPT_FFLAG: + if( err ){ + fprintf(err,"%soption requires an argument.\n",emsg); + errline(i,0,err); + } + errcnt++; + break; + case OPT_DBL: + case OPT_FDBL: + dv = strtod(cp,&end); + if( *end ){ + if( err ){ + fprintf(err,"%sillegal character in floating-point argument.\n",emsg); + errline(i,((unsigned long)end)-(unsigned long)argv[i],err); + } + errcnt++; + } + break; + case OPT_INT: + case OPT_FINT: + lv = strtol(cp,&end,0); + if( *end ){ + if( err ){ + fprintf(err,"%sillegal character in integer argument.\n",emsg); + errline(i,((unsigned long)end)-(unsigned long)argv[i],err); + } + errcnt++; + } + break; + case OPT_STR: + case OPT_FSTR: + sv = cp; + break; + } + switch( op[j].type ){ + case OPT_FLAG: + case OPT_FFLAG: + break; + case OPT_DBL: + *(double*)(op[j].arg) = dv; + break; + case OPT_FDBL: + (*(void(*)())(intptr_t)(op[j].arg))(dv); + break; + case OPT_INT: + *(int*)(op[j].arg) = lv; + break; + case OPT_FINT: + (*(void(*)())(intptr_t)(op[j].arg))((int)lv); + break; + case OPT_STR: + *(char**)(op[j].arg) = sv; + break; + case OPT_FSTR: + (*(void(*)())(intptr_t)(op[j].arg))(sv); + break; + } + } + return errcnt; +} + +int OptInit(a,o,err) +char **a; +struct s_options *o; +FILE *err; +{ + int errcnt = 0; + argv = a; + op = o; + errstream = err; + if( argv && *argv && op ){ + int i; + for(i=1; argv[i]; i++){ + if( argv[i][0]=='+' || argv[i][0]=='-' ){ + errcnt += handleflags(i,err); + }else if( strchr(argv[i],'=') ){ + errcnt += handleswitch(i,err); + } + } + } + if( errcnt>0 ){ + fprintf(err,"Valid command line options for \"%s\" are:\n",*a); + OptPrint(); + exit(1); + } + return 0; +} + +int OptNArgs(){ + int cnt = 0; + int dashdash = 0; + int i; + if( argv!=0 && argv[0]!=0 ){ + for(i=1; argv[i]; i++){ + if( dashdash || !ISOPT(argv[i]) ) cnt++; + if( strcmp(argv[i],"--")==0 ) dashdash = 1; + } + } + return cnt; +} + +char *OptArg(n) +int n; +{ + int i; + i = argindex(n); + return i>=0 ? argv[i] : 0; +} + +void OptErr(n) +int n; +{ + int i; + i = argindex(n); + if( i>=0 ) errline(i,0,errstream); +} + +void OptPrint(){ + int i; + int max, len; + max = 0; + for(i=0; op[i].label; i++){ + len = strlen(op[i].label) + 1; + switch( op[i].type ){ + case OPT_FLAG: + case OPT_FFLAG: + break; + case OPT_INT: + case OPT_FINT: + len += 9; /* length of "<integer>" */ + break; + case OPT_DBL: + case OPT_FDBL: + len += 6; /* length of "<real>" */ + break; + case OPT_STR: + case OPT_FSTR: + len += 8; /* length of "<string>" */ + break; + } + if( len>max ) max = len; + } + for(i=0; op[i].label; i++){ + switch( op[i].type ){ + case OPT_FLAG: + case OPT_FFLAG: + fprintf(errstream," -%-*s %s\n",max,op[i].label,op[i].message); + break; + case OPT_INT: + case OPT_FINT: + fprintf(errstream," %s=<integer>%*s %s\n",op[i].label, + (int)(max-strlen(op[i].label)-9),"",op[i].message); + break; + case OPT_DBL: + case OPT_FDBL: + fprintf(errstream," %s=<real>%*s %s\n",op[i].label, + (int)(max-strlen(op[i].label)-6),"",op[i].message); + break; + case OPT_STR: + case OPT_FSTR: + fprintf(errstream," %s=<string>%*s %s\n",op[i].label, + (int)(max-strlen(op[i].label)-8),"",op[i].message); + break; + } + } +} +/*********************** From the file "parse.c" ****************************/ +/* +** Input file parser for the LEMON parser generator. +*/ + +/* The state of the parser */ +struct pstate { + char *filename; /* Name of the input file */ + int tokenlineno; /* Linenumber at which current token starts */ + int errorcnt; /* Number of errors so far */ + char *tokenstart; /* Text of current token */ + struct lemon *gp; /* Global state vector */ + enum e_state { + INITIALIZE, + WAITING_FOR_DECL_OR_RULE, + WAITING_FOR_DECL_KEYWORD, + WAITING_FOR_DECL_ARG, + WAITING_FOR_PRECEDENCE_SYMBOL, + WAITING_FOR_ARROW, + IN_RHS, + LHS_ALIAS_1, + LHS_ALIAS_2, + LHS_ALIAS_3, + RHS_ALIAS_1, + RHS_ALIAS_2, + PRECEDENCE_MARK_1, + PRECEDENCE_MARK_2, + RESYNC_AFTER_RULE_ERROR, + RESYNC_AFTER_DECL_ERROR, + WAITING_FOR_DESTRUCTOR_SYMBOL, + WAITING_FOR_DATATYPE_SYMBOL, + WAITING_FOR_FALLBACK_ID + } state; /* The state of the parser */ + struct symbol *fallback; /* The fallback token */ + struct symbol *lhs; /* Left-hand side of current rule */ + char *lhsalias; /* Alias for the LHS */ + int nrhs; /* Number of right-hand side symbols seen */ + struct symbol *rhs[MAXRHS]; /* RHS symbols */ + char *alias[MAXRHS]; /* Aliases for each RHS symbol (or NULL) */ + struct rule *prevrule; /* Previous rule parsed */ + char *declkeyword; /* Keyword of a declaration */ + char **declargslot; /* Where the declaration argument should be put */ + int *decllnslot; /* Where the declaration linenumber is put */ + enum e_assoc declassoc; /* Assign this association to decl arguments */ + int preccounter; /* Assign this precedence to decl arguments */ + struct rule *firstrule; /* Pointer to first rule in the grammar */ + struct rule *lastrule; /* Pointer to the most recently parsed rule */ +}; + +/* Parse a single token */ +static void parseonetoken(psp) +struct pstate *psp; +{ + char *x; + x = Strsafe(psp->tokenstart); /* Save the token permanently */ +#if 0 + printf("%s:%d: Token=[%s] state=%d\n",psp->filename,psp->tokenlineno, + x,psp->state); +#endif + switch( psp->state ){ + case INITIALIZE: + psp->prevrule = 0; + psp->preccounter = 0; + psp->firstrule = psp->lastrule = 0; + psp->gp->nrule = 0; + /* Fall through */ + case WAITING_FOR_DECL_OR_RULE: + if( x[0]=='%' ){ + psp->state = WAITING_FOR_DECL_KEYWORD; + }else if( islower(x[0]) ){ + psp->lhs = Symbol_new(x); + psp->nrhs = 0; + psp->lhsalias = 0; + psp->state = WAITING_FOR_ARROW; + }else if( x[0]=='{' ){ + if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"There is not prior rule opon which to attach the code \ +fragment which begins on this line."); + psp->errorcnt++; + }else if( psp->prevrule->code!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Code fragment beginning on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->line = psp->tokenlineno; + psp->prevrule->code = &x[1]; + } + }else if( x[0]=='[' ){ + psp->state = PRECEDENCE_MARK_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Token \"%s\" should be either \"%%\" or a nonterminal name.", + x); + psp->errorcnt++; + } + break; + case PRECEDENCE_MARK_1: + if( !isupper(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "The precedence symbol must be a terminal."); + psp->errorcnt++; + }else if( psp->prevrule==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "There is no prior rule to assign precedence \"[%s]\".",x); + psp->errorcnt++; + }else if( psp->prevrule->precsym!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, +"Precedence mark on this line is not the first \ +to follow the previous rule."); + psp->errorcnt++; + }else{ + psp->prevrule->precsym = Symbol_new(x); + } + psp->state = PRECEDENCE_MARK_2; + break; + case PRECEDENCE_MARK_2: + if( x[0]!=']' ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"]\" on precedence mark."); + psp->errorcnt++; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + break; + case WAITING_FOR_ARROW: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else if( x[0]=='(' ){ + psp->state = LHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Expected to see a \":\" following the LHS symbol \"%s\".", + psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->lhsalias = x; + psp->state = LHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the LHS \"%s\"\n", + x,psp->lhs->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = LHS_ALIAS_3; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case LHS_ALIAS_3: + if( x[0]==':' && x[1]==':' && x[2]=='=' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \"->\" following: \"%s(%s)\".", + psp->lhs->name,psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case IN_RHS: + if( x[0]=='.' ){ + struct rule *rp; + rp = (struct rule *)malloc( sizeof(struct rule) + + sizeof(struct symbol*)*psp->nrhs + sizeof(char*)*psp->nrhs ); + if( rp==0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't allocate enough memory for this rule."); + psp->errorcnt++; + psp->prevrule = 0; + }else{ + int i; + rp->ruleline = psp->tokenlineno; + rp->rhs = (struct symbol**)&rp[1]; + rp->rhsalias = (char**)&(rp->rhs[psp->nrhs]); + for(i=0; i<psp->nrhs; i++){ + rp->rhs[i] = psp->rhs[i]; + rp->rhsalias[i] = psp->alias[i]; + } + rp->lhs = psp->lhs; + rp->lhsalias = psp->lhsalias; + rp->nrhs = psp->nrhs; + rp->code = 0; + rp->precsym = 0; + rp->index = psp->gp->nrule++; + rp->nextlhs = rp->lhs->rule; + rp->lhs->rule = rp; + rp->next = 0; + if( psp->firstrule==0 ){ + psp->firstrule = psp->lastrule = rp; + }else{ + psp->lastrule->next = rp; + psp->lastrule = rp; + } + psp->prevrule = rp; + } + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isalpha(x[0]) ){ + if( psp->nrhs>=MAXRHS ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Too many symbol on RHS or rule beginning at \"%s\".", + x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + }else{ + psp->rhs[psp->nrhs] = Symbol_new(x); + psp->alias[psp->nrhs] = 0; + psp->nrhs++; + } + }else if( x[0]=='(' && psp->nrhs>0 ){ + psp->state = RHS_ALIAS_1; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal character on RHS of rule: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_1: + if( isalpha(x[0]) ){ + psp->alias[psp->nrhs-1] = x; + psp->state = RHS_ALIAS_2; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "\"%s\" is not a valid alias for the RHS symbol \"%s\"\n", + x,psp->rhs[psp->nrhs-1]->name); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case RHS_ALIAS_2: + if( x[0]==')' ){ + psp->state = IN_RHS; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Missing \")\" following LHS alias name \"%s\".",psp->lhsalias); + psp->errorcnt++; + psp->state = RESYNC_AFTER_RULE_ERROR; + } + break; + case WAITING_FOR_DECL_KEYWORD: + if( isalpha(x[0]) ){ + psp->declkeyword = x; + psp->declargslot = 0; + psp->decllnslot = 0; + psp->state = WAITING_FOR_DECL_ARG; + if( strcmp(x,"name")==0 ){ + psp->declargslot = &(psp->gp->name); + }else if( strcmp(x,"include")==0 ){ + psp->declargslot = &(psp->gp->include); + psp->decllnslot = &psp->gp->includeln; + }else if( strcmp(x,"code")==0 ){ + psp->declargslot = &(psp->gp->extracode); + psp->decllnslot = &psp->gp->extracodeln; + }else if( strcmp(x,"token_destructor")==0 ){ + psp->declargslot = &psp->gp->tokendest; + psp->decllnslot = &psp->gp->tokendestln; + }else if( strcmp(x,"default_destructor")==0 ){ + psp->declargslot = &psp->gp->vardest; + psp->decllnslot = &psp->gp->vardestln; + }else if( strcmp(x,"token_prefix")==0 ){ + psp->declargslot = &psp->gp->tokenprefix; + }else if( strcmp(x,"syntax_error")==0 ){ + psp->declargslot = &(psp->gp->error); + psp->decllnslot = &psp->gp->errorln; + }else if( strcmp(x,"parse_accept")==0 ){ + psp->declargslot = &(psp->gp->accept); + psp->decllnslot = &psp->gp->acceptln; + }else if( strcmp(x,"parse_failure")==0 ){ + psp->declargslot = &(psp->gp->failure); + psp->decllnslot = &psp->gp->failureln; + }else if( strcmp(x,"stack_overflow")==0 ){ + psp->declargslot = &(psp->gp->overflow); + psp->decllnslot = &psp->gp->overflowln; + }else if( strcmp(x,"extra_argument")==0 ){ + psp->declargslot = &(psp->gp->arg); + }else if( strcmp(x,"token_type")==0 ){ + psp->declargslot = &(psp->gp->tokentype); + }else if( strcmp(x,"default_type")==0 ){ + psp->declargslot = &(psp->gp->vartype); + }else if( strcmp(x,"stack_size")==0 ){ + psp->declargslot = &(psp->gp->stacksize); + }else if( strcmp(x,"start_symbol")==0 ){ + psp->declargslot = &(psp->gp->start); + }else if( strcmp(x,"left")==0 ){ + psp->preccounter++; + psp->declassoc = LEFT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"right")==0 ){ + psp->preccounter++; + psp->declassoc = RIGHT; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"nonassoc")==0 ){ + psp->preccounter++; + psp->declassoc = NONE; + psp->state = WAITING_FOR_PRECEDENCE_SYMBOL; + }else if( strcmp(x,"destructor")==0 ){ + psp->state = WAITING_FOR_DESTRUCTOR_SYMBOL; + }else if( strcmp(x,"type")==0 ){ + psp->state = WAITING_FOR_DATATYPE_SYMBOL; + }else if( strcmp(x,"fallback")==0 ){ + psp->fallback = 0; + psp->state = WAITING_FOR_FALLBACK_ID; + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Unknown declaration keyword: \"%%%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal declaration keyword: \"%s\".",x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_DESTRUCTOR_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->destructor; + psp->decllnslot = &sp->destructorln; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_DATATYPE_SYMBOL: + if( !isalpha(x[0]) ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol name missing after %destructor keyword"); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + struct symbol *sp = Symbol_new(x); + psp->declargslot = &sp->datatype; + psp->decllnslot = 0; + psp->state = WAITING_FOR_DECL_ARG; + } + break; + case WAITING_FOR_PRECEDENCE_SYMBOL: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( isupper(x[0]) ){ + struct symbol *sp; + sp = Symbol_new(x); + if( sp->prec>=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "Symbol \"%s\" has already be given a precedence.",x); + psp->errorcnt++; + }else{ + sp->prec = psp->preccounter; + sp->assoc = psp->declassoc; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Can't assign a precedence to \"%s\".",x); + psp->errorcnt++; + } + break; + case WAITING_FOR_DECL_ARG: + if( (x[0]=='{' || x[0]=='\"' || isalnum(x[0])) ){ + if( *(psp->declargslot)!=0 ){ + ErrorMsg(psp->filename,psp->tokenlineno, + "The argument \"%s\" to declaration \"%%%s\" is not the first.", + x[0]=='\"' ? &x[1] : x,psp->declkeyword); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + }else{ + *(psp->declargslot) = (x[0]=='\"' || x[0]=='{') ? &x[1] : x; + if( psp->decllnslot ) *psp->decllnslot = psp->tokenlineno; + psp->state = WAITING_FOR_DECL_OR_RULE; + } + }else{ + ErrorMsg(psp->filename,psp->tokenlineno, + "Illegal argument to %%%s: %s",psp->declkeyword,x); + psp->errorcnt++; + psp->state = RESYNC_AFTER_DECL_ERROR; + } + break; + case WAITING_FOR_FALLBACK_ID: + if( x[0]=='.' ){ + psp->state = WAITING_FOR_DECL_OR_RULE; + }else if( !isupper(x[0]) ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "%%fallback argument \"%s\" should be a token", x); + psp->errorcnt++; + }else{ + struct symbol *sp = Symbol_new(x); + if( psp->fallback==0 ){ + psp->fallback = sp; + }else if( sp->fallback ){ + ErrorMsg(psp->filename, psp->tokenlineno, + "More than one fallback assigned to token %s", x); + psp->errorcnt++; + }else{ + sp->fallback = psp->fallback; + psp->gp->has_fallback = 1; + } + } + break; + case RESYNC_AFTER_RULE_ERROR: +/* if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; +** break; */ + case RESYNC_AFTER_DECL_ERROR: + if( x[0]=='.' ) psp->state = WAITING_FOR_DECL_OR_RULE; + if( x[0]=='%' ) psp->state = WAITING_FOR_DECL_KEYWORD; + break; + } +} + +/* In spite of its name, this function is really a scanner. It read +** in the entire input file (all at once) then tokenizes it. Each +** token is passed to the function "parseonetoken" which builds all +** the appropriate data structures in the global state vector "gp". +*/ +struct pstate ps; +void Parse(gp) +struct lemon *gp; +{ + FILE *fp; + char *filebuf; + size_t filesize; + int lineno; + int c; + char *cp, *nextcp; + int startline = 0; + + ps.gp = gp; + ps.filename = gp->filename; + ps.errorcnt = 0; + ps.state = INITIALIZE; + + /* Begin by reading the input file */ + fp = fopen(ps.filename,"rb"); + if( fp==0 ){ + ErrorMsg(ps.filename,0,"Can't open this file for reading."); + gp->errorcnt++; + return; + } + fseek(fp,0,2); + filesize = ftell(fp); + rewind(fp); + filebuf = (char *)malloc( filesize+1 ); + if( filebuf==0 ){ + ErrorMsg(ps.filename,0,"Can't allocate %d of memory to hold this file.", + filesize+1); + fclose(fp); + gp->errorcnt++; + return; + } + if( fread(filebuf,1,filesize,fp)!=filesize ){ + ErrorMsg(ps.filename,0,"Can't read in all %d bytes of this file.", + filesize); + free(filebuf); + fclose(fp); + gp->errorcnt++; + return; + } + fclose(fp); + filebuf[filesize] = 0; + + /* Now scan the text of the input file */ + lineno = 1; + for(cp=filebuf; (c= *cp)!=0; ){ + if( c=='\n' ) lineno++; /* Keep track of the line number */ + if( isspace(c) ){ cp++; continue; } /* Skip all white space */ + if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments */ + cp+=2; + while( (c= *cp)!=0 && c!='\n' ) cp++; + continue; + } + if( c=='/' && cp[1]=='*' ){ /* Skip C style comments */ + cp+=2; + while( (c= *cp)!=0 && (c!='/' || cp[-1]!='*') ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c ) cp++; + continue; + } + ps.tokenstart = cp; /* Mark the beginning of the token */ + ps.tokenlineno = lineno; /* Linenumber on which token begins */ + if( c=='\"' ){ /* String literals */ + cp++; + while( (c= *cp)!=0 && c!='\"' ){ + if( c=='\n' ) lineno++; + cp++; + } + if( c==0 ){ + ErrorMsg(ps.filename,startline, +"String starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( c=='{' ){ /* A block of C code */ + int level; + cp++; + for(level=1; (c= *cp)!=0 && (level>1 || c!='}'); cp++){ + if( c=='\n' ) lineno++; + else if( c=='{' ) level++; + else if( c=='}' ) level--; + else if( c=='/' && cp[1]=='*' ){ /* Skip comments */ + int prevc; + cp = &cp[2]; + prevc = 0; + while( (c= *cp)!=0 && (c!='/' || prevc!='*') ){ + if( c=='\n' ) lineno++; + prevc = c; + cp++; + } + }else if( c=='/' && cp[1]=='/' ){ /* Skip C++ style comments too */ + cp = &cp[2]; + while( (c= *cp)!=0 && c!='\n' ) cp++; + if( c ) lineno++; + }else if( c=='\'' || c=='\"' ){ /* String a character literals */ + int startchar, prevc; + startchar = c; + prevc = 0; + for(cp++; (c= *cp)!=0 && (c!=startchar || prevc=='\\'); cp++){ + if( c=='\n' ) lineno++; + if( prevc=='\\' ) prevc = 0; + else prevc = c; + } + } + } + if( c==0 ){ + ErrorMsg(ps.filename,ps.tokenlineno, +"C code starting on this line is not terminated before the end of the file."); + ps.errorcnt++; + nextcp = cp; + }else{ + nextcp = cp+1; + } + }else if( isalnum(c) ){ /* Identifiers */ + while( (c= *cp)!=0 && (isalnum(c) || c=='_') ) cp++; + nextcp = cp; + }else if( c==':' && cp[1]==':' && cp[2]=='=' ){ /* The operator "::=" */ + cp += 3; + nextcp = cp; + }else{ /* All other (one character) operators */ + cp++; + nextcp = cp; + } + c = *cp; + *cp = 0; /* Null terminate the token */ + parseonetoken(&ps); /* Parse the token */ + *cp = c; /* Restore the buffer */ + cp = nextcp; + } + free(filebuf); /* Release the buffer after parsing */ + gp->rule = ps.firstrule; + gp->errorcnt = ps.errorcnt; +} +/*************************** From the file "plink.c" *********************/ +/* +** Routines processing configuration follow-set propagation links +** in the LEMON parser generator. +*/ +static struct plink *plink_freelist = 0; + +/* Allocate a new plink */ +struct plink *Plink_new(){ + struct plink *new; + + if( plink_freelist==0 ){ + int i; + int amt = 100; + plink_freelist = (struct plink *)malloc( sizeof(struct plink)*amt ); + if( plink_freelist==0 ){ + fprintf(stderr, + "Unable to allocate memory for a new follow-set propagation link.\n"); + exit(1); + } + for(i=0; i<amt-1; i++) plink_freelist[i].next = &plink_freelist[i+1]; + plink_freelist[amt-1].next = 0; + } + new = plink_freelist; + plink_freelist = plink_freelist->next; + return new; +} + +/* Add a plink to a plink list */ +void Plink_add(plpp,cfp) +struct plink **plpp; +struct config *cfp; +{ + struct plink *new; + new = Plink_new(); + new->next = *plpp; + *plpp = new; + new->cfp = cfp; +} + +/* Transfer every plink on the list "from" to the list "to" */ +void Plink_copy(to,from) +struct plink **to; +struct plink *from; +{ + struct plink *nextpl; + while( from ){ + nextpl = from->next; + from->next = *to; + *to = from; + from = nextpl; + } +} + +/* Delete every plink on the list */ +void Plink_delete(plp) +struct plink *plp; +{ + struct plink *nextpl; + + while( plp ){ + nextpl = plp->next; + plp->next = plink_freelist; + plink_freelist = plp; + plp = nextpl; + } +} +/*********************** From the file "report.c" **************************/ +/* +** Procedures for generating reports and tables in the LEMON parser generator. +*/ + +/* Generate a filename with the given suffix. Space to hold the +** name comes from malloc() and must be freed by the calling +** function. +*/ +PRIVATE char *file_makename(lemp,suffix) +struct lemon *lemp; +char *suffix; +{ + char *name; + char *cp; + + name = malloc( strlen(out_dir) + strlen(lemp->filename) + strlen(suffix) + 6 ); + if( name==0 ){ + fprintf(stderr,"Can't allocate space for a filename.\n"); + exit(1); + } + /* skip directory, JK */ + if (NULL == (cp = strrchr(lemp->filename, '/'))) { + cp = lemp->filename; + } else { + cp++; + } + strcpy(name,out_dir); + strcat(name,"/"); + strcat(name,cp); + cp = strrchr(name,'.'); + if( cp ) *cp = 0; + strcat(name,suffix); + return name; +} + +/* Open a file with a name based on the name of the input file, +** but with a different (specified) suffix, and return a pointer +** to the stream */ +PRIVATE FILE *file_open(lemp,suffix,mode) +struct lemon *lemp; +char *suffix; +char *mode; +{ + FILE *fp; + + if( lemp->outname ) free(lemp->outname); + lemp->outname = file_makename(lemp, suffix); + fp = fopen(lemp->outname,mode); + if( fp==0 && *mode=='w' ){ + fprintf(stderr,"Can't open file \"%s\".\n",lemp->outname); + lemp->errorcnt++; + return 0; + } + return fp; +} + +/* Duplicate the input file without comments and without actions +** on rules */ +void Reprint(lemp) +struct lemon *lemp; +{ + struct rule *rp; + struct symbol *sp; + int i, j, maxlen, len, ncolumns, skip; + printf("// Reprint of input file \"%s\".\n// Symbols:\n",lemp->filename); + maxlen = 10; + for(i=0; i<lemp->nsymbol; i++){ + sp = lemp->symbols[i]; + len = strlen(sp->name); + if( len>maxlen ) maxlen = len; + } + ncolumns = 76/(maxlen+5); + if( ncolumns<1 ) ncolumns = 1; + skip = (lemp->nsymbol + ncolumns - 1)/ncolumns; + for(i=0; i<skip; i++){ + printf("//"); + for(j=i; j<lemp->nsymbol; j+=skip){ + sp = lemp->symbols[j]; + assert( sp->index==j ); + printf(" %3d %-*.*s",j,maxlen,maxlen,sp->name); + } + printf("\n"); + } + for(rp=lemp->rule; rp; rp=rp->next){ + printf("%s",rp->lhs->name); +/* if( rp->lhsalias ) printf("(%s)",rp->lhsalias); */ + printf(" ::="); + for(i=0; i<rp->nrhs; i++){ + printf(" %s",rp->rhs[i]->name); +/* if( rp->rhsalias[i] ) printf("(%s)",rp->rhsalias[i]); */ + } + printf("."); + if( rp->precsym ) printf(" [%s]",rp->precsym->name); +/* if( rp->code ) printf("\n %s",rp->code); */ + printf("\n"); + } +} + +PRIVATE void ConfigPrint(fp,cfp) +FILE *fp; +struct config *cfp; +{ + struct rule *rp; + int i; + rp = cfp->rp; + fprintf(fp,"%s ::=",rp->lhs->name); + for(i=0; i<=rp->nrhs; i++){ + if( i==cfp->dot ) fprintf(fp," *"); + if( i==rp->nrhs ) break; + fprintf(fp," %s",rp->rhs[i]->name); + } +} + +/* #define TEST */ +#ifdef TEST +/* Print a set */ +PRIVATE void SetPrint(out,set,lemp) +FILE *out; +char *set; +struct lemon *lemp; +{ + int i; + char *spacer; + spacer = ""; + fprintf(out,"%12s[",""); + for(i=0; i<lemp->nterminal; i++){ + if( SetFind(set,i) ){ + fprintf(out,"%s%s",spacer,lemp->symbols[i]->name); + spacer = " "; + } + } + fprintf(out,"]\n"); +} + +/* Print a plink chain */ +void PlinkPrint(out,plp,tag) +FILE *out; +struct plink *plp; +char *tag; +{ + while( plp ){ + fprintf(out,"%12s%s (state %2d) ","",tag,plp->cfp->stp->index); + ConfigPrint(out,plp->cfp); + fprintf(out,"\n"); + plp = plp->next; + } +} +#endif + +/* Print an action to the given file descriptor. Return FALSE if +** nothing was actually printed. +*/ +PRIVATE int PrintAction(struct action *ap, FILE *fp, int indent){ + int result = 1; + switch( ap->type ){ + case SHIFT: + fprintf(fp,"%*s shift %d",indent,ap->sp->name,ap->x.stp->index); + break; + case REDUCE: + fprintf(fp,"%*s reduce %d",indent,ap->sp->name,ap->x.rp->index); + break; + case ACCEPT: + fprintf(fp,"%*s accept",indent,ap->sp->name); + break; + case ERROR: + fprintf(fp,"%*s error",indent,ap->sp->name); + break; + case CONFLICT: + fprintf(fp,"%*s reduce %-3d ** Parsing conflict **", + indent,ap->sp->name,ap->x.rp->index); + break; + case SH_RESOLVED: + case RD_RESOLVED: + case NOT_USED: + result = 0; + break; + } + return result; +} + +/* Generate the "y.output" log file */ +void ReportOutput(lemp) +struct lemon *lemp; +{ + int i; + struct state *stp; + struct config *cfp; + struct action *ap; + FILE *fp; + + fp = file_open(lemp,".out","w"); + if( fp==0 ) return; + fprintf(fp," \b"); + for(i=0; i<lemp->nstate; i++){ + stp = lemp->sorted[i]; + fprintf(fp,"State %d:\n",stp->index); + if( lemp->basisflag ) cfp=stp->bp; + else cfp=stp->cfp; + while( cfp ){ + char buf[20]; + if( cfp->dot==cfp->rp->nrhs ){ + sprintf(buf,"(%d)",cfp->rp->index); + fprintf(fp," %5s ",buf); + }else{ + fprintf(fp," "); + } + ConfigPrint(fp,cfp); + fprintf(fp,"\n"); +#ifdef TEST + SetPrint(fp,cfp->fws,lemp); + PlinkPrint(fp,cfp->fplp,"To "); + PlinkPrint(fp,cfp->bplp,"From"); +#endif + if( lemp->basisflag ) cfp=cfp->bp; + else cfp=cfp->next; + } + fprintf(fp,"\n"); + for(ap=stp->ap; ap; ap=ap->next){ + if( PrintAction(ap,fp,30) ) fprintf(fp,"\n"); + } + fprintf(fp,"\n"); + } + fclose(fp); + return; +} + +/* Search for the file "name" which is in the same directory as +** the exacutable */ +PRIVATE char *pathsearch(argv0,name,modemask) +char *argv0; +char *name; +int modemask; +{ + char *pathlist; + char *path,*cp; + char c; + +#ifdef __WIN32__ + cp = strrchr(argv0,'\\'); +#else + cp = strrchr(argv0,'/'); +#endif + if( cp ){ + c = *cp; + *cp = 0; + path = (char *)malloc( strlen(argv0) + strlen(name) + 2 ); + if( path ) sprintf(path,"%s/%s",argv0,name); + *cp = c; + }else{ + pathlist = getenv("PATH"); + if( pathlist==0 ) pathlist = ".:/bin:/usr/bin"; + path = (char *)malloc( strlen(pathlist)+strlen(name)+2 ); + if( path!=0 ){ + while( *pathlist ){ + cp = strchr(pathlist,':'); + if( cp==0 ) cp = &pathlist[strlen(pathlist)]; + c = *cp; + *cp = 0; + sprintf(path,"%s/%s",pathlist,name); + *cp = c; + if( c==0 ) pathlist = ""; + else pathlist = &cp[1]; + if( access(path,modemask)==0 ) break; + } + } + } + return path; +} + +/* Given an action, compute the integer value for that action +** which is to be put in the action table of the generated machine. +** Return negative if no action should be generated. +*/ +PRIVATE int compute_action(lemp,ap) +struct lemon *lemp; +struct action *ap; +{ + int act; + switch( ap->type ){ + case SHIFT: act = ap->x.stp->index; break; + case REDUCE: act = ap->x.rp->index + lemp->nstate; break; + case ERROR: act = lemp->nstate + lemp->nrule; break; + case ACCEPT: act = lemp->nstate + lemp->nrule + 1; break; + default: act = -1; break; + } + return act; +} + +#define LINESIZE 1000 +/* The next cluster of routines are for reading the template file +** and writing the results to the generated parser */ +/* The first function transfers data from "in" to "out" until +** a line is seen which begins with "%%". The line number is +** tracked. +** +** if name!=0, then any word that begin with "Parse" is changed to +** begin with *name instead. +*/ +PRIVATE void tplt_xfer(name,in,out,lineno) +char *name; +FILE *in; +FILE *out; +int *lineno; +{ + int i, iStart; + char line[LINESIZE]; + while( fgets(line,LINESIZE,in) && (line[0]!='%' || line[1]!='%') ){ + (*lineno)++; + iStart = 0; + if( name ){ + for(i=0; line[i]; i++){ + if( line[i]=='P' && strncmp(&line[i],"Parse",5)==0 + && (i==0 || !isalpha(line[i-1])) + ){ + if( i>iStart ) fprintf(out,"%.*s",i-iStart,&line[iStart]); + fprintf(out,"%s",name); + i += 4; + iStart = i+1; + } + } + } + fprintf(out,"%s",&line[iStart]); + } +} + +/* The next function finds the template file and opens it, returning +** a pointer to the opened file. */ +PRIVATE FILE *tplt_open(lemp) +struct lemon *lemp; +{ + + char buf[1000]; + FILE *in; + char *tpltname; + char *tpltname_alloc = NULL; + char *cp; + + cp = strrchr(lemp->filename,'.'); + if( cp ){ + sprintf(buf,"%.*s.lt",(int)(cp-lemp->filename),lemp->filename); + }else{ + sprintf(buf,"%s.lt",lemp->filename); + } + if( access(buf,004)==0 ){ + tpltname = buf; + }else if( access(lemp->tmplname,004)==0 ){ + tpltname = lemp->tmplname; + }else{ + tpltname = tpltname_alloc = pathsearch(lemp->argv0,lemp->tmplname,0); + } + if( tpltname==0 ){ + fprintf(stderr,"Can't find the parser driver template file \"%s\".\n", + lemp->tmplname); + lemp->errorcnt++; + return 0; + } + in = fopen(tpltname,"r"); + if( in==0 ){ + fprintf(stderr,"Can't open the template file \"%s\".\n",tpltname); + lemp->errorcnt++; + } + if (tpltname_alloc) free(tpltname_alloc); + return in; +} + +/* Print a string to the file and keep the linenumber up to date */ +PRIVATE void tplt_print(out,lemp,str,strln,lineno) +FILE *out; +struct lemon *lemp; +char *str; +int strln; +int *lineno; +{ + if( str==0 ) return; + fprintf(out,"#line %d \"%s\"\n",strln,lemp->filename); (*lineno)++; + while( *str ){ + if( *str=='\n' ) (*lineno)++; + putc(*str,out); + str++; + } + fprintf(out,"\n#line %d \"%s\"\n",*lineno+2,lemp->outname); (*lineno)+=2; + return; +} + +/* +** The following routine emits code for the destructor for the +** symbol sp +*/ +PRIVATE void emit_destructor_code(out,sp,lemp,lineno) +FILE *out; +struct symbol *sp; +struct lemon *lemp; +int *lineno; +{ + char *cp = 0; + + int linecnt = 0; + if( sp->type==TERMINAL ){ + cp = lemp->tokendest; + if( cp==0 ) return; + fprintf(out,"#line %d \"%s\"\n{",lemp->tokendestln,lemp->filename); + }else if( sp->destructor ){ + cp = sp->destructor; + fprintf(out,"#line %d \"%s\"\n{",sp->destructorln,lemp->filename); + }else{ + cp = lemp->vardest; + if( cp==0 ) return; + fprintf(out,"#line %d \"%s\"\n{",lemp->vardestln,lemp->filename); + } + for(; *cp; cp++){ + if( *cp=='$' && cp[1]=='$' ){ + fprintf(out,"(yypminor->yy%d)",sp->dtnum); + cp++; + continue; + } + if( *cp=='\n' ) linecnt++; + fputc(*cp,out); + } + (*lineno) += 3 + linecnt; + fprintf(out,"}\n#line %d \"%s\"\n",*lineno,lemp->outname); + return; +} + +/* +** Return TRUE (non-zero) if the given symbol has a destructor. +*/ +PRIVATE int has_destructor(sp, lemp) +struct symbol *sp; +struct lemon *lemp; +{ + int ret; + if( sp->type==TERMINAL ){ + ret = lemp->tokendest!=0; + }else{ + ret = lemp->vardest!=0 || sp->destructor!=0; + } + return ret; +} + +/* +** Generate code which executes when the rule "rp" is reduced. Write +** the code to "out". Make sure lineno stays up-to-date. +*/ +PRIVATE void emit_code(out,rp,lemp,lineno) +FILE *out; +struct rule *rp; +struct lemon *lemp; +int *lineno; +{ + char *cp, *xp; + int linecnt = 0; + int i; + char lhsused = 0; /* True if the LHS element has been used */ + char used[MAXRHS]; /* True for each RHS element which is used */ + + for(i=0; i<rp->nrhs; i++) used[i] = 0; + lhsused = 0; + + /* Generate code to do the reduce action */ + if( rp->code ){ + fprintf(out,"#line %d \"%s\"\n{",rp->line,lemp->filename); + for(cp=rp->code; *cp; cp++){ + if( isalpha(*cp) && (cp==rp->code || (!isalnum(cp[-1]) && cp[-1]!='_')) ){ + char saved; + for(xp= &cp[1]; isalnum(*xp) || *xp=='_'; xp++); + saved = *xp; + *xp = 0; + if( rp->lhsalias && strcmp(cp,rp->lhsalias)==0 ){ + fprintf(out,"yygotominor.yy%d",rp->lhs->dtnum); + cp = xp; + lhsused = 1; + }else{ + for(i=0; i<rp->nrhs; i++){ + if( rp->rhsalias[i] && strcmp(cp,rp->rhsalias[i])==0 ){ + fprintf(out,"yymsp[%d].minor.yy%d",i-rp->nrhs+1,rp->rhs[i]->dtnum); + cp = xp; + used[i] = 1; + break; + } + } + } + *xp = saved; + } + if( *cp=='\n' ) linecnt++; + fputc(*cp,out); + } /* End loop */ + (*lineno) += 3 + linecnt; + fprintf(out,"}\n#line %d \"%s\"\n",*lineno,lemp->outname); + } /* End if( rp->code ) */ + + /* Check to make sure the LHS has been used */ + if( rp->lhsalias && !lhsused ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label \"%s\" for \"%s(%s)\" is never used.", + rp->lhsalias,rp->lhs->name,rp->lhsalias); + lemp->errorcnt++; + } + + /* Generate destructor code for RHS symbols which are not used in the + ** reduce code */ + for(i=0; i<rp->nrhs; i++){ + if( rp->rhsalias[i] && !used[i] ){ + ErrorMsg(lemp->filename,rp->ruleline, + "Label %s for \"%s(%s)\" is never used.", + rp->rhsalias[i],rp->rhs[i]->name,rp->rhsalias[i]); + lemp->errorcnt++; + }else if( rp->rhsalias[i]==0 ){ + if( has_destructor(rp->rhs[i],lemp) ){ + fprintf(out," yy_destructor(%d,&yymsp[%d].minor);\n", + rp->rhs[i]->index,i-rp->nrhs+1); (*lineno)++; + }else{ + fprintf(out," /* No destructor defined for %s */\n", + rp->rhs[i]->name); + (*lineno)++; + } + } + } + return; +} + +/* +** Print the definition of the union used for the parser's data stack. +** This union contains fields for every possible data type for tokens +** and nonterminals. In the process of computing and printing this +** union, also set the ".dtnum" field of every terminal and nonterminal +** symbol. +*/ +PRIVATE void print_stack_union(out,lemp,plineno,mhflag) +FILE *out; /* The output stream */ +struct lemon *lemp; /* The main info structure for this parser */ +int *plineno; /* Pointer to the line number */ +int mhflag; /* True if generating makeheaders output */ +{ + int lineno; /* The line number of the output */ + char **types; /* A hash table of datatypes */ + int arraysize; /* Size of the "types" array */ + int maxdtlength; /* Maximum length of any ".datatype" field. */ + char *stddt; /* Standardized name for a datatype */ + int i,j; /* Loop counters */ + int hash; /* For hashing the name of a type */ + char *name; /* Name of the parser */ + + /* Allocate and initialize types[] and allocate stddt[] */ + arraysize = lemp->nsymbol * 2; + types = (char**)malloc( arraysize * sizeof(char*) ); + for(i=0; i<arraysize; i++) types[i] = 0; + maxdtlength = 0; + if( lemp->vartype ){ + maxdtlength = strlen(lemp->vartype); + } + for(i=0; i<lemp->nsymbol; i++){ + int len; + struct symbol *sp = lemp->symbols[i]; + if( sp->datatype==0 ) continue; + len = strlen(sp->datatype); + if( len>maxdtlength ) maxdtlength = len; + } + stddt = (char*)malloc( maxdtlength*2 + 1 ); + if( types==0 || stddt==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + + /* Build a hash table of datatypes. The ".dtnum" field of each symbol + ** is filled in with the hash index plus 1. A ".dtnum" value of 0 is + ** used for terminal symbols. If there is no %default_type defined then + ** 0 is also used as the .dtnum value for nonterminals which do not specify + ** a datatype using the %type directive. + */ + for(i=0; i<lemp->nsymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + char *cp; + if( sp==lemp->errsym ){ + sp->dtnum = arraysize+1; + continue; + } + if( sp->type!=NONTERMINAL || (sp->datatype==0 && lemp->vartype==0) ){ + sp->dtnum = 0; + continue; + } + cp = sp->datatype; + if( cp==0 ) cp = lemp->vartype; + j = 0; + while( isspace(*cp) ) cp++; + while( *cp ) stddt[j++] = *cp++; + while( j>0 && isspace(stddt[j-1]) ) j--; + stddt[j] = 0; + hash = 0; + for(j=0; stddt[j]; j++){ + hash = (unsigned int)hash*53u + (unsigned int) stddt[j]; + } + hash = (hash & 0x7fffffff)%arraysize; + while( types[hash] ){ + if( strcmp(types[hash],stddt)==0 ){ + sp->dtnum = hash + 1; + break; + } + hash++; + if( hash>=arraysize ) hash = 0; + } + if( types[hash]==0 ){ + sp->dtnum = hash + 1; + types[hash] = (char*)malloc( strlen(stddt)+1 ); + if( types[hash]==0 ){ + fprintf(stderr,"Out of memory.\n"); + exit(1); + } + strcpy(types[hash],stddt); + } + } + + /* Print out the definition of YYTOKENTYPE and YYMINORTYPE */ + name = lemp->name ? lemp->name : "Parse"; + lineno = *plineno; + if( mhflag ){ fprintf(out,"#if INTERFACE\n"); lineno++; } + fprintf(out,"#define %sTOKENTYPE %s\n",name, + lemp->tokentype?lemp->tokentype:"void*"); lineno++; + if( mhflag ){ fprintf(out,"#endif\n"); lineno++; } + fprintf(out,"typedef union {\n"); lineno++; + fprintf(out," %sTOKENTYPE yy0;\n",name); lineno++; + for(i=0; i<arraysize; i++){ + if( types[i]==0 ) continue; + fprintf(out," %s yy%d;\n",types[i],i+1); lineno++; + free(types[i]); + } + fprintf(out," int yy%d;\n",lemp->errsym->dtnum); lineno++; + free(stddt); + free(types); + fprintf(out,"} YYMINORTYPE;\n"); lineno++; + *plineno = lineno; +} + +/* +** Return the name of a C datatype able to represent values between +** lwr and upr, inclusive. +*/ +static const char *minimum_size_type(int lwr, int upr){ + if( lwr>=0 ){ + if( upr<=255 ){ + return "unsigned char"; + }else if( upr<65535 ){ + return "unsigned short int"; + }else{ + return "unsigned int"; + } + }else if( lwr>=-127 && upr<=127 ){ + return "signed char"; + }else if( lwr>=-32767 && upr<32767 ){ + return "short"; + }else{ + return "int"; + } +} + +/* +** Each state contains a set of token transaction and a set of +** nonterminal transactions. Each of these sets makes an instance +** of the following structure. An array of these structures is used +** to order the creation of entries in the yy_action[] table. +*/ +struct axset { + struct state *stp; /* A pointer to a state */ + int isTkn; /* True to use tokens. False for non-terminals */ + int nAction; /* Number of actions */ +}; + +/* +** Compare to axset structures for sorting purposes +*/ +static int axset_compare(const void *a, const void *b){ + struct axset *p1 = (struct axset*)a; + struct axset *p2 = (struct axset*)b; + return p2->nAction - p1->nAction; +} + +/* Generate C source code for the parser */ +void ReportTable(lemp, mhflag) +struct lemon *lemp; +int mhflag; /* Output in makeheaders format if true */ +{ + FILE *out, *in; + char line[LINESIZE]; + int lineno; + struct state *stp; + struct action *ap; + struct rule *rp; + struct acttab *pActtab; + int i, j, n; + int mnTknOfst, mxTknOfst; + int mnNtOfst, mxNtOfst; + struct axset *ax; + char *name; + + in = tplt_open(lemp); + if( in==0 ) return; + out = file_open(lemp,".c","w"); + if( out==0 ){ + fclose(in); + return; + } + lineno = 1; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the include code, if any */ + tplt_print(out,lemp,lemp->include,lemp->includeln,&lineno); + if( mhflag ){ + name = file_makename(lemp, ".h"); + fprintf(out,"#include \"%s\"\n", name); lineno++; + free(name); + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate #defines for all tokens */ + if( mhflag ){ + char *prefix; + fprintf(out,"#if INTERFACE\n"); lineno++; + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + for(i=1; i<lemp->nterminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + lineno++; + } + fprintf(out,"#endif\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the defines */ + fprintf(out,"/* \001 */\n"); + fprintf(out,"#define YYCODETYPE %s\n", + minimum_size_type(0, lemp->nsymbol+5)); lineno++; + fprintf(out,"#define YYNOCODE %d\n",lemp->nsymbol+1); lineno++; + fprintf(out,"#define YYACTIONTYPE %s\n", + minimum_size_type(0, lemp->nstate+lemp->nrule+5)); lineno++; + print_stack_union(out,lemp,&lineno,mhflag); + if( lemp->stacksize ){ + if( atoi(lemp->stacksize)<=0 ){ + ErrorMsg(lemp->filename,0, +"Illegal stack size: [%s]. The stack size should be an integer constant.", + lemp->stacksize); + lemp->errorcnt++; + lemp->stacksize = "100"; + } + fprintf(out,"#define YYSTACKDEPTH %s\n",lemp->stacksize); lineno++; + }else{ + fprintf(out,"#define YYSTACKDEPTH 100\n"); lineno++; + } + if( mhflag ){ + fprintf(out,"#if INTERFACE\n"); lineno++; + } + name = lemp->name ? lemp->name : "Parse"; + if( lemp->arg && lemp->arg[0] ){ + i = strlen(lemp->arg); + while( i>=1 && isspace(lemp->arg[i-1]) ) i--; + while( i>=1 && (isalnum(lemp->arg[i-1]) || lemp->arg[i-1]=='_') ) i--; + fprintf(out,"#define %sARG_SDECL %s;\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_PDECL ,%s\n",name,lemp->arg); lineno++; + fprintf(out,"#define %sARG_FETCH %s = yypParser->%s\n", + name,lemp->arg,&lemp->arg[i]); lineno++; + fprintf(out,"#define %sARG_STORE yypParser->%s = %s\n", + name,&lemp->arg[i],&lemp->arg[i]); lineno++; + }else{ + fprintf(out,"#define %sARG_SDECL\n",name); lineno++; + fprintf(out,"#define %sARG_PDECL\n",name); lineno++; + fprintf(out,"#define %sARG_FETCH\n",name); lineno++; + fprintf(out,"#define %sARG_STORE\n",name); lineno++; + } + if( mhflag ){ + fprintf(out,"#endif\n"); lineno++; + } + fprintf(out,"#define YYNSTATE %d\n",lemp->nstate); lineno++; + fprintf(out,"#define YYNRULE %d\n",lemp->nrule); lineno++; + fprintf(out,"#define YYERRORSYMBOL %d\n",lemp->errsym->index); lineno++; + fprintf(out,"#define YYERRSYMDT yy%d\n",lemp->errsym->dtnum); lineno++; + if( lemp->has_fallback ){ + fprintf(out,"#define YYFALLBACK 1\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the action table and its associates: + ** + ** yy_action[] A single table containing all actions. + ** yy_lookahead[] A table containing the lookahead for each entry in + ** yy_action. Used to detect hash collisions. + ** yy_shift_ofst[] For each state, the offset into yy_action for + ** shifting terminals. + ** yy_reduce_ofst[] For each state, the offset into yy_action for + ** shifting non-terminals after a reduce. + ** yy_default[] Default action for each state. + */ + + /* Compute the actions on all states and count them up */ + ax = malloc( sizeof(ax[0])*lemp->nstate*2 ); + if( ax==0 ){ + fprintf(stderr,"malloc failed\n"); + exit(1); + } + for(i=0; i<lemp->nstate; i++){ + stp = lemp->sorted[i]; + stp->nTknAct = stp->nNtAct = 0; + stp->iDflt = lemp->nstate + lemp->nrule; + stp->iTknOfst = NO_OFFSET; + stp->iNtOfst = NO_OFFSET; + for(ap=stp->ap; ap; ap=ap->next){ + if( compute_action(lemp,ap)>=0 ){ + if( ap->sp->index<lemp->nterminal ){ + stp->nTknAct++; + }else if( ap->sp->index<lemp->nsymbol ){ + stp->nNtAct++; + }else{ + stp->iDflt = compute_action(lemp, ap); + } + } + } + ax[i*2].stp = stp; + ax[i*2].isTkn = 1; + ax[i*2].nAction = stp->nTknAct; + ax[i*2+1].stp = stp; + ax[i*2+1].isTkn = 0; + ax[i*2+1].nAction = stp->nNtAct; + } + mxTknOfst = mnTknOfst = 0; + mxNtOfst = mnNtOfst = 0; + + /* Compute the action table. In order to try to keep the size of the + ** action table to a minimum, the heuristic of placing the largest action + ** sets first is used. + */ + qsort(ax, lemp->nstate*2, sizeof(ax[0]), axset_compare); + pActtab = acttab_alloc(); + for(i=0; i<lemp->nstate*2 && ax[i].nAction>0; i++){ + stp = ax[i].stp; + if( ax[i].isTkn ){ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->index>=lemp->nterminal ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iTknOfst = acttab_insert(pActtab); + if( stp->iTknOfst<mnTknOfst ) mnTknOfst = stp->iTknOfst; + if( stp->iTknOfst>mxTknOfst ) mxTknOfst = stp->iTknOfst; + }else{ + for(ap=stp->ap; ap; ap=ap->next){ + int action; + if( ap->sp->index<lemp->nterminal ) continue; + if( ap->sp->index==lemp->nsymbol ) continue; + action = compute_action(lemp, ap); + if( action<0 ) continue; + acttab_action(pActtab, ap->sp->index, action); + } + stp->iNtOfst = acttab_insert(pActtab); + if( stp->iNtOfst<mnNtOfst ) mnNtOfst = stp->iNtOfst; + if( stp->iNtOfst>mxNtOfst ) mxNtOfst = stp->iNtOfst; + } + } + free(ax); + + /* Output the yy_action table */ + fprintf(out,"static YYACTIONTYPE yy_action[] = {\n"); lineno++; + n = acttab_size(pActtab); + for(i=j=0; i<n; i++){ + int action = acttab_yyaction(pActtab, i); + if( action<0 ) action = lemp->nsymbol + lemp->nrule + 2; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", action); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_lookahead table */ + fprintf(out,"static YYCODETYPE yy_lookahead[] = {\n"); lineno++; + for(i=j=0; i<n; i++){ + int la = acttab_yylookahead(pActtab, i); + if( la<0 ) la = lemp->nsymbol; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", la); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_shift_ofst[] table */ + fprintf(out, "#define YY_SHIFT_USE_DFLT (%d)\n", mnTknOfst-1); lineno++; + fprintf(out, "static %s yy_shift_ofst[] = {\n", + minimum_size_type(mnTknOfst-1, mxTknOfst)); lineno++; + n = lemp->nstate; + for(i=j=0; i<n; i++){ + int ofst; + stp = lemp->sorted[i]; + ofst = stp->iTknOfst; + if( ofst==NO_OFFSET ) ofst = mnTknOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the yy_reduce_ofst[] table */ + fprintf(out, "#define YY_REDUCE_USE_DFLT (%d)\n", mnNtOfst-1); lineno++; + fprintf(out, "static %s yy_reduce_ofst[] = {\n", + minimum_size_type(mnNtOfst-1, mxNtOfst)); lineno++; + n = lemp->nstate; + for(i=j=0; i<n; i++){ + int ofst; + stp = lemp->sorted[i]; + ofst = stp->iNtOfst; + if( ofst==NO_OFFSET ) ofst = mnNtOfst - 1; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", ofst); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + + /* Output the default action table */ + fprintf(out, "static YYACTIONTYPE yy_default[] = {\n"); lineno++; + n = lemp->nstate; + for(i=j=0; i<n; i++){ + stp = lemp->sorted[i]; + if( j==0 ) fprintf(out," /* %5d */ ", i); + fprintf(out, " %4d,", stp->iDflt); + if( j==9 || i==n-1 ){ + fprintf(out, "\n"); lineno++; + j = 0; + }else{ + j++; + } + } + fprintf(out, "};\n"); lineno++; + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of fallback tokens. + */ + if( lemp->has_fallback ){ + for(i=0; i<lemp->nterminal; i++){ + struct symbol *p = lemp->symbols[i]; + if( p->fallback==0 ){ + fprintf(out, " 0, /* %10s => nothing */\n", p->name); + }else{ + fprintf(out, " %3d, /* %10s => %s */\n", p->fallback->index, + p->name, p->fallback->name); + } + lineno++; + } + } + tplt_xfer(lemp->name, in, out, &lineno); + + /* Generate a table containing the symbolic name of every symbol + */ + for(i=0; i<lemp->nsymbol; i++){ + sprintf(line,"\"%s\",",lemp->symbols[i]->name); + fprintf(out," %-15s",line); + if( (i&3)==3 ){ fprintf(out,"\n"); lineno++; } + } + if( (i&3)!=0 ){ fprintf(out,"\n"); lineno++; } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate a table containing a text string that describes every + ** rule in the rule set of the grammer. This information is used + ** when tracing REDUCE actions. + */ + for(i=0, rp=lemp->rule; rp; rp=rp->next, i++){ + assert( rp->index==i ); + fprintf(out," /* %3d */ \"%s ::=", i, rp->lhs->name); + for(j=0; j<rp->nrhs; j++) fprintf(out," %s",rp->rhs[j]->name); + fprintf(out,"\",\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes every time a symbol is popped from + ** the stack while processing errors or while destroying the parser. + ** (In other words, generate the %destructor actions) + */ + if( lemp->tokendest ){ + for(i=0; i<lemp->nsymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type!=TERMINAL ) continue; + fprintf(out," case %d:\n",sp->index); lineno++; + } + for(i=0; i<lemp->nsymbol && lemp->symbols[i]->type!=TERMINAL; i++); + if( i<lemp->nsymbol ){ + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + } + for(i=0; i<lemp->nsymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || sp->destructor==0 ) continue; + fprintf(out," case %d:\n",sp->index); lineno++; + emit_destructor_code(out,lemp->symbols[i],lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + if( lemp->vardest ){ + struct symbol *dflt_sp = 0; + for(i=0; i<lemp->nsymbol; i++){ + struct symbol *sp = lemp->symbols[i]; + if( sp==0 || sp->type==TERMINAL || + sp->index<=0 || sp->destructor!=0 ) continue; + fprintf(out," case %d:\n",sp->index); lineno++; + dflt_sp = sp; + } + if( dflt_sp!=0 ){ + emit_destructor_code(out,dflt_sp,lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes whenever the parser stack overflows */ + tplt_print(out,lemp,lemp->overflow,lemp->overflowln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate the table of rule information + ** + ** Note: This code depends on the fact that rules are number + ** sequentually beginning with 0. + */ + for(rp=lemp->rule; rp; rp=rp->next){ + fprintf(out," { %d, %d },\n",rp->lhs->index,rp->nrhs); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which execution during each REDUCE action */ + for(rp=lemp->rule; rp; rp=rp->next){ + fprintf(out," case %d:\n",rp->index); lineno++; + emit_code(out,rp,lemp,&lineno); + fprintf(out," break;\n"); lineno++; + } + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes if a parse fails */ + tplt_print(out,lemp,lemp->failure,lemp->failureln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when a syntax error occurs */ + tplt_print(out,lemp,lemp->error,lemp->errorln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Generate code which executes when the parser accepts its input */ + tplt_print(out,lemp,lemp->accept,lemp->acceptln,&lineno); + tplt_xfer(lemp->name,in,out,&lineno); + + /* Append any addition code the user desires */ + tplt_print(out,lemp,lemp->extracode,lemp->extracodeln,&lineno); + + fclose(in); + fclose(out); + return; +} + +/* Generate a header file for the parser */ +void ReportHeader(lemp) +struct lemon *lemp; +{ + FILE *out, *in; + char *prefix; + char line[LINESIZE]; + char pattern[LINESIZE]; + int i; + + if( lemp->tokenprefix ) prefix = lemp->tokenprefix; + else prefix = ""; + in = file_open(lemp,".h","r"); + if( in ){ + for(i=1; i<lemp->nterminal && fgets(line,LINESIZE,in); i++){ + sprintf(pattern,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + if( strcmp(line,pattern) ) break; + } + fclose(in); + if( i==lemp->nterminal ){ + /* No change in the file. Don't rewrite it. */ + return; + } + } + out = file_open(lemp,".h","w"); + if( out ){ + for(i=1; i<lemp->nterminal; i++){ + fprintf(out,"#define %s%-30s %2d\n",prefix,lemp->symbols[i]->name,i); + } + fclose(out); + } + return; +} + +/* Reduce the size of the action tables, if possible, by making use +** of defaults. +** +** In this version, we take the most frequent REDUCE action and make +** it the default. Only default a reduce if there are more than one. +*/ +void CompressTables(lemp) +struct lemon *lemp; +{ + struct state *stp; + struct action *ap, *ap2; + struct rule *rp, *rp2, *rbest; + int nbest, n; + int i; + + for(i=0; i<lemp->nstate; i++){ + stp = lemp->sorted[i]; + nbest = 0; + rbest = 0; + + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type!=REDUCE ) continue; + rp = ap->x.rp; + if( rp==rbest ) continue; + n = 1; + for(ap2=ap->next; ap2; ap2=ap2->next){ + if( ap2->type!=REDUCE ) continue; + rp2 = ap2->x.rp; + if( rp2==rbest ) continue; + if( rp2==rp ) n++; + } + if( n>nbest ){ + nbest = n; + rbest = rp; + } + } + + /* Do not make a default if the number of rules to default + ** is not at least 2 */ + if( nbest<2 ) continue; + + + /* Combine matching REDUCE actions into a single default */ + for(ap=stp->ap; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) break; + } + assert( ap ); + ap->sp = Symbol_new("{default}"); + for(ap=ap->next; ap; ap=ap->next){ + if( ap->type==REDUCE && ap->x.rp==rbest ) ap->type = NOT_USED; + } + stp->ap = Action_sort(stp->ap); + } +} + +/***************** From the file "set.c" ************************************/ +/* +** Set manipulation routines for the LEMON parser generator. +*/ + +static int global_size = 0; + +/* Set the set size */ +void SetSize(n) +int n; +{ + global_size = n+1; +} + +/* Allocate a new set */ +char *SetNew(){ + char *s; + int i; + s = (char*)malloc( global_size ); + if( s==0 ){ + memory_error(); + } + for(i=0; i<global_size; i++) s[i] = 0; + return s; +} + +/* Deallocate a set */ +void SetFree(s) +char *s; +{ + free(s); +} + +/* Add a new element to the set. Return TRUE if the element was added +** and FALSE if it was already there. */ +int SetAdd(s,e) +char *s; +int e; +{ + int rv; + rv = s[e]; + s[e] = 1; + return !rv; +} + +/* Add every element of s2 to s1. Return TRUE if s1 changes. */ +int SetUnion(s1,s2) +char *s1; +char *s2; +{ + int i, progress; + progress = 0; + for(i=0; i<global_size; i++){ + if( s2[i]==0 ) continue; + if( s1[i]==0 ){ + progress = 1; + s1[i] = 1; + } + } + return progress; +} +/********************** From the file "table.c" ****************************/ +/* +** All code in this file has been automatically generated +** from a specification in the file +** "table.q" +** by the associative array code building program "aagen". +** Do not edit this file! Instead, edit the specification +** file, then rerun aagen. +*/ +/* +** Code for processing tables in the LEMON parser generator. +*/ + +PRIVATE int strhash(x) +char *x; +{ + unsigned int h = 0; + while( *x) h = h*13u + (unsigned int) *(x++); + return h; +} + +/* Works like strdup, sort of. Save a string in malloced memory, but +** keep strings in a table so that the same string is not in more +** than one place. +*/ +char *Strsafe(y) +char *y; +{ + char *z; + + z = Strsafe_find(y); + if( z==0 && (z=malloc( strlen(y)+1 ))!=0 ){ + strcpy(z,y); + Strsafe_insert(z); + } + MemoryCheck(z); + return z; +} + +/* There is one instance of the following structure for each +** associative array of type "x1". +*/ +struct s_x1 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x1node *tbl; /* The data stored here */ + struct s_x1node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x1". +*/ +typedef struct s_x1node { + char *data; /* The data */ + struct s_x1node *next; /* Next entry with the same hash */ + struct s_x1node **from; /* Previous link */ +} x1node; + +/* There is only one instance of the array, which is the following */ +static struct s_x1 *x1a; + +/* Allocate a new associative array */ +void Strsafe_init(){ + if( x1a ) return; + x1a = (struct s_x1*)malloc( sizeof(struct s_x1) ); + if( x1a ){ + x1a->size = 1024; + x1a->count = 0; + x1a->tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*1024 ); + if( x1a->tbl==0 ){ + free(x1a); + x1a = 0; + }else{ + int i; + x1a->ht = (x1node**)&(x1a->tbl[1024]); + for(i=0; i<1024; i++) x1a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Strsafe_insert(data) +char *data; +{ + x1node *np; + int h; + int ph; + + if( x1a==0 ) return 0; + ph = strhash(data); + h = ph & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x1a->count>=x1a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x1 array; + array.size = size = x1a->size*2; + array.count = x1a->count; + array.tbl = (x1node*)malloc( + (sizeof(x1node) + sizeof(x1node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x1node**)&(array.tbl[size]); + for(i=0; i<size; i++) array.ht[i] = 0; + for(i=0; i<x1a->count; i++){ + x1node *oldnp, *newnp; + oldnp = &(x1a->tbl[i]); + h = strhash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x1a->tbl); + /* *x1a = array; *//* copy 'array' */ + memcpy(x1a, &array, sizeof(array)); + } + /* Insert the new data */ + h = ph & (x1a->size-1); + np = &(x1a->tbl[x1a->count++]); + np->data = data; + if( x1a->ht[h] ) x1a->ht[h]->from = &(np->next); + np->next = x1a->ht[h]; + x1a->ht[h] = np; + np->from = &(x1a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +char *Strsafe_find(key) +char *key; +{ + int h; + x1node *np; + + if( x1a==0 ) return 0; + h = strhash(key) & (x1a->size-1); + np = x1a->ht[h]; + while( np ){ + if( strcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return a pointer to the (terminal or nonterminal) symbol "x". +** Create a new symbol if this is the first time "x" has been seen. +*/ +struct symbol *Symbol_new(x) +char *x; +{ + struct symbol *sp; + + sp = Symbol_find(x); + if( sp==0 ){ + sp = (struct symbol *)malloc( sizeof(struct symbol) ); + MemoryCheck(sp); + sp->name = Strsafe(x); + sp->type = isupper(*x) ? TERMINAL : NONTERMINAL; + sp->rule = 0; + sp->fallback = 0; + sp->prec = -1; + sp->assoc = UNK; + sp->firstset = 0; + sp->lambda = Bo_FALSE; + sp->destructor = 0; + sp->datatype = 0; + Symbol_insert(sp,sp->name); + } + return sp; +} + +/* Compare two symbols for working purposes +** +** Symbols that begin with upper case letters (terminals or tokens) +** must sort before symbols that begin with lower case letters +** (non-terminals). Other than that, the order does not matter. +** +** We find experimentally that leaving the symbols in their original +** order (the order they appeared in the grammar file) gives the +** smallest parser tables in SQLite. +*/ +int Symbolcmpp(struct symbol **a, struct symbol **b){ + int i1 = (**a).index + 10000000*((**a).name[0]>'Z'); + int i2 = (**b).index + 10000000*((**b).name[0]>'Z'); + return i1-i2; +} + +/* There is one instance of the following structure for each +** associative array of type "x2". +*/ +struct s_x2 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x2node *tbl; /* The data stored here */ + struct s_x2node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x2". +*/ +typedef struct s_x2node { + struct symbol *data; /* The data */ + char *key; /* The key */ + struct s_x2node *next; /* Next entry with the same hash */ + struct s_x2node **from; /* Previous link */ +} x2node; + +/* There is only one instance of the array, which is the following */ +static struct s_x2 *x2a; + +/* Allocate a new associative array */ +void Symbol_init(){ + if( x2a ) return; + x2a = (struct s_x2*)malloc( sizeof(struct s_x2) ); + if( x2a ){ + x2a->size = 128; + x2a->count = 0; + x2a->tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*128 ); + if( x2a->tbl==0 ){ + free(x2a); + x2a = 0; + }else{ + int i; + x2a->ht = (x2node**)&(x2a->tbl[128]); + for(i=0; i<128; i++) x2a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Symbol_insert(data,key) +struct symbol *data; +char *key; +{ + x2node *np; + int h; + int ph; + + if( x2a==0 ) return 0; + ph = strhash(key); + h = ph & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x2a->count>=x2a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x2 array; + array.size = size = x2a->size*2; + array.count = x2a->count; + array.tbl = (x2node*)malloc( + (sizeof(x2node) + sizeof(x2node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x2node**)&(array.tbl[size]); + for(i=0; i<size; i++) array.ht[i] = 0; + for(i=0; i<x2a->count; i++){ + x2node *oldnp, *newnp; + oldnp = &(x2a->tbl[i]); + h = strhash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x2a->tbl); + /* *x2a = array; *//* copy 'array' */ + memcpy(x2a, &array, sizeof(array)); + } + /* Insert the new data */ + h = ph & (x2a->size-1); + np = &(x2a->tbl[x2a->count++]); + np->key = key; + np->data = data; + if( x2a->ht[h] ) x2a->ht[h]->from = &(np->next); + np->next = x2a->ht[h]; + x2a->ht[h] = np; + np->from = &(x2a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct symbol *Symbol_find(key) +char *key; +{ + int h; + x2node *np; + + if( x2a==0 ) return 0; + h = strhash(key) & (x2a->size-1); + np = x2a->ht[h]; + while( np ){ + if( strcmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return the n-th data. Return NULL if n is out of range. */ +struct symbol *Symbol_Nth(n) +int n; +{ + struct symbol *data; + if( x2a && n>0 && n<=x2a->count ){ + data = x2a->tbl[n-1].data; + }else{ + data = 0; + } + return data; +} + +/* Return the size of the array */ +int Symbol_count() +{ + return x2a ? x2a->count : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct symbol **Symbol_arrayof() +{ + struct symbol **array; + int i,size; + if( x2a==0 ) return 0; + size = x2a->count; + array = (struct symbol **)malloc( sizeof(struct symbol *)*size ); + if( array ){ + for(i=0; i<size; i++) array[i] = x2a->tbl[i].data; + } + return array; +} + +/* Compare two configurations */ +int Configcmp(a,b) +struct config *a; +struct config *b; +{ + int x; + x = a->rp->index - b->rp->index; + if( x==0 ) x = a->dot - b->dot; + return x; +} + +/* Compare two states */ +PRIVATE int statecmp(a,b) +struct config *a; +struct config *b; +{ + int rc; + for(rc=0; rc==0 && a && b; a=a->bp, b=b->bp){ + rc = a->rp->index - b->rp->index; + if( rc==0 ) rc = a->dot - b->dot; + } + if( rc==0 ){ + if( a ) rc = 1; + if( b ) rc = -1; + } + return rc; +} + +/* Hash a state */ +PRIVATE int statehash(a) +struct config *a; +{ + unsigned int h=0; + while( a ){ + h = h*571u + (unsigned int)a->rp->index*37u + (unsigned int)a->dot; + a = a->bp; + } + return h; +} + +/* Allocate a new state structure */ +struct state *State_new() +{ + struct state *new; + new = (struct state *)malloc( sizeof(struct state) ); + MemoryCheck(new); + return new; +} + +/* There is one instance of the following structure for each +** associative array of type "x3". +*/ +struct s_x3 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x3node *tbl; /* The data stored here */ + struct s_x3node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x3". +*/ +typedef struct s_x3node { + struct state *data; /* The data */ + struct config *key; /* The key */ + struct s_x3node *next; /* Next entry with the same hash */ + struct s_x3node **from; /* Previous link */ +} x3node; + +/* There is only one instance of the array, which is the following */ +static struct s_x3 *x3a; + +/* Allocate a new associative array */ +void State_init(){ + if( x3a ) return; + x3a = (struct s_x3*)malloc( sizeof(struct s_x3) ); + if( x3a ){ + x3a->size = 128; + x3a->count = 0; + x3a->tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*128 ); + if( x3a->tbl==0 ){ + free(x3a); + x3a = 0; + }else{ + int i; + x3a->ht = (x3node**)&(x3a->tbl[128]); + for(i=0; i<128; i++) x3a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int State_insert(data,key) +struct state *data; +struct config *key; +{ + x3node *np; + int h; + int ph; + + if( x3a==0 ) return 0; + ph = statehash(key); + h = ph & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x3a->count>=x3a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x3 array; + array.size = size = x3a->size*2; + array.count = x3a->count; + array.tbl = (x3node*)malloc( + (sizeof(x3node) + sizeof(x3node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x3node**)&(array.tbl[size]); + for(i=0; i<size; i++) array.ht[i] = 0; + for(i=0; i<x3a->count; i++){ + x3node *oldnp, *newnp; + oldnp = &(x3a->tbl[i]); + h = statehash(oldnp->key) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->key = oldnp->key; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x3a->tbl); + /* *x3a = array; *//* copy 'array' */ + memcpy(x3a, &array, sizeof(array)); + } + /* Insert the new data */ + h = ph & (x3a->size-1); + np = &(x3a->tbl[x3a->count++]); + np->key = key; + np->data = data; + if( x3a->ht[h] ) x3a->ht[h]->from = &(np->next); + np->next = x3a->ht[h]; + x3a->ht[h] = np; + np->from = &(x3a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct state *State_find(key) +struct config *key; +{ + int h; + x3node *np; + + if( x3a==0 ) return 0; + h = statehash(key) & (x3a->size-1); + np = x3a->ht[h]; + while( np ){ + if( statecmp(np->key,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Return the size of the array */ +int State_count(void) +{ + return x3a ? x3a->count : 0; +} + +/* Return an array of pointers to all data in the table. +** The array is obtained from malloc. Return NULL if memory allocation +** problems, or if the array is empty. */ +struct state **State_arrayof() +{ + struct state **array; + int i,size; + if( x3a==0 ) return 0; + size = x3a->count; + array = (struct state **)malloc( sizeof(struct state *)*size ); + if( array ){ + for(i=0; i<size; i++) array[i] = x3a->tbl[i].data; + } + return array; +} + +/* Hash a configuration */ +PRIVATE int confighash(a) +struct config *a; +{ + int h=0; + h = h*571 + a->rp->index*37 + a->dot; + return h; +} + +/* There is one instance of the following structure for each +** associative array of type "x4". +*/ +struct s_x4 { + int size; /* The number of available slots. */ + /* Must be a power of 2 greater than or */ + /* equal to 1 */ + int count; /* Number of currently slots filled */ + struct s_x4node *tbl; /* The data stored here */ + struct s_x4node **ht; /* Hash table for lookups */ +}; + +/* There is one instance of this structure for every data element +** in an associative array of type "x4". +*/ +typedef struct s_x4node { + struct config *data; /* The data */ + struct s_x4node *next; /* Next entry with the same hash */ + struct s_x4node **from; /* Previous link */ +} x4node; + +/* There is only one instance of the array, which is the following */ +static struct s_x4 *x4a; + +/* Allocate a new associative array */ +void Configtable_init(){ + if( x4a ) return; + x4a = (struct s_x4*)malloc( sizeof(struct s_x4) ); + if( x4a ){ + x4a->size = 64; + x4a->count = 0; + x4a->tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*64 ); + if( x4a->tbl==0 ){ + free(x4a); + x4a = 0; + }else{ + int i; + x4a->ht = (x4node**)&(x4a->tbl[64]); + for(i=0; i<64; i++) x4a->ht[i] = 0; + } + } +} +/* Insert a new record into the array. Return TRUE if successful. +** Prior data with the same key is NOT overwritten */ +int Configtable_insert(data) +struct config *data; +{ + x4node *np; + int h; + int ph; + + if( x4a==0 ) return 0; + ph = confighash(data); + h = ph & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp(np->data,data)==0 ){ + /* An existing entry with the same key is found. */ + /* Fail because overwrite is not allows. */ + return 0; + } + np = np->next; + } + if( x4a->count>=x4a->size ){ + /* Need to make the hash table bigger */ + int i,size; + struct s_x4 array; + array.size = size = x4a->size*2; + array.count = x4a->count; + array.tbl = (x4node*)malloc( + (sizeof(x4node) + sizeof(x4node*))*size ); + if( array.tbl==0 ) return 0; /* Fail due to malloc failure */ + array.ht = (x4node**)&(array.tbl[size]); + for(i=0; i<size; i++) array.ht[i] = 0; + for(i=0; i<x4a->count; i++){ + x4node *oldnp, *newnp; + oldnp = &(x4a->tbl[i]); + h = confighash(oldnp->data) & (size-1); + newnp = &(array.tbl[i]); + if( array.ht[h] ) array.ht[h]->from = &(newnp->next); + newnp->next = array.ht[h]; + newnp->data = oldnp->data; + newnp->from = &(array.ht[h]); + array.ht[h] = newnp; + } + free(x4a->tbl); + /* *x4a = array; *//* copy 'array' */ + memcpy(x4a, &array, sizeof(array)); + } + /* Insert the new data */ + h = ph & (x4a->size-1); + np = &(x4a->tbl[x4a->count++]); + np->data = data; + if( x4a->ht[h] ) x4a->ht[h]->from = &(np->next); + np->next = x4a->ht[h]; + x4a->ht[h] = np; + np->from = &(x4a->ht[h]); + return 1; +} + +/* Return a pointer to data assigned to the given key. Return NULL +** if no such key. */ +struct config *Configtable_find(key) +struct config *key; +{ + int h; + x4node *np; + + if( x4a==0 ) return 0; + h = confighash(key) & (x4a->size-1); + np = x4a->ht[h]; + while( np ){ + if( Configcmp(np->data,key)==0 ) break; + np = np->next; + } + return np ? np->data : 0; +} + +/* Remove all data from the table. Pass each data to the function "f" +** as it is removed. ("f" may be null to avoid this step.) */ +void Configtable_clear(f) +int(*f)(/* struct config * */); +{ + int i; + if( x4a==0 || x4a->count==0 ) return; + if( f ) for(i=0; i<x4a->count; i++) (*f)(x4a->tbl[i].data); + for(i=0; i<x4a->size; i++) x4a->ht[i] = 0; + x4a->count = 0; + return; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/lempar.c b/data/lighttpd/lighttpd-1.4.53/src/lempar.c new file mode 100644 index 000000000..309967c49 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/lempar.c @@ -0,0 +1,699 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +*/ +/* First off, code is include which follows the "include" declaration +** in the input file. */ +#include "first.h" +#include <stdio.h> +%% +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +%% +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** ParseTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is ParseTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. +** ParseARG_SDECL A static variable declaration for the %extra_argument +** ParseARG_PDECL A parameter declaration for the %extra_argument +** ParseARG_STORE Code to store %extra_argument into yypParser +** ParseARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +%% +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* Next are that tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +%% +#define YY_SZ_ACTTAB (sizeof(yy_action)/sizeof(yy_action[0])) + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammer, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +%% +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + int stateno; /* The state-number */ + int major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ + int yyerrcnt; /* Shifts left before out of the error */ + ParseARG_SDECL /* A place to hold %extra_argument */ + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include <stdio.h> +static FILE *yyTraceFILE = NULL; +static char *yyTracePrompt = NULL; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +** <ul> +** <li> A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +** <li> A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +** </ul> +** +** Outputs: +** None. +*/ +#if 0 +void ParseTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *yyTokenName[] = { +%% +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *yyRuleName[] = { +%% +}; +#endif /* NDEBUG */ + +/* +** This function returns the symbolic name associated with a token +** value. +*/ +#if 0 +const char *ParseTokenName(int tokenType){ +#ifndef NDEBUG + if( tokenType>0 && (size_t)tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ + return yyTokenName[tokenType]; + }else{ + return "Unknown"; + } +#else + return ""; +#endif +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to Parse and ParseFree. +*/ +void *ParseAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){ + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ +%% + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos; + + if( pParser->yyidx<0 ) return 0; + yytos = &pParser->yystack[pParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor( yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +** <ul> +** <li> A pointer to the parser. This should be a pointer +** obtained from ParseAlloc. +** <li> A pointer to a function used to reclaim memory obtained +** from malloc. +** </ul> +*/ +void ParseFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + if( pParser==NULL ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); + (*freeProc)((void*)pParser); +} + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + int iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + /* if( pParser->yyidx<0 ) return YY_NO_ACTION; */ + i = yy_shift_ofst[stateno]; + if( i==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + if( iLookAhead==YYNOCODE ){ + return YY_NO_ACTION; + } + i += iLookAhead; + if( i<0 || (size_t)i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ +#ifdef YYFALLBACK + int iFallback; /* Fallback token */ + if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) + && (iFallback = yyFallback[iLookAhead])!=0 ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + yyParser *pParser, /* The parser */ + int iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + i = yy_reduce_ofst[stateno]; + if( i==YY_REDUCE_USE_DFLT ){ + return yy_default[stateno]; + } + if( iLookAhead==YYNOCODE ){ + return YY_NO_ACTION; + } + i += iLookAhead; + if( i<0 || (size_t)i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; + if( yypParser->yyidx>=YYSTACKDEPTH ){ + ParseARG_FETCH; + yypParser->yyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument var */ + return; + } + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = yyNewState; + yytos->major = yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { +%% +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ParseARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE ) { + if (yyruleno>=0 + && (size_t)yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } else { + return; /*(should not happen)*/ + } + } +#endif /* NDEBUG */ + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line <lineno> <grammarfile> + ** { ... } // User supplied code + ** #line <lineno> <thisfile> + ** break; + */ +%% + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yypParser,yygoto); + if( yyact < YYNSTATE ){ + yy_shift(yypParser,yyact,yygoto,&yygotominor); + }else if( yyact == YYNSTATE + YYNRULE + 1 ){ + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + ParseARG_FETCH; + UNUSED(yymajor); + UNUSED(yyminor); +#define TOKEN (yyminor.yy0) +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ParseARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ +%% + ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ParseAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +** <ul> +** <li> A pointer to the parser (an opaque structure.) +** <li> The major token number. +** <li> The minor token number. +** <li> An option argument of a grammar-specified type. +** </ul> +** +** Outputs: +** None. +*/ +void Parse( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ParseTOKENTYPE yyminor /* The value for the token */ + ParseARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ + int yyerrorhit = 0; /* True if yymajor has invoked an error */ + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ + if( yymajor==0 ) return; + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + ParseARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,yymajor); + if( yyact<YYNSTATE ){ + yy_shift(yypParser,yyact,yymajor,&yyminorunion); + yypParser->yyerrcnt--; + if( yyendofinput && yypParser->yyidx>=0 ){ + yymajor = 0; + }else{ + yymajor = YYNOCODE; + } + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else if( yyact == YY_ERROR_ACTION ){ + int yymx; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_shift_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + }else{ + yy_accept(yypParser); + yymajor = YYNOCODE; + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/lighttpd-angel.c b/data/lighttpd/lighttpd-1.4.53/src/lighttpd-angel.c new file mode 100644 index 000000000..189c1b92a --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/lighttpd-angel.c @@ -0,0 +1,167 @@ +#include "first.h" + +/** + * angel process for lighttpd + * + * the purpose is the run as root all the time and handle: + * - restart on crash + * - spawn on HUP to allow graceful restart + * - ... + * + * it has to stay safe and small to be trustable + */ + +#include <sys/wait.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <signal.h> + +#define BINPATH SBIN_DIR"/lighttpd" + +static siginfo_t last_sigterm_info; +static siginfo_t last_sighup_info; + +static volatile sig_atomic_t start_process = 1; +static volatile pid_t pid = -1; + +#define UNUSED(x) ( (void)(x) ) + +static void sigaction_handler(int sig, siginfo_t *si, void *context) { + int exitcode; + + UNUSED(context); + switch (sig) { + case SIGINT: + case SIGTERM: + case SIGUSR1: + if (pid <= 0) break; + memcpy(&last_sigterm_info, si, sizeof(*si)); + + /** forward the sig to the child */ + kill(pid, sig); + break; + case SIGHUP: /** do a graceful restart */ + if (pid <= 0) break; + memcpy(&last_sighup_info, si, sizeof(*si)); + + /** do a graceful shutdown on the main process and start a new child */ + kill(pid, SIGINT); + + usleep(5 * 1000); /** wait 5 microsec */ + + start_process = 1; + break; + case SIGCHLD: + /** a child died, de-combie it */ + wait(&exitcode); + break; + } +} + +int main(int argc, char **argv) { + int is_shutdown = 0; + struct sigaction act; + + UNUSED(argc); + *(const char **)&argv[0] = BINPATH; + #ifdef __COVERITY__ + __coverity_tainted_data_sanitize__(argv); + #endif + + /** + * we are running as root BEWARE + */ + + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); + sigaction(SIGUSR1, &act, NULL); + + act.sa_sigaction = sigaction_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGUSR1, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGALRM, &act, NULL); + sigaction(SIGCHLD, &act, NULL); + + /* check that the compiled in path has the right user, + * + * BEWARE: there is a race between the check here and the exec later + */ + + while (!is_shutdown) { + int exitcode = 0; + + if (start_process) { + pid = fork(); + + if (0 == pid) { + /* i'm the child */ + + /* intentionally pass argv params */ + /* coverity[tainted_string : FALSE] */ + execvp(argv[0], argv); + _exit(1); + } else if (-1 == pid) { + /** error */ + + return -1; + } + + /* I'm the angel */ + start_process = 0; + } + + if ((pid_t)-1 == waitpid(pid, &exitcode, 0)) { + switch (errno) { + case EINTR: + /* someone sent a signal ... + * do we have to shutdown or restart the process */ + break; + case ECHILD: + /** + * make sure we are not in a race between the signal handler + * and the process restart */ + if (!start_process) is_shutdown = 1; + break; + default: + break; + } + } else { + /** process went away */ + + if (WIFEXITED(exitcode)) { + /** normal exit */ + + is_shutdown = 1; + + fprintf(stderr, "%s.%d: child (pid=%d) exited normally with exitcode: %d\n", + __FILE__, __LINE__, + pid, + WEXITSTATUS(exitcode)); + + } else if (WIFSIGNALED(exitcode)) { + /** got a signal */ + + fprintf(stderr, "%s.%d: child (pid=%d) exited unexpectedly with signal %d, restarting\n", + __FILE__, __LINE__, + pid, + WTERMSIG(exitcode)); + + start_process = 1; + } + } + } + + return 0; +} + diff --git a/data/lighttpd/lighttpd-1.4.53/src/log.c b/data/lighttpd/lighttpd-1.4.53/src/log.c new file mode 100644 index 000000000..866ade394 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/log.c @@ -0,0 +1,216 @@ +#include "first.h" + +#include "base.h" +#include "log.h" + +#include <sys/types.h> +#include <errno.h> +#include <time.h> +#include <string.h> +#include <stdarg.h> +#include <unistd.h> + +#ifdef HAVE_SYSLOG_H +# include <syslog.h> +#endif + +#ifndef HAVE_CLOCK_GETTIME +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> /* gettimeofday() */ +#endif +#endif + +int log_clock_gettime_realtime (struct timespec *ts) { + #ifdef HAVE_CLOCK_GETTIME + return clock_gettime(CLOCK_REALTIME, ts); + #else + /* Mac OSX does not provide clock_gettime() + * e.g. defined(__APPLE__) && defined(__MACH__) */ + struct timeval tv; + gettimeofday(&tv, NULL); + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * 1000; + return 0; + #endif +} + +/* retry write on EINTR or when not all data was written */ +ssize_t write_all(int fd, const void* buf, size_t count) { + ssize_t written = 0; + + while (count > 0) { + ssize_t r = write(fd, buf, count); + if (r < 0) { + switch (errno) { + case EINTR: + /* try again */ + break; + default: + /* fail - repeating probably won't help */ + return -1; + } + } else if (0 == r) { + /* really shouldn't happen... */ + errno = EIO; + return -1; + } else { + force_assert(r <= (ssize_t) count); + written += r; + buf = r + (char const*) buf; + count -= r; + } + } + + return written; +} + +/* lowercase: append space, uppercase: don't */ +static void log_buffer_append_printf(buffer *out, const char *fmt, va_list ap) { + for(; *fmt; fmt++) { + int d; + char *s; + buffer *b; + off_t o; + + switch(*fmt) { + case 'S': /* string */ + case 's': /* string */ + s = va_arg(ap, char *); + buffer_append_string_c_escaped(out, s, (NULL != s) ? strlen(s) : 0); + break; + case 'B': /* buffer */ + case 'b': /* buffer */ + b = va_arg(ap, buffer *); + buffer_append_string_c_escaped(out, CONST_BUF_LEN(b)); + break; + case 'D': /* int */ + case 'd': /* int */ + d = va_arg(ap, int); + buffer_append_int(out, d); + break; + case 'O': /* off_t */ + case 'o': /* off_t */ + o = va_arg(ap, off_t); + buffer_append_int(out, o); + break; + case 'X': /* int (hex) */ + case 'x': /* int (hex) */ + d = va_arg(ap, int); + buffer_append_string_len(out, CONST_STR_LEN("0x")); + buffer_append_uint_hex(out, d); + break; + case '(': + case ')': + case '<': + case '>': + case ',': + case ' ': + buffer_append_string_len(out, fmt, 1); + break; + } + + if (*fmt >= 'a') { /* 's' 'b' 'd' 'o' 'x' */ + buffer_append_string_len(out, CONST_STR_LEN(" ")); + } + } +} + +static int log_buffer_prepare(buffer *b, server *srv, const char *filename, unsigned int line) { + switch(srv->errorlog_mode) { + case ERRORLOG_PIPE: + case ERRORLOG_FILE: + case ERRORLOG_FD: + if (-1 == srv->errorlog_fd) return -1; + /* cache the generated timestamp */ + if (srv->cur_ts != srv->last_generated_debug_ts) { + buffer_clear(srv->ts_debug_str); + buffer_append_strftime(srv->ts_debug_str, "%Y-%m-%d %H:%M:%S", localtime(&(srv->cur_ts))); + + srv->last_generated_debug_ts = srv->cur_ts; + } + + buffer_copy_buffer(b, srv->ts_debug_str); + buffer_append_string_len(b, CONST_STR_LEN(": (")); + break; + case ERRORLOG_SYSLOG: + /* syslog is generating its own timestamps */ + buffer_copy_string_len(b, CONST_STR_LEN("(")); + break; + } + + buffer_append_string(b, filename); + buffer_append_string_len(b, CONST_STR_LEN(".")); + buffer_append_int(b, line); + buffer_append_string_len(b, CONST_STR_LEN(") ")); + + return 0; +} + +static void log_write(server *srv, buffer *b) { + switch(srv->errorlog_mode) { + case ERRORLOG_PIPE: + case ERRORLOG_FILE: + case ERRORLOG_FD: + buffer_append_string_len(b, CONST_STR_LEN("\n")); + write_all(srv->errorlog_fd, CONST_BUF_LEN(b)); + break; + case ERRORLOG_SYSLOG: + syslog(LOG_ERR, "%s", b->ptr); + break; + } +} + +int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...) { + va_list ap; + + if (-1 == log_buffer_prepare(srv->errorlog_buf, srv, filename, line)) return 0; + + va_start(ap, fmt); + log_buffer_append_printf(srv->errorlog_buf, fmt, ap); + va_end(ap); + + log_write(srv, srv->errorlog_buf); + + return 0; +} + +int log_error_write_multiline_buffer(server *srv, const char *filename, unsigned int line, buffer *multiline, const char *fmt, ...) { + va_list ap; + size_t prefix_len; + buffer *b = srv->errorlog_buf; + char *pos, *end, *current_line; + + if (buffer_string_is_empty(multiline)) return 0; + + if (-1 == log_buffer_prepare(b, srv, filename, line)) return 0; + + va_start(ap, fmt); + log_buffer_append_printf(b, fmt, ap); + va_end(ap); + + prefix_len = buffer_string_length(b); + + current_line = pos = multiline->ptr; + end = multiline->ptr + buffer_string_length(multiline); + + for ( ; pos <= end ; ++pos) { + switch (*pos) { + case '\n': + case '\r': + case '\0': /* handles end of string */ + if (current_line < pos) { + /* truncate to prefix */ + buffer_string_set_length(b, prefix_len); + + buffer_append_string_len(b, current_line, pos - current_line); + log_write(srv, b); + } + current_line = pos + 1; + break; + default: + break; + } + } + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/log.h b/data/lighttpd/lighttpd-1.4.53/src/log.h new file mode 100644 index 000000000..815b6ab1a --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/log.h @@ -0,0 +1,16 @@ +#ifndef _LOG_H_ +#define _LOG_H_ +#include "first.h" + +#include "base_decls.h" +#include "buffer.h" + +struct timespec; /* declaration */ +int log_clock_gettime_realtime (struct timespec *ts); + +ssize_t write_all(int fd, const void* buf, size_t count); + +int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...); +int log_error_write_multiline_buffer(server *srv, const char *filename, unsigned int line, buffer *multiline, const char *fmt, ...); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/md5.c b/data/lighttpd/lighttpd-1.4.53/src/md5.c new file mode 100644 index 000000000..42fe466d4 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/md5.c @@ -0,0 +1,343 @@ +#include "first.h" + +/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + + +#include "md5.h" + +#define UINT4 uint32_t +#define UINT2 uint16_t +#define POINTER unsigned char * + +#if 0 /* Note: not defined here or in lighttpd local "md5.h" */ +#include "sys-crypto.h" /* USE_OPENSSL_CRYPTO */ +#endif + +#ifndef USE_OPENSSL_CRYPTO +#include <string.h> + +/* Constants for MD5Transform routine. + */ + +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void li_MD5Transform (UINT4 [4], const unsigned char [64]); +static void Encode (unsigned char *, UINT4 *, unsigned int); +static void Decode (UINT4 *, const unsigned char *, unsigned int); + +#ifdef HAVE_MEMCPY +#define MD5_memcpy(output, input, len) memcpy((output), (input), (len)) +#else +static void MD5_memcpy (POINTER, POINTER, unsigned int); +#endif +#ifdef HAVE_MEMSET +#define MD5_memset(output, value, len) memset((output), (value), (len)) +#else +static void MD5_memset (POINTER, int, unsigned int); +#endif + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* F, G, H and I are basic MD5 functions. + */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits. + */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +Rotation is separate from addition to prevent recomputation. + */ +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +/* MD5 initialization. Begins an MD5 operation, writing a new context. + */ +void li_MD5_Init (li_MD5_CTX *context) +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. +*/ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +/* MD5 block update operation. Continues an MD5 message-digest + operation, processing another message block, and updating the + context. + */ +void li_MD5_Update (li_MD5_CTX *context, const void *_input, unsigned int inputLen) +{ + unsigned int i, ndx, partLen; + const unsigned char *input = (const unsigned char*) _input; + + /* Compute number of bytes mod 64 */ + ndx = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - ndx; + + /* Transform as many times as possible. +*/ + if (inputLen >= partLen) { + MD5_memcpy + ((POINTER)&context->buffer[ndx], (POINTER)input, partLen); + li_MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + li_MD5Transform (context->state, &input[i]); + + ndx = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy + ((POINTER)&context->buffer[ndx], (POINTER)&input[i], + inputLen-i); +} + +/* MD5 finalization. Ends an MD5 message-digest operation, writing the + the message digest and zeroizing the context. + */ +void li_MD5_Final (unsigned char digest[16], li_MD5_CTX *context) +{ + unsigned char bits[8]; + unsigned int ndx, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. +*/ + ndx = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (ndx < 56) ? (56 - ndx) : (120 - ndx); + li_MD5_Update (context, PADDING, padLen); + + /* Append length (before padding) */ + li_MD5_Update (context, bits, 8); + + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. +*/ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +/* MD5 basic transformation. Transforms state based on block. + */ +static void li_MD5Transform (UINT4 state[4], const unsigned char block[64]) +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. + +*/ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +/* Encodes input (UINT4) into output (unsigned char). Assumes len is + a multiple of 4. + */ +static void Encode (unsigned char *output, UINT4 *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +/* Decodes input (unsigned char) into output (UINT4). Assumes len is + a multiple of 4. + */ +static void Decode (UINT4 *output, const unsigned char *input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +/* Note: Replace "for loop" with standard memcpy if possible. + */ +#ifndef HAVE_MEMCPY +static void MD5_memcpy (POINTER output, POINTER input, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) + + output[i] = input[i]; +} +#endif +/* Note: Replace "for loop" with standard memset if possible. + */ +#ifndef HAVE_MEMSET +static void MD5_memset (POINTER output, int value, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} +#endif +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/md5.h b/data/lighttpd/lighttpd-1.4.53/src/md5.h new file mode 100644 index 000000000..b8cb70039 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/md5.h @@ -0,0 +1,45 @@ +#ifndef LI_MD5_H +#define LI_MD5_H +#include "first.h" + +/* MD5.H - header file for MD5C.C + */ + +/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +rights reserved. + +License to copy and use this software is granted provided that it +is identified as the "RSA Data Security, Inc. MD5 Message-Digest +Algorithm" in all material mentioning or referencing this software +or this function. + +License is also granted to make and use derivative works provided +that such works are identified as "derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm" in all material +mentioning or referencing the derived work. + +RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided "as is" +without express or implied warranty of any kind. + +These notices must be retained in any copies of any part of this +documentation and/or software. + */ + +/* MD5 context. */ +typedef struct { + uint32_t state[4]; /* state (ABCD) */ + uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} li_MD5_CTX; + +#ifndef MD5_DIGEST_LENGTH +#define MD5_DIGEST_LENGTH 16 +#endif + +void li_MD5_Init (li_MD5_CTX *); +void li_MD5_Update (li_MD5_CTX *, const void *, unsigned int); +void li_MD5_Final (unsigned char [MD5_DIGEST_LENGTH], li_MD5_CTX *); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/meson.build b/data/lighttpd/lighttpd-1.4.53/src/meson.build new file mode 100644 index 000000000..842afaa45 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/meson.build @@ -0,0 +1,981 @@ +sbinddir = join_paths(get_option('prefix'), get_option('sbindir')) +moduledir = join_paths(get_option('prefix'), get_option('moduledir')) + +include_base_paths = [ + '/usr/include', + '/usr/local/include', +# '/opt/local/include', +] + +defs = [ + '-D_FILE_OFFSET_BITS=64', + '-D_LARGEFILE_SOURCE', + '-D_LARGE_FILES', + '-D_GNU_SOURCE', +] + +libws2_32 = [] +if target_machine.system() == 'windows' + libws2_32 = [ compiler.find_library('ws2_32') ] + defs += [ + '-DNVALGRIND', + ] +endif + + +compiler = meson.get_compiler('c') +conf_data = configuration_data() + +conf_data.set('HAVE_SYS_DEVPOLL_H', compiler.has_header('sys/devpoll.h')) +conf_data.set('HAVE_SYS_EPOLL_H', compiler.has_header('sys/epoll.h')) +conf_data.set('HAVE_SYS_EVENT_H', compiler.has_header('sys/event.h')) +conf_data.set('HAVE_SYS_MMAN_H', compiler.has_header('sys/mman.h')) +conf_data.set('HAVE_SYS_POLL_H', compiler.has_header('sys/poll.h')) +conf_data.set('HAVE_SYS_PORT_H', compiler.has_header('sys/port.h')) +conf_data.set('HAVE_SYS_PRCTL_H', compiler.has_header('sys/prctl.h')) +conf_data.set('HAVE_SYS_RESOURCE_H', compiler.has_header('sys/resource.h')) +conf_data.set('HAVE_SYS_SENDFILE_H', compiler.has_header('sys/sendfile.h')) +conf_data.set('HAVE_SYS_SELECT_H', compiler.has_header('sys/select.h')) +conf_data.set('HAVE_SYS_TYPES_H', compiler.has_header('sys/types.h')) +conf_data.set('HAVE_SYS_UIO_H', compiler.has_header('sys/uio.h')) +conf_data.set('HAVE_SYS_UN_H', compiler.has_header('sys/un.h')) +conf_data.set('HAVE_SYS_WAIT_H', compiler.has_header('sys/wait.h')) +conf_data.set('HAVE_SYS_TIME_H', compiler.has_header('sys/time.h')) +conf_data.set('HAVE_UNISTD_H', compiler.has_header('unistd.h')) +conf_data.set('HAVE_PTHREAD_H', compiler.has_header('pthread.h')) +conf_data.set('HAVE_GETOPT_H', compiler.has_header('getopt.h')) +conf_data.set('HAVE_INTTYPES_H', compiler.has_header('inttypes.h')) +conf_data.set('HAVE_POLL_H', compiler.has_header('poll.h')) +conf_data.set('HAVE_PWD_H', compiler.has_header('pwd.h')) +conf_data.set('HAVE_STDDEF_H', compiler.has_header('stddef.h')) +conf_data.set('HAVE_STDINT_H', compiler.has_header('stdint.h')) +conf_data.set('HAVE_STRINGS_H', compiler.has_header('strings.h')) +conf_data.set('HAVE_SYSLOG_H', compiler.has_header('syslog.h')) + +# check for fastcgi lib, for the tests only +conf_data.set('HAVE_FASTCGI_H', compiler.has_header('fastcgi.h')) +if not(conf_data.get('HAVE_FASTCGI_H')) + conf_data.set('HAVE_FASTCGI_FASTCGI_H', compiler.has_header('fastcgi/fastcgi.h')) +endif + +# will be needed for auth +conf_data.set('HAVE_CRYPT_H', compiler.has_header('crypt.h')) +if conf_data.get('HAVE_CRYPT_H') + # check if we need libcrypt for crypt_r / crypt + + # crypt_r in default libs? + if compiler.has_function('crypt_r', args: defs, prefix: '#include <crypt.h>') + libcrypt = [] + conf_data.set('HAVE_CRYPT_R', 1) + # crypt_r in -lcrypt ? + elif compiler.has_function('crypt_r', args: defs + ['-lcrypt'], prefix: '#include <crypt.h>') + libcrypt = [ compiler.find_library('crypt') ] + conf_data.set('HAVE_CRYPT_R', 1) + # crypt in default libs? + elif compiler.has_function('crypt', args: defs, prefix: '#include <crypt.h>') + libcrypt = [] + conf_data.set('HAVE_CRYPT', 1) + # crypt in -lcrypt ? + elif compiler.has_function('crypt', args: defs + ['-lcrypt'], prefix: '#include <crypt.h>') + libcrypt = [ compiler.find_library('crypt') ] + conf_data.set('HAVE_CRYPT', 1) + endif +endif + +conf_data.set('HAVE_SYS_INOTIFY_H', compiler.has_header('sys/inotify.h')) +if conf_data.get('HAVE_SYS_INOTIFY_H') + conf_data.set('HAVE_INOTIFY_INIT', compiler.has_function('inotify_init', args: defs)) +endif + +conf_data.set('HAVE_SOCKLEN_T', compiler.has_type('socklen_t', args: defs, prefix: '#include <sys/socket.h>')) + +conf_data.set('HAVE_SYS_RANDOM_H', compiler.has_header('sys/random.h')) +if conf_data.get('HAVE_SYS_RANDOM_H') + conf_data.set('HAVE_GETENTROPY', compiler.has_function( + 'getentropy', + args: defs, + prefix: '#include <sys/random.h>' + )) +endif + +conf_data.set('HAVE_LINUX_RANDOM_H', compiler.has_header('linux/random.h')) +if conf_data.get('HAVE_LINUX_RANDOM_H') + conf_data.set('HAVE_GETRANDOM', compiler.has_function( + 'getrandom', + args: defs, + prefix: '#include <linux/random.h>' + )) +endif + +conf_data.set('SIZEOF_LONG', compiler.sizeof('long', args: defs)) +conf_data.set('SIZEOF_OFF_T', compiler.sizeof('off_t', args: defs)) + +conf_data.set('HAVE_ARC4RANDOM_BUF', compiler.has_function('arc4random_buf', args: defs)) +conf_data.set('HAVE_CHROOT', compiler.has_function('chroot', args: defs)) +conf_data.set('HAVE_EPOLL_CTL', compiler.has_function('epoll_ctl', args: defs)) +conf_data.set('HAVE_FORK', compiler.has_function('fork', args: defs)) +conf_data.set('HAVE_GETLOADAVG', compiler.has_function('getloadavg', args: defs)) +conf_data.set('HAVE_GETRLIMIT', compiler.has_function('getrlimit', args: defs)) +conf_data.set('HAVE_GETUID', compiler.has_function('getuid', args: defs)) +conf_data.set('HAVE_GMTIME_R', compiler.has_function('gmtime_r', args: defs)) +conf_data.set('HAVE_INET_NTOP', compiler.has_function('inet_ntop', args: defs)) +conf_data.set('HAVE_JRAND48', compiler.has_function('jrand48', args: defs)) +conf_data.set('HAVE_KQUEUE', compiler.has_function('kqueue', args: defs)) +conf_data.set('HAVE_LOCALTIME_R', compiler.has_function('localtime_r', args: defs)) +conf_data.set('HAVE_LSTAT', compiler.has_function('lstat', args: defs)) +conf_data.set('HAVE_MADVISE', compiler.has_function('madvise', args: defs)) +conf_data.set('HAVE_MEMCPY', compiler.has_function('memcpy', args: defs)) +conf_data.set('HAVE_MEMSET', compiler.has_function('memset', args: defs)) +conf_data.set('HAVE_MMAP', compiler.has_function('mmap', args: defs)) +conf_data.set('HAVE_PATHCONF', compiler.has_function('pathconf', args: defs)) +conf_data.set('HAVE_PIPE2', compiler.has_function('pipe2', args: defs)) +conf_data.set('HAVE_POLL', compiler.has_function('poll', args: defs)) +conf_data.set('HAVE_PORT_CREATE', compiler.has_function('port_create', args: defs)) +conf_data.set('HAVE_PRCTL', compiler.has_function('prctl', args: defs)) +conf_data.set('HAVE_PREAD', compiler.has_function('pread', args: defs)) +conf_data.set('HAVE_POSIX_FADVISE', compiler.has_function('posix_fadvise', args: defs)) +conf_data.set('HAVE_SELECT', compiler.has_function('select', args: defs)) +conf_data.set('HAVE_SENDFILE', compiler.has_function('sendfile', args: defs)) +conf_data.set('HAVE_SEND_FILE', compiler.has_function('send_file', args: defs)) +conf_data.set('HAVE_SENDFILE64', compiler.has_function('sendfile64', args: defs)) +conf_data.set('HAVE_SENDFILEV', compiler.has_function('sendfilev', args: defs)) +conf_data.set('HAVE_SIGACTION', compiler.has_function('sigaction', args: defs)) +conf_data.set('HAVE_SIGNAL', compiler.has_function('signal', args: defs)) +conf_data.set('HAVE_SIGTIMEDWAIT', compiler.has_function('sigtimedwait', args: defs)) +conf_data.set('HAVE_SRANDOM', compiler.has_function('srandom', args: defs)) +conf_data.set('HAVE_STRPTIME', compiler.has_function('strptime', args: defs)) +conf_data.set('HAVE_SYSLOG', compiler.has_function('syslog', args: defs)) +conf_data.set('HAVE_WRITEV', compiler.has_function('writev', args: defs)) +conf_data.set('HAVE_INET_ATON', compiler.has_function('inet_aton', args: defs)) +conf_data.set('HAVE_ISSETUGID', compiler.has_function('issetugid', args: defs)) +conf_data.set('HAVE_INET_PTON', compiler.has_function('inet_pton', args: defs)) +conf_data.set('HAVE_MEMSET_S', compiler.has_function('memset_s', args: defs)) +conf_data.set('HAVE_EXPLICIT_BZERO', compiler.has_function('explicit_bzero', args: defs)) + +conf_data.set('HAVE_CLOCK_GETTIME', compiler.has_header_symbol('time.h', 'clock_gettime')) +clock_lib = [] +if not(conf_data.get('HAVE_CLOCK_GETTIME')) + if compiler.has_function('clock_gettime', args: defs + ['-lrt'], prefix: '#include <time.h>') + conf_data.set('HAVE_CLOCK_GETTIME', true) + clock_lib = [ compiler.find_library('rt') ] + endif +endif + +conf_data.set('HAVE_IPV6', compiler.compiles(''' + #include <sys/types.h> + #include <sys/socket.h> + #include <netinet/in.h> + + int main() { + struct sockaddr_in6 s; struct in6_addr t=in6addr_any; int i=AF_INET6; s; t.s6_addr[0] = 0; + return 0; + } +''', + name: 'IPv6 support', + args: defs +)) + +conf_data.set('HAVE_WEAK_SYMBOLS', compiler.compiles(''' + __attribute__((weak)) void __dummy(void *x) { } + int main() { + void *x; + __dummy(x); + } +''', + name: 'weak symbols', + args: defs +)) + +conf_data.set('HAVE_STRUCT_TM_GMTOFF', compiler.compiles(''' + #include <time.h> + int main(void) { + struct tm t; + t.tm_gmtoff = 0; + return 0; + } +''', + name: 'struct tm gmt offset', + args: defs +)) + +conf_data.set('LIGHTTPD_VERSION_ID', 10400) +conf_data.set_quoted('PACKAGE_NAME', meson.project_name()) +conf_data.set_quoted('PACKAGE_VERSION', meson.project_version()) +conf_data.set_quoted('LIBRARY_DIR', moduledir) + +conf_data.set('LIGHTTPD_STATIC', get_option('build_static')) +libdl = [] +if not(get_option('build_static')) + if target_machine.system() != 'windows' + libdl = [ compiler.find_library('dl') ] + if not(compiler.has_function('dlopen', args: defs, dependencies: libdl, prefix: '#include <dlfcn.h>')) + error('Couldn\'t find dlfcn.h or dlopen in lib dl') + endif + endif +endif + +libbz2 = [] +if get_option('with_bzip') + libbz2 = [ compiler.find_library('bz2') ] + if compiler.has_function('BZ2_bzCompress', args: defs, dependencies: libbz2, prefix: '#include <bzlib.h>') + conf_data.set('HAVE_BZLIB_H', true) + conf_data.set('HAVE_LIBBZ2', true) + else + error('Couldn\'t find bz2 header / library') + endif +endif + +if get_option('with_dbi') + libdbi = dependency('dbi', required: false) + if libdbi.found() + libdbi = [ libdbi ] + else + libdbi = [ compiler.find_library('dbi') ] + if not(compiler.has_function('dbi_conn_connect', args: defs, dependencies: libdbi, prefix: '#include <dbi/dbi.h>')) + error('Couldn\'t find dbi/dbi.h or dbi_conn_connect in lib dbi') + endif + endif + conf_data.set('HAVE_DBI', true) +endif + +libfam = [] +if get_option('with_fam') + libfam = [ compiler.find_library('fam') ] + if not(compiler.has_function('FAMOpen2', args: defs, dependencies: libfam, prefix: '#include <fam.h>')) + error('Couldn\'t find fam.h or FAMOpen2 in lib fam') + endif + conf_data.set('HAVE_FAM_H', true) +endif + +libgeoip = [] +if get_option('with_geoip') + libgeoip = dependency('geoip', required: false) + if libgeoip.found() + libgeoip = [ libgeoip ] + else + libgeoip = [ compiler.find_library('GeoIP') ] + if not(compiler.has_function('GeoIP_country_name_by_addr', args: defs, dependencies: libgeoip)) + error('Couldn\'t find GeoIP_country_name_by_addr in lib GeoIP') + endif + endif +endif + +libgdbm = [] +if get_option('with_gdbm') + libgdbm = [ compiler.find_library('gdbm') ] + if not(compiler.has_function('gdbm_open', args: defs, dependencies: libgdbm, prefix: '#include <gdbm.h>')) + error('Couldn\'t find gdbm.h or gdbm_open in lib gdbm') + endif + conf_data.set('HAVE_GDBM_H', true) + conf_data.set('HAVE_GDBM', true) +endif + +libkrb5 = [] +libgssapi_krb5 = [] +if get_option('with_krb5') + libkrb5 = dependency('krb5', required: false) + if libkrb5.found() + libkrb5 = [ libkrb5 ] + else + libkrb5 = [ compiler.find_library('krb5') ] + if not(compiler.has_function('krb5_init_context', args: defs, dependencies: libkrb5)) + error('Couldn\'t find krb5_init_context in lib krb5') + endif + endif + + libgssapi_krb5 = dependency('krb5-gssapi', required: false) + if libgssapi_krb5.found() + libgssapi_krb5 = [ libgssapi_krb5 ] + else + libgssapi_krb5 = [ compiler.find_library('gssapi_krb5') ] + endif + + conf_data.set('HAVE_KRB5', true) +endif + +libldap = [] +liblber = [] +if get_option('with_ldap') + libldap = [ compiler.find_library('ldap') ] + if not(compiler.has_function('ldap_sasl_bind_s', + args: defs, + dependencies: libldap, + prefix: ''' + #include <ldap.h> + ''' + )) + error('Couldn\'t find ldap.h or ldap_bind in lib libldap') + endif + conf_data.set('HAVE_LDAP_H', true) + conf_data.set('HAVE_LIBLDAP', true) + liblber = [ compiler.find_library('lber') ] + if not(compiler.has_function('ber_printf', args: defs, dependencies: liblber, prefix: '#include <lber.h>')) + error('Couldn\'t find lber.h or ber_printf in lib liblber') + endif + conf_data.set('HAVE_LBER_H', true) + conf_data.set('HAVE_LIBLBER', true) +endif + +libpam = [] +if get_option('with_pam') + libpam = [ compiler.find_library('pam') ] + if not(compiler.has_function('pam_start', + args: defs, + dependencies: libpam, + prefix: ''' + #include <security/pam_appl.h> + ''' + )) + error('Couldn\'t find security/pam_appl.h or pam_start in lib libpam') + endif + conf_data.set('HAVE_PAM', true) +endif + +libev = [] +if get_option('with_libev') + libev = dependency('ev', required: false) + if libev.found() + libev = [ libev ] + elif compiler.has_header('ev.h') and compiler.has_function('ev_time', args: defs + ['-lev']) + libev = [ compiler.find_library('ev') ] + else + error('Couldn\'t find libev header / library') + endif + conf_data.set('HAVE_LIBEV', true) +endif + +libunwind = [] +if get_option('with_libunwind') + libunwind = [ dependency('libunwind') ] +endif + +liblua = [] +if get_option('with_lua') + found_lua = false + foreach l: ['lua5.3', 'lua-5.3', 'lua5.2', 'lua-5.2', 'lua5.1', 'lua-5.1', 'lua'] + if not(found_lua) + liblua = dependency(l, required: false) + found_lua = liblua.found() + endif + endforeach + if not(found_lua) + error('Couldn\'t find any lua library') + endif + liblua = [ liblua ] + conf_data.set('HAVE_LUA_H', true) +endif + +libmemcached = [] +if get_option('with_memcached') + # manual search: + # header: libmemcached/memcached.h + # function: memcached (-lmemcached) + libmemcached = [ dependency('libmemcached') ] + conf_data.set('USE_MEMCACHED', true) +endif + +libmysqlclient = [] +if get_option('with_mysql') + # manual search: extend include path with 'mysql/' + # header: mysql.h + # function: mysql_real_connect (-lmariadb) + libmysqlclient = [ dependency('mariadb') ] + #-# function: mysql_real_connect (-lmysqlclient) + #-libmysqlclient = [ dependency('mysqlclient') ] + conf_data.set('HAVE_MYSQL', true) +endif + +libssl = [] +libcrypto = [] +if get_option('with_openssl') + # manual search: + # header: openssl/ssl.h + # function: SSL_new (-lssl) + # function: BIO_f_base64 (-lcrypto) + libssl = [ dependency('libssl') ] + libcrypto = [ dependency('libcrypto') ] + conf_data.set('HAVE_OPENSSL_SSL_H', true) + conf_data.set('HAVE_LIBSSL', true) +endif +if get_option('with_wolfssl') != 'false' + # manual search: + # header: wolfssl/ssl.h + # function: wolfSSL_Init (-lwolfssl) + p = get_option('with_wolfssl') + if p == 'true' + p = '/usr/local' + endif + i = include_directories(p+'/include', p+'/include/wolfssl') + libwolfssl_includes_dep = [ declare_dependency(include_directories: i) ] + libcrypto = [ compiler.find_library('libwolfssl', dirs: [ p+'/lib', p+'/lib64' ]) ] + libcrypto += libwolfssl_includes_dep + conf_data.set('HAVE_WOLFSSL_SSL_H', true) +endif + +libpcre = [] +if get_option('with_pcre') + # manual search: + # header: pcre.h + # function: pcre_exec (-lpcre) + libpcre = [ dependency('libpcre') ] + conf_data.set('HAVE_PCRE_H', true) + conf_data.set('HAVE_LIBPCRE', true) +endif + +libpq = [] +if get_option('with_pgsql') + # manual search: + # header: libpq-fe.h + # function: PQsetdbLogin (-lpq) + libpq = [ dependency('libpq') ] + conf_data.set('HAVE_PGSQL', true) +endif + +if get_option('with_sasl') + libsasl = dependency('sasl2', required: false) + if libsasl.found() + libsasl = [ libsasl2 ] + else + libsasl = [ compiler.find_library('sasl2') ] + if not(compiler.has_function('sasl_server_init', args: defs, dependencies: libsasl, prefix: '#include <sasl/sasl.h>')) + error('Couldn\'t find sasl/sasl.h or sasl_server_init in libsasl2') + endif + endif + conf_data.set('HAVE_SASL', true) +endif + +#if get_option('with_valgrind') +#endif + +libuuid = [] +if get_option('with_webdav_locks') + libuuid = dependency('uuid', required: false) + if libuuid.found() + libuuid = [ libuuid ] + elif compiler.has_function('uuid_generate', args: defs, prefix: '#include <uuid/uuid.h>') + # uuid_generate in libc, everything is fine, no lib needed + libuuid = [] + else + libuuid = compiler.find_library('uuid') + if not(compiler.has_function('uuid_generate', + args: defs, + dependencies: libuuid, + prefix: '#include <uuid/uuid.h>' + )) + error('Couldn\'t find uuid/uuid.h or uuid_generate in lib c and uuid') + endif + endif + conf_data.set('HAVE_UUID', true) + conf_data.set('HAVE_UUID_UUID_H', true) +endif + +libxml2 = [] +libsqlite3 = [] +if get_option('with_webdav_props') + libxml2 = dependency('libxml-2.0', required: false) + if libxml2.found() + libxml2 = [ libxml2 ] + else + libxml2_includes = [] + libxml2_includes_dep = [] + libxml2_found_header = compiler.has_header('libxml/tree.h') + foreach i: include_base_paths + if not(libxml2_found_header) + message('Searching in ' + join_paths(i, 'libxml2')) + i = include_directories(join_paths(i, 'libxml2')) + if compiler.has_header('libxml/tree.h', include_directories: i) + libxml2_found_header = true + libxml2_includes = [ i ] + libxml2_includes_dep = [ declare_dependency(include_directories: i) ] + endif + endif + endforeach + if not(libxml2_found_header) + error('Couldn\'t find libxml/tree.h') + endif + libxml2 = [ compiler.find_library('xml2') ] + if not(compiler.has_function('xmlParseChunk', + args: defs, + dependencies: libxml2, + include_directories: libxml2_includes, + prefix: ''' + #include <libxml/tree.h> + ''' + )) + error('Couldn\'t find xmlParseChunk in lib xml2') + endif + # has_function doesn't like "internal dependencies" + libxml2 += libxml2_includes_dep + endif + conf_data.set('HAVE_LIBXML_H', true) + + libsqlite3 = dependency('sqlite31', required: false) + if libsqlite3.found() + libsqlite3 = [ libsqlite3 ] + else + libsqlite3 = [ compiler.find_library('sqlite3') ] + if not(compiler.has_function('sqlite3_reset', + args: defs, + dependencies: libsqlite3, + prefix: ''' + #include <sqlite3.h> + ''' + )) + error('Couldn\'t find sqlite3.h or sqlite3_reset in lib sqlite3') + endif + endif + conf_data.set('HAVE_SQLITE3_H', true) +endif + +libattr = [] +if get_option('with_xattr') + libattr = [ compiler.find_library('attr') ] + if not(compiler.has_function('attr_get', + args: defs, + dependencies: libattr, + prefix: ''' + #include <sys/types.h> + #include <attr/attributes.h> + ''' + )) + error('Couldn\'t find attr/attributes.h or attr_get in lib attr') + endif + conf_data.set('HAVE_ATTR_ATTRIBUTES_H', true) + conf_data.set('HAVE_XATTR', true) +endif + +libz = [] +if get_option('with_zlib') + libz = dependency('zlib', required: false) + if libz.found() + libz = [ libz ] + else + # windows alternative names? 'zlib', 'zdll' + libz = [ compiler.find_library('z') ] + if not(compiler.has_function('deflate', args: defs, dependencies: libz, prefix: '#include <zlib.h>')) + error('Couldn\'t find z header / library') + endif + endif + conf_data.set('HAVE_ZLIB_H', true) + conf_data.set('HAVE_LIBZ', true) +endif + +configure_file( + output : 'config.h', + configuration : conf_data, +) + +common_src = [ + 'algo_sha1.c', + 'array.c', + 'base64.c', + 'buffer.c', + 'burl.c', + 'chunk.c', + 'configfile-glue.c', + 'connections-glue.c', + 'crc32.c', + 'data_array.c', + 'data_config.c', + 'data_integer.c', + 'data_string.c', + 'etag.c', + 'fdevent_freebsd_kqueue.c', + 'fdevent_libev.c', + 'fdevent_linux_sysepoll.c', + 'fdevent_poll.c', + 'fdevent_select.c', + 'fdevent_solaris_devpoll.c', + 'fdevent_solaris_port.c', + 'fdevent.c', + 'gw_backend.c', + 'http_auth.c', + 'http_chunk.c', + 'http_header.c', + 'http_kv.c', + 'http_vhostdb.c', + 'http-header-glue.c', + 'joblist.c', + 'keyvalue.c', + 'log.c', + 'md5.c', + 'plugin.c', + 'rand.c', + 'request.c', + 'safe_memclear.c', + 'sock_addr.c', + 'splaytree.c', + 'stat_cache.c', + 'stream.c', + 'vector.c', +] +if target_machine.system() == 'windows' + common_src += [ 'xgetopt.c' ] +endif +main_src = [ + 'configfile.c', + 'connections.c', + 'inet_ntop_cache.c', + 'network_write.c', + 'network.c', + 'response.c', + 'server.c', +] + +lemon = executable('lemon', + sources: 'lemon.c', + native: true, +) +# generator doesn't handle additional "input dependencies" like lempar.c +# => use custom_target +configparser = custom_target('configparser', + input: ['configparser.y', 'lempar.c'], + output: ['configparser.c', 'configparser.h'], + command: [lemon, '-q', 'o=@OUTDIR@', '@INPUT0@', '@INPUT1@'], +) +ssi_exprparser = custom_target('mod_ssi_exprparser', + input: ['mod_ssi_exprparser.y', 'lempar.c'], + output: ['mod_ssi_exprparser.c', 'mod_ssi_exprparser.h'], + command: [lemon, '-q', 'o=@OUTDIR@', '@INPUT0@', '@INPUT1@'], +) + +common_cflags = defs + [ + '-DHAVE_CONFIG_H', +] + +if compiler.get_id() == 'gcc' or compiler.get_id() == 'clang' + common_cflags += [ + '-Wall', + '-g', + '-Wshadow', + '-W', + '-pedantic', + ] + if get_option('build_extra_warnings') + common_cflags += get_option('warn_cflags').split() + endif +endif + +common_flags = [ declare_dependency( + compile_args: common_cflags, + # tests also use common_flags, and need this + include_directories: include_directories('.'), +) ] + +lighttpd_flags = [] +lighttpd_angel_flags = [] +if target_machine.system() == 'windows' + lighttpd_flags += [ declare_dependency( + compile_args: [ + '-DLI_DECLARE_EXPORTS', + ], + ) ] + if compiler.get_id() == 'gcc' + libmsvcr70 = [ compiler.find_library('msvcr70') ] + lighttpd_flags += libmsvcr70 + [ declare_dependency( + link_args: [ + '-Wl,-subsystem,console', + ], + ) ] + lighttpd_angel_flags += libmsvcr70 + [ declare_dependency( + link_args: [ + '-Wl,-subsystem,console', + ], + ) ] + endif +endif + +if compiler.get_id() == 'gcc' or target_machine.system() != 'darwin' + lighttpd_flags += [ declare_dependency( + link_args: [ + '-Wl,-export-dynamic', + ], + ) ] +endif + +executable('lighttpd-angel', + sources: 'lighttpd-angel.c', + dependencies: common_flags + lighttpd_angel_flags, + c_args: ['-DSBIN_DIR="' + sbinddir + '"'], + install: true, + install_dir: sbinddir, +) + +executable('lighttpd', configparser, + sources: common_src + main_src, + # libssl needed? + dependencies: [ common_flags, lighttpd_flags + , libattr + , libcrypto + , libdl + , libev + , libfam + , libpcre + , libunwind + , libws2_32 + ], + install: true, + install_dir: sbinddir, +) + +test('test_array', executable('test_array', + sources: ['t/test_array.c', 'array.c', 'data_array.c', 'data_integer.c', 'data_string.c', 'buffer.c'], + dependencies: common_flags + libunwind, + build_by_default: false, +)) + +test('test_buffer', executable('test_buffer', + sources: ['t/test_buffer.c', 'buffer.c'], + dependencies: common_flags + libunwind, + build_by_default: false, +)) + +test('test_burl', executable('test_burl', + sources: ['t/test_burl.c', 'burl.c', 'buffer.c', 'base64.c'], + dependencies: common_flags + libunwind, + build_by_default: false, +)) + +test('test_base64', executable('test_base64', + sources: ['t/test_base64.c', 'buffer.c', 'base64.c'], + dependencies: common_flags + libunwind, + build_by_default: false, +)) + +test('test_configfile', executable('test_configfile', + sources: [ + 't/test_configfile.c', + 'buffer.c', + 'array.c', + 'data_config.c', + 'data_integer.c', + 'data_string.c', + 'http_header.c', + 'http_kv.c', + 'vector.c', + 'log.c', + 'sock_addr.c', + ], + dependencies: common_flags + libpcre + libunwind, + build_by_default: false, +)) + +test('test_keyvalue', executable('test_keyvalue', + sources: [ + 't/test_keyvalue.c', + 'burl.c', + 'buffer.c', + 'base64.c', + 'array.c', + 'data_integer.c', + 'data_string.c', + 'log.c', + ], + dependencies: common_flags + libpcre + libunwind, + build_by_default: false, +)) + +test('test_mod_access', executable('test_mod_access', + sources: [ + 't/test_mod_access.c', + 'configfile-glue.c', + 'buffer.c', + 'array.c', + 'data_config.c', + 'data_integer.c', + 'data_string.c', + 'http_header.c', + 'http_kv.c', + 'vector.c', + 'log.c', + 'sock_addr.c', + ], + dependencies: common_flags + libpcre + libunwind, + build_by_default: false, +)) + +test('test_mod_evhost', executable('test_mod_evhost', + sources: [ + 't/test_mod_evhost.c', + 'configfile-glue.c', + 'buffer.c', + 'array.c', + 'data_config.c', + 'data_integer.c', + 'data_string.c', + 'http_header.c', + 'http_kv.c', + 'vector.c', + 'log.c', + 'sock_addr.c', + ], + dependencies: common_flags + libpcre + libunwind, + build_by_default: false, +)) + +test('test_mod_simple_vhost', executable('test_mod_simple_vhost', + sources: [ + 't/test_mod_simple_vhost.c', + 'configfile-glue.c', + 'buffer.c', + 'array.c', + 'data_config.c', + 'data_integer.c', + 'data_string.c', + 'http_header.c', + 'http_kv.c', + 'vector.c', + 'log.c', + 'sock_addr.c', + ], + dependencies: common_flags + libpcre + libunwind, + build_by_default: false, +)) + +test('test_request', executable('test_request', + sources: [ + 't/test_request.c', + 'request.c', + 'buffer.c', + 'array.c', + 'data_integer.c', + 'data_string.c', + 'http_header.c', + 'http_kv.c', + 'log.c', + 'sock_addr.c', + ], + dependencies: common_flags + libunwind, + build_by_default: false, +)) + +modules = [ + [ 'mod_access', [ 'mod_access.c' ] ], + [ 'mod_accesslog', [ 'mod_accesslog.c' ] ], + [ 'mod_alias', [ 'mod_alias.c' ] ], + [ 'mod_auth', [ 'mod_auth.c' ] ], + [ 'mod_authn_file', [ 'mod_authn_file.c' ], [ libcrypt, libcrypto ] ], + [ 'mod_compress', [ 'mod_compress.c' ], libbz2 + libz ], + [ 'mod_deflate', [ 'mod_deflate.c' ], libbz2 + libz ], + [ 'mod_dirlisting', [ 'mod_dirlisting.c' ], libpcre ], + [ 'mod_evasive', [ 'mod_evasive.c' ] ], + [ 'mod_evhost', [ 'mod_evhost.c' ] ], + [ 'mod_expire', [ 'mod_expire.c' ] ], + [ 'mod_extforward', [ 'mod_extforward.c' ] ], + [ 'mod_fastcgi', [ 'mod_fastcgi.c' ], libws2_32 ], + [ 'mod_flv_streaming', [ 'mod_flv_streaming.c' ] ], + [ 'mod_indexfile', [ 'mod_indexfile.c' ] ], + [ 'mod_proxy', [ 'mod_proxy.c' ], libws2_32 ], + [ 'mod_redirect', [ 'mod_redirect.c' ], libpcre ], + [ 'mod_rewrite', [ 'mod_rewrite.c' ], libpcre ], + [ 'mod_rrdtool', [ 'mod_rrdtool.c' ] ], + [ 'mod_scgi', [ 'mod_scgi.c' ], libws2_32 ], + [ 'mod_secdownload', [ 'mod_secdownload.c' ], libcrypto ], + [ 'mod_setenv', [ 'mod_setenv.c' ] ], + [ 'mod_simple_vhost', [ 'mod_simple_vhost.c' ] ], + [ 'mod_sockproxy', [ 'mod_sockproxy.c' ] ], + [ 'mod_ssi', [ ssi_exprparser, 'mod_ssi_expr.c', 'mod_ssi.c' ], libws2_32 ], + [ 'mod_staticfile', [ 'mod_staticfile.c' ] ], + [ 'mod_status', [ 'mod_status.c' ] ], + [ 'mod_uploadprogress', [ 'mod_uploadprogress.c' ] ], + [ 'mod_userdir', [ 'mod_userdir.c' ] ], + [ 'mod_usertrack', [ 'mod_usertrack.c' ] ], + [ 'mod_vhostdb', [ 'mod_vhostdb.c' ] ], + [ 'mod_webdav', [ 'mod_webdav.c' ], libsqlite3 + libuuid + libxml2 ], + [ 'mod_wstunnel', [ 'mod_wstunnel.c' ], libcrypto ], +] + +if target_machine.system() != 'windows' + modules += [ + [ 'mod_cgi', [ 'mod_cgi.c' ] ], + ] +endif + +if get_option('with_pcre') and (get_option('with_memcached') or get_option('with_gdbm')) + modules += [ + [ 'mod_trigger_b4_dl', [ 'mod_trigger_b4_dl.c' ], libpcre + libmemcached + libgdbm ], + ] +endif + +if get_option('with_lua') + modules += [ + [ 'mod_cml', [ 'mod_cml.c', 'mod_cml_lua.c', 'mod_cml_funcs.c' ], liblua + libmemcached ], + [ 'mod_magnet', [ 'mod_magnet.c', 'mod_magnet_cache.c' ], liblua ], + ] +endif + +if get_option('with_geoip') + modules += [ + [ 'mod_geoip', [ 'mod_geoip.c' ], libgeoip ], + ] +endif + +if get_option('with_mysql') + modules += [ + [ 'mod_authn_mysql', [ 'mod_authn_mysql.c' ], libcrypt + libmysqlclient ], + [ 'mod_mysql_vhost', [ 'mod_mysql_vhost.c' ], libmysqlclient ], + [ 'mod_vhostdb_mysql', [ 'mod_vhostdb_mysql.c' ], libmysqlclient ], + ] +endif + +if get_option('with_pgsql') + modules += [ + [ 'mod_vhostdb_pgsql', [ 'mod_vhostdb_pgsql.c' ], libpq ], + ] +endif + +if get_option('with_dbi') + modules += [ + [ 'mod_vhostdb_dbi', [ 'mod_vhostdb_dbi.c' ], libdbi ], + ] +endif + +if get_option('with_krb5') + modules += [ + [ 'mod_authn_gssapi', [ 'mod_authn_gssapi.c' ], libkrb5 + libgssapi_krb5 ], + ] +endif + +if get_option('with_ldap') + modules += [ + [ 'mod_authn_ldap', [ 'mod_authn_ldap.c' ], libldap + liblber ], + [ 'mod_vhostdb_ldap', [ 'mod_vhostdb_ldap.c' ], libldap + liblber ], + ] +endif + +if get_option('with_openssl') + modules += [ + [ 'mod_openssl', [ 'mod_openssl.c' ], libssl + libcrypto ], + ] +endif + +if get_option('with_wolfssl') != 'false' + modules += [ + [ 'mod_openssl', [ 'mod_openssl.c' ], libcrypto ], + ] +endif + +if get_option('with_pam') + modules += [ + [ 'mod_authn_pam', [ 'mod_authn_pam.c' ], libpam ], + ] +endif + +if get_option('with_sasl') + modules += [ + [ 'mod_authn_sasl', [ 'mod_authn_sasl.c' ], libsasl ], + ] +endif + +foreach mod: modules + mod_name = mod.get(0) + mod_sources = mod.get(1) + mod_deps = mod.length() > 2 ? mod.get(2) : [] + shared_module(mod_name, + sources: mod_sources, + dependencies: [ common_flags, mod_deps ], + name_prefix: '', + install: true, + install_dir: moduledir, + ) +endforeach diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_access.c b/data/lighttpd/lighttpd-1.4.53/src/mod_access.c new file mode 100644 index 000000000..9798f8844 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_access.c @@ -0,0 +1,216 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" + +#include "plugin.h" + +#include <stdlib.h> +#include <string.h> + +typedef struct { + array *access_allow; + array *access_deny; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +INIT_FUNC(mod_access_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + return p; +} + +FREE_FUNC(mod_access_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->access_allow); + array_free(s->access_deny); + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_access_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "url.access-deny", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { "url.access-allow", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->access_deny = array_init(); + s->access_allow = array_init(); + + cv[0].destination = s->access_deny; + cv[1].destination = s->access_allow; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_vlist(s->access_deny)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for url.access-deny; expected list of \"suffix\""); + return HANDLER_ERROR; + } + + if (!array_is_vlist(s->access_allow)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for url.access-allow; expected list of \"suffix\""); + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_access_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(access_allow); + PATCH(access_deny); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.access-deny"))) { + PATCH(access_deny); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.access-allow"))) { + PATCH(access_allow); + } + } + } + + return 0; +} +#undef PATCH + +static int mod_access_check (const array *allow, const array *deny, const buffer *urlpath, const int lc) { + + if (allow->used) { + const buffer *match = (!lc) + ? array_match_value_suffix(allow, urlpath) + : array_match_value_suffix_nc(allow, urlpath); + return (match != NULL); /* allowed if match; denied if none matched */ + } + + if (deny->used) { + const buffer *match = (!lc) + ? array_match_value_suffix(deny, urlpath) + : array_match_value_suffix_nc(deny, urlpath); + return (match == NULL); /* deny if match; allow if none matched */ + } + + return 1; /* allowed (not denied) */ +} + +/** + * URI handler + * + * we will get called twice: + * - after the clean up of the URL and + * - after the pathinfo checks are done + * + * this handles the issue of trailing slashes + */ +URIHANDLER_FUNC(mod_access_uri_handler) { + plugin_data *p = p_d; + if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; + + mod_access_patch_connection(srv, con, p); + + if (0 == p->conf.access_allow->used && 0 == p->conf.access_deny->used) { + return HANDLER_GO_ON; /* access allowed; nothing to match */ + } + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", + "-- mod_access_uri_handler called"); + } + + if (mod_access_check(p->conf.access_allow, p->conf.access_deny, + con->uri.path, con->conf.force_lowercase_filenames)) { + return HANDLER_GO_ON; /* access allowed */ + } + + /* (else) access denied */ + if (con->conf.log_request_handling) { + if (p->conf.access_allow->used) { + log_error_write(srv, __FILE__, __LINE__, "sb", "url denied as failed to match any from access_allow", con->uri.path); + } + else { + log_error_write(srv, __FILE__, __LINE__, "sb", "url denied as we match access_deny", con->uri.path); + } + } + + con->http_status = 403; + con->mode = DIRECT; + return HANDLER_FINISHED; +} + + +int mod_access_plugin_init(plugin *p); +int mod_access_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("access"); + + p->init = mod_access_init; + p->set_defaults = mod_access_set_defaults; + p->handle_uri_clean = mod_access_uri_handler; + p->handle_subrequest_start = mod_access_uri_handler; + p->cleanup = mod_access_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_accesslog.c b/data/lighttpd/lighttpd-1.4.53/src/mod_accesslog.c new file mode 100644 index 000000000..5a64cf1dd --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_accesslog.c @@ -0,0 +1,1167 @@ +#include "first.h" + +#include "base.h" +#include "fdevent.h" +#include "log.h" +#include "buffer.h" +#include "http_header.h" +#include "sock_addr.h" + +#include "plugin.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <time.h> + +#ifdef HAVE_SYSLOG_H +# include <syslog.h> +#endif + +typedef struct { + char key; + enum { + FORMAT_UNSET, + FORMAT_UNSUPPORTED, + FORMAT_PERCENT, + FORMAT_REMOTE_HOST, + FORMAT_REMOTE_IDENT, + FORMAT_REMOTE_USER, + FORMAT_TIMESTAMP, + FORMAT_REQUEST_LINE, + FORMAT_STATUS, + FORMAT_BYTES_OUT_NO_HEADER, + FORMAT_HEADER, + + FORMAT_REMOTE_ADDR, + FORMAT_LOCAL_ADDR, + FORMAT_COOKIE, + FORMAT_TIME_USED_US, + FORMAT_ENV, + FORMAT_FILENAME, + FORMAT_REQUEST_PROTOCOL, + FORMAT_REQUEST_METHOD, + FORMAT_SERVER_PORT, + FORMAT_QUERY_STRING, + FORMAT_TIME_USED, + FORMAT_URL, + FORMAT_SERVER_NAME, + FORMAT_HTTP_HOST, + FORMAT_CONNECTION_STATUS, + FORMAT_BYTES_IN, + FORMAT_BYTES_OUT, + + FORMAT_KEEPALIVE_COUNT, + FORMAT_RESPONSE_HEADER, + FORMAT_NOTE + } type; +} format_mapping; + +/** + * + * + * "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" + * + */ + +static const format_mapping fmap[] = +{ + { '%', FORMAT_PERCENT }, + { 'h', FORMAT_REMOTE_HOST }, + { 'l', FORMAT_REMOTE_IDENT }, + { 'u', FORMAT_REMOTE_USER }, + { 't', FORMAT_TIMESTAMP }, + { 'r', FORMAT_REQUEST_LINE }, + { 's', FORMAT_STATUS }, + { 'b', FORMAT_BYTES_OUT_NO_HEADER }, + { 'i', FORMAT_HEADER }, + + { 'a', FORMAT_REMOTE_ADDR }, + { 'A', FORMAT_LOCAL_ADDR }, + { 'B', FORMAT_BYTES_OUT_NO_HEADER }, + { 'C', FORMAT_COOKIE }, + { 'D', FORMAT_TIME_USED_US }, + { 'e', FORMAT_ENV }, + { 'f', FORMAT_FILENAME }, + { 'H', FORMAT_REQUEST_PROTOCOL }, + { 'k', FORMAT_KEEPALIVE_COUNT }, + { 'm', FORMAT_REQUEST_METHOD }, + { 'n', FORMAT_NOTE }, + { 'p', FORMAT_SERVER_PORT }, + { 'P', FORMAT_UNSUPPORTED }, /* we are only one process */ + { 'q', FORMAT_QUERY_STRING }, + { 'T', FORMAT_TIME_USED }, + { 'U', FORMAT_URL }, /* w/o querystring */ + { 'v', FORMAT_SERVER_NAME }, + { 'V', FORMAT_HTTP_HOST }, + { 'X', FORMAT_CONNECTION_STATUS }, + { 'I', FORMAT_BYTES_IN }, + { 'O', FORMAT_BYTES_OUT }, + + { 'o', FORMAT_RESPONSE_HEADER }, + + { '\0', FORMAT_UNSET } +}; + + +enum e_optflags_time { + /* format string is passed to strftime unless other format optflags set + * (besides FORMAT_FLAG_TIME_BEGIN or FORMAT_FLAG_TIME_END) */ + FORMAT_FLAG_TIME_END = 0x00,/* use request end time (default) */ + FORMAT_FLAG_TIME_BEGIN = 0x01,/* use request start time */ + FORMAT_FLAG_TIME_SEC = 0x02,/* request time as num sec since epoch */ + FORMAT_FLAG_TIME_MSEC = 0x04,/* request time as num msec since epoch */ + FORMAT_FLAG_TIME_USEC = 0x08,/* request time as num usec since epoch */ + FORMAT_FLAG_TIME_NSEC = 0x10,/* request time as num nsec since epoch */ + FORMAT_FLAG_TIME_MSEC_FRAC = 0x20,/* request time msec fraction */ + FORMAT_FLAG_TIME_USEC_FRAC = 0x40,/* request time usec fraction */ + FORMAT_FLAG_TIME_NSEC_FRAC = 0x80 /* request time nsec fraction */ +}; + +enum e_optflags_port { + FORMAT_FLAG_PORT_LOCAL = 0x01,/* (default) */ + FORMAT_FLAG_PORT_REMOTE = 0x02 +}; + + +typedef struct { + enum { FIELD_UNSET, FIELD_STRING, FIELD_FORMAT } type; + + buffer *string; + int field; + int opt; +} format_field; + +typedef struct { + format_field **ptr; + + size_t used; + size_t size; +} format_fields; + +typedef struct { + buffer *access_logfile; + int log_access_fd; + buffer *access_logbuffer; /* each logfile has a separate buffer */ + + unsigned short use_syslog; /* syslog has global buffer */ + unsigned short syslog_level; + + buffer *format; + + time_t last_generated_accesslog_ts; + time_t *last_generated_accesslog_ts_ptr; + + buffer *ts_accesslog_str; + + format_fields *parsed_format; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + plugin_config conf; + + buffer *syslog_logbuffer; /* syslog has global buffer. no caching, always written directly */ +} plugin_data; + +INIT_FUNC(mod_accesslog_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + p->syslog_logbuffer = buffer_init(); + + return p; +} + +static void accesslog_write_all(server *srv, const buffer *filename, int fd, const void* buf, size_t count) { + if (-1 == write_all(fd, buf, count)) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "writing access log entry failed:", filename, strerror(errno)); + } +} + +static void accesslog_append_escaped(buffer *dest, buffer *str) { + char *ptr, *start, *end; + + /* replaces non-printable chars with \xHH where HH is the hex representation of the byte */ + /* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */ + if (buffer_string_is_empty(str)) return; + buffer_string_prepare_append(dest, buffer_string_length(str)); + + for (ptr = start = str->ptr, end = str->ptr + buffer_string_length(str); ptr < end; ptr++) { + unsigned char const c = (unsigned char) *ptr; + if (c >= ' ' && c <= '~' && c != '"' && c != '\\') { + /* nothing to change, add later as one block */ + } else { + /* copy previous part */ + if (start < ptr) { + buffer_append_string_len(dest, start, ptr - start); + } + start = ptr + 1; + + switch (c) { + case '"': + BUFFER_APPEND_STRING_CONST(dest, "\\\""); + break; + case '\\': + BUFFER_APPEND_STRING_CONST(dest, "\\\\"); + break; + case '\b': + BUFFER_APPEND_STRING_CONST(dest, "\\b"); + break; + case '\n': + BUFFER_APPEND_STRING_CONST(dest, "\\n"); + break; + case '\r': + BUFFER_APPEND_STRING_CONST(dest, "\\r"); + break; + case '\t': + BUFFER_APPEND_STRING_CONST(dest, "\\t"); + break; + case '\v': + BUFFER_APPEND_STRING_CONST(dest, "\\v"); + break; + default: { + /* non printable char => \xHH */ + char hh[5] = {'\\','x',0,0,0}; + char h = c / 16; + hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0'); + h = c % 16; + hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0'); + buffer_append_string_len(dest, &hh[0], 4); + } + break; + } + } + } + + if (start < end) { + buffer_append_string_len(dest, start, end - start); + } +} + +static int accesslog_parse_format(server *srv, format_fields *fields, buffer *format) { + size_t i, j, k = 0, start = 0; + + if (buffer_is_empty(format)) return -1; + + for (i = 0; i < buffer_string_length(format); i++) { + switch(format->ptr[i]) { + case '%': + if (i > 0 && start != i) { + /* copy the string before this % */ + if (fields->size == 0) { + fields->size = 16; + fields->used = 0; + fields->ptr = malloc(fields->size * sizeof(format_field * )); + } else if (fields->used == fields->size) { + fields->size += 16; + fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * )); + } + + fields->ptr[fields->used] = malloc(sizeof(format_field)); + fields->ptr[fields->used]->type = FIELD_STRING; + fields->ptr[fields->used]->string = buffer_init(); + + buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start); + + fields->used++; + } + + /* we need a new field */ + + if (fields->size == 0) { + fields->size = 16; + fields->used = 0; + fields->ptr = malloc(fields->size * sizeof(format_field * )); + } else if (fields->used == fields->size) { + fields->size += 16; + fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * )); + } + + /* search for the terminating command */ + switch (format->ptr[i+1]) { + case '>': + case '<': + /* after the } has to be a character */ + if (format->ptr[i+2] == '\0') { + log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a format-specifier"); + return -1; + } + + + for (j = 0; fmap[j].key != '\0'; j++) { + if (fmap[j].key != format->ptr[i+2]) continue; + + /* found key */ + + fields->ptr[fields->used] = malloc(sizeof(format_field)); + fields->ptr[fields->used]->type = FIELD_FORMAT; + fields->ptr[fields->used]->field = fmap[j].type; + fields->ptr[fields->used]->string = NULL; + fields->ptr[fields->used]->opt = 0; + + fields->used++; + + break; + } + + if (fmap[j].key == '\0') { + log_error_write(srv, __FILE__, __LINE__, "s", "%< and %> have to be followed by a valid format-specifier"); + return -1; + } + + start = i + 3; + i = start - 1; /* skip the string */ + + break; + case '{': + /* go forward to } */ + + for (k = i+2; k < buffer_string_length(format); k++) { + if (format->ptr[k] == '}') break; + } + + if (k == buffer_string_length(format)) { + log_error_write(srv, __FILE__, __LINE__, "s", "%{ has to be terminated by a }"); + return -1; + } + + /* after the } has to be a character */ + if (format->ptr[k+1] == '\0') { + log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a format-specifier"); + return -1; + } + + if (k == i + 2) { + log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to contain a string"); + return -1; + } + + for (j = 0; fmap[j].key != '\0'; j++) { + if (fmap[j].key != format->ptr[k+1]) continue; + + /* found key */ + + fields->ptr[fields->used] = malloc(sizeof(format_field)); + fields->ptr[fields->used]->type = FIELD_FORMAT; + fields->ptr[fields->used]->field = fmap[j].type; + fields->ptr[fields->used]->string = buffer_init(); + fields->ptr[fields->used]->opt = 0; + + buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + i + 2, k - (i + 2)); + + fields->used++; + + break; + } + + if (fmap[j].key == '\0') { + log_error_write(srv, __FILE__, __LINE__, "s", "%{...} has to be followed by a valid format-specifier"); + return -1; + } + + start = k + 2; + i = start - 1; /* skip the string */ + + break; + default: + /* after the % has to be a character */ + if (format->ptr[i+1] == '\0') { + log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a format-specifier"); + return -1; + } + + for (j = 0; fmap[j].key != '\0'; j++) { + if (fmap[j].key != format->ptr[i+1]) continue; + + /* found key */ + + fields->ptr[fields->used] = malloc(sizeof(format_field)); + fields->ptr[fields->used]->type = FIELD_FORMAT; + fields->ptr[fields->used]->field = fmap[j].type; + fields->ptr[fields->used]->string = NULL; + fields->ptr[fields->used]->opt = 0; + + fields->used++; + + break; + } + + if (fmap[j].key == '\0') { + log_error_write(srv, __FILE__, __LINE__, "s", "% has to be followed by a valid format-specifier"); + return -1; + } + + start = i + 2; + i = start - 1; /* skip the string */ + + break; + } + + break; + } + } + + if (start < i) { + /* copy the string */ + if (fields->size == 0) { + fields->size = 16; + fields->used = 0; + fields->ptr = malloc(fields->size * sizeof(format_field * )); + } else if (fields->used == fields->size) { + fields->size += 16; + fields->ptr = realloc(fields->ptr, fields->size * sizeof(format_field * )); + } + + fields->ptr[fields->used] = malloc(sizeof(format_field)); + fields->ptr[fields->used]->type = FIELD_STRING; + fields->ptr[fields->used]->string = buffer_init(); + + buffer_copy_string_len(fields->ptr[fields->used]->string, format->ptr + start, i - start); + + fields->used++; + } + + return 0; +} + +FREE_FUNC(mod_accesslog_free) { + plugin_data *p = p_d; + size_t i; + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + if (!buffer_string_is_empty(s->access_logbuffer)) { + if (s->log_access_fd != -1) { + accesslog_write_all(srv, s->access_logfile, s->log_access_fd, CONST_BUF_LEN(s->access_logbuffer)); + } + } + + if (s->log_access_fd != -1) { + if (buffer_string_is_empty(s->access_logfile) || *s->access_logfile->ptr != '|') { + close(s->log_access_fd); + } /*(else piped loggers closed in fdevent_close_logger_pipes())*/ + } + + buffer_free(s->ts_accesslog_str); + buffer_free(s->access_logbuffer); + buffer_free(s->format); + buffer_free(s->access_logfile); + + if (s->parsed_format) { + size_t j; + for (j = 0; j < s->parsed_format->used; j++) { + if (s->parsed_format->ptr[j]->string) buffer_free(s->parsed_format->ptr[j]->string); + free(s->parsed_format->ptr[j]); + } + free(s->parsed_format->ptr); + free(s->parsed_format); + } + + free(s); + } + + free(p->config_storage); + } + + if (p->syslog_logbuffer) buffer_free(p->syslog_logbuffer); + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(log_access_open) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "accesslog.filename", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "accesslog.use-syslog", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, + { "accesslog.format", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "accesslog.syslog-level", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->access_logfile = buffer_init(); + s->format = buffer_init(); + s->access_logbuffer = buffer_init(); + s->ts_accesslog_str = buffer_init(); + s->log_access_fd = -1; + s->last_generated_accesslog_ts = 0; + s->last_generated_accesslog_ts_ptr = &(s->last_generated_accesslog_ts); + s->syslog_level = LOG_INFO; + + + cv[0].destination = s->access_logfile; + cv[1].destination = &(s->use_syslog); + cv[2].destination = s->format; + cv[3].destination = &(s->syslog_level); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (i == 0 && buffer_string_is_empty(s->format)) { + /* set a default logfile string */ + + buffer_copy_string_len(s->format, CONST_STR_LEN("%h %V %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"")); + } + + /* parse */ + + if (!buffer_is_empty(s->format)) { + size_t j, tcount = 0; + + s->parsed_format = calloc(1, sizeof(*(s->parsed_format))); + + if (-1 == accesslog_parse_format(srv, s->parsed_format, s->format)) { + + log_error_write(srv, __FILE__, __LINE__, "sb", + "parsing accesslog-definition failed:", s->format); + + return HANDLER_ERROR; + } + + for (j = 0; j < s->parsed_format->used; ++j) { + format_field * const f = s->parsed_format->ptr[j]; + if (FIELD_FORMAT != f->type) continue; + if (FORMAT_TIMESTAMP == f->field) { + if (!buffer_string_is_empty(f->string)) { + const char *ptr = f->string->ptr; + if (0 == strncmp(ptr, "begin:", sizeof("begin:")-1)) { + f->opt |= FORMAT_FLAG_TIME_BEGIN; + ptr += sizeof("begin:")-1; + } else if (0 == strncmp(ptr, "end:", sizeof("end:")-1)) { + f->opt |= FORMAT_FLAG_TIME_END; + ptr += sizeof("end:")-1; + } + if (0 == strcmp(ptr, "sec")) f->opt |= FORMAT_FLAG_TIME_SEC; + else if (0 == strcmp(ptr, "msec")) f->opt |= FORMAT_FLAG_TIME_MSEC; + else if (0 == strcmp(ptr, "usec")) f->opt |= FORMAT_FLAG_TIME_USEC; + else if (0 == strcmp(ptr, "nsec")) f->opt |= FORMAT_FLAG_TIME_NSEC; + else if (0 == strcmp(ptr, "msec_frac")) f->opt |= FORMAT_FLAG_TIME_MSEC_FRAC; + else if (0 == strcmp(ptr, "usec_frac")) f->opt |= FORMAT_FLAG_TIME_USEC_FRAC; + else if (0 == strcmp(ptr, "nsec_frac")) f->opt |= FORMAT_FLAG_TIME_NSEC_FRAC; + else if (NULL == strchr(ptr, '%')) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "constant string for time format (misspelled token? or missing '%'):", s->format); + + return HANDLER_ERROR; + } + } + + /* make sure they didn't try to send the timestamp in twice + * (would invalidate s->ts_accesslog_str cache of timestamp str) */ + if (!(f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END|FORMAT_FLAG_TIME_SEC)) && ++tcount > 1) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "you may not use strftime timestamp format %{}t twice in the same access log:", s->format); + + return HANDLER_ERROR; + } + + if (f->opt & FORMAT_FLAG_TIME_BEGIN) srv->srvconf.high_precision_timestamps = 1; + } else if (FORMAT_TIME_USED_US == f->field) { + f->opt |= FORMAT_FLAG_TIME_USEC; + srv->srvconf.high_precision_timestamps = 1; + } else if (FORMAT_TIME_USED == f->field) { + if (buffer_string_is_empty(f->string) + || buffer_is_equal_string(f->string, CONST_STR_LEN("s")) + || buffer_is_equal_string(f->string, CONST_STR_LEN("sec"))) f->opt |= FORMAT_FLAG_TIME_SEC; + else if (buffer_is_equal_string(f->string, CONST_STR_LEN("ms")) + || buffer_is_equal_string(f->string, CONST_STR_LEN("msec"))) f->opt |= FORMAT_FLAG_TIME_MSEC; + else if (buffer_is_equal_string(f->string, CONST_STR_LEN("us")) + || buffer_is_equal_string(f->string, CONST_STR_LEN("usec"))) f->opt |= FORMAT_FLAG_TIME_USEC; + else if (buffer_is_equal_string(f->string, CONST_STR_LEN("ns")) + || buffer_is_equal_string(f->string, CONST_STR_LEN("nsec"))) f->opt |= FORMAT_FLAG_TIME_NSEC; + else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "invalid time unit in %{UNIT}T:", s->format); + + return HANDLER_ERROR; + } + + if (f->opt & ~(FORMAT_FLAG_TIME_SEC)) srv->srvconf.high_precision_timestamps = 1; + } else if (FORMAT_COOKIE == f->field) { + if (buffer_string_is_empty(f->string)) f->type = FIELD_STRING; /*(blank)*/ + } else if (FORMAT_SERVER_PORT == f->field) { + if (buffer_string_is_empty(f->string)) + f->opt |= FORMAT_FLAG_PORT_LOCAL; + else if (buffer_is_equal_string(f->string, CONST_STR_LEN("canonical"))) + f->opt |= FORMAT_FLAG_PORT_LOCAL; + else if (buffer_is_equal_string(f->string, CONST_STR_LEN("local"))) + f->opt |= FORMAT_FLAG_PORT_LOCAL; + else if (buffer_is_equal_string(f->string, CONST_STR_LEN("remote"))) + f->opt |= FORMAT_FLAG_PORT_REMOTE; + else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "invalid format %{canonical,local,remote}p:", s->format); + + return HANDLER_ERROR; + } + } + } + +#if 0 + /* debugging */ + for (j = 0; j < s->parsed_format->used; j++) { + switch (s->parsed_format->ptr[j]->type) { + case FIELD_FORMAT: + log_error_write(srv, __FILE__, __LINE__, "ssds", + "config:", "format", s->parsed_format->ptr[j]->field, + s->parsed_format->ptr[j]->string ? + s->parsed_format->ptr[j]->string->ptr : "" ); + break; + case FIELD_STRING: + log_error_write(srv, __FILE__, __LINE__, "ssbs", "config:", "string '", s->parsed_format->ptr[j]->string, "'"); + break; + default: + break; + } + } +#endif + } + + if (s->use_syslog) { + /* ignore the next checks */ + continue; + } + + if (buffer_string_is_empty(s->access_logfile)) continue; + + if (srv->srvconf.preflight_check) continue; + + if (-1 == (s->log_access_fd = fdevent_open_logger(s->access_logfile->ptr))) { + log_error_write(srv, __FILE__, __LINE__, "SBSS", + "opening log '", s->access_logfile, + "' failed: ", strerror(errno)); + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +static void log_access_flush(server *srv, void *p_d) { + plugin_data *p = p_d; + size_t i; + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (!buffer_string_is_empty(s->access_logbuffer)) { + if (s->log_access_fd != -1) { + accesslog_write_all(srv, s->access_logfile, s->log_access_fd, CONST_BUF_LEN(s->access_logbuffer)); + } + + buffer_clear(s->access_logbuffer); + } + } +} + +TRIGGER_FUNC(log_access_periodic_flush) { + /* flush buffered access logs every 4 seconds */ + if (0 == (srv->cur_ts & 3)) log_access_flush(srv, p_d); + return HANDLER_GO_ON; +} + +SIGHUP_FUNC(log_access_cycle) { + plugin_data *p = p_d; + size_t i; + + if (!p->config_storage) return HANDLER_GO_ON; + + log_access_flush(srv, p); + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + if (s->use_syslog == 0 + && !buffer_string_is_empty(s->access_logfile) + && s->access_logfile->ptr[0] != '|') { + + if (-1 == fdevent_cycle_logger(s->access_logfile->ptr, &s->log_access_fd)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "cycling access-log failed:", strerror(errno)); + return HANDLER_ERROR; + } + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_accesslog_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(access_logfile); + PATCH(log_access_fd); + PATCH(last_generated_accesslog_ts_ptr); + PATCH(access_logbuffer); + PATCH(ts_accesslog_str); + PATCH(parsed_format); + PATCH(use_syslog); + PATCH(syslog_level); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.filename"))) { + PATCH(access_logfile); + PATCH(log_access_fd); + PATCH(access_logbuffer); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.format"))) { + PATCH(parsed_format); + PATCH(last_generated_accesslog_ts_ptr); + PATCH(ts_accesslog_str); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.use-syslog"))) { + PATCH(use_syslog); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.syslog-level"))) { + PATCH(syslog_level); + } + } + } + + return 0; +} +#undef PATCH + +REQUESTDONE_FUNC(log_access_write) { + plugin_data *p = p_d; + buffer *b; + size_t j; + + int newts = 0; + buffer *vb; + struct timespec ts = { 0, 0 }; + + mod_accesslog_patch_connection(srv, con, p); + + /* No output device, nothing to do */ + if (!p->conf.use_syslog && p->conf.log_access_fd == -1) return HANDLER_GO_ON; + + if (p->conf.use_syslog) { + b = p->syslog_logbuffer; + } else { + b = p->conf.access_logbuffer; + } + + for (j = 0; j < p->conf.parsed_format->used; j++) { + const format_field * const f = p->conf.parsed_format->ptr[j]; + switch(f->type) { + case FIELD_STRING: + buffer_append_string_buffer(b, f->string); + break; + case FIELD_FORMAT: + switch(f->field) { + case FORMAT_TIMESTAMP: + + if (f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END)) { + if (f->opt & FORMAT_FLAG_TIME_SEC) { + time_t t = (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) ? srv->cur_ts : con->request_start; + buffer_append_int(b, (intmax_t)t); + } else if (f->opt & (FORMAT_FLAG_TIME_MSEC|FORMAT_FLAG_TIME_USEC|FORMAT_FLAG_TIME_NSEC)) { + off_t t; /*(expected to be 64-bit since large file support enabled)*/ + long ns; + if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) { + if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts); + t = (off_t)ts.tv_sec; + ns = ts.tv_nsec; + } else { + t = (off_t)con->request_start_hp.tv_sec; + ns = con->request_start_hp.tv_nsec; + } + if (f->opt & FORMAT_FLAG_TIME_MSEC) { + t *= 1000; + t += (ns + 999999) / 1000000; /* ceil */ + } else if (f->opt & FORMAT_FLAG_TIME_USEC) { + t *= 1000000; + t += (ns + 999) / 1000; /* ceil */ + } else {/*(f->opt & FORMAT_FLAG_TIME_NSEC)*/ + t *= 1000000000; + t += ns; + } + buffer_append_int(b, (intmax_t)t); + } else { /*(FORMAT_FLAG_TIME_MSEC_FRAC|FORMAT_FLAG_TIME_USEC_FRAC|FORMAT_FLAG_TIME_NSEC_FRAC)*/ + long ns; + char *ptr; + if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) { + if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts); + ns = ts.tv_nsec; + } else { + ns = con->request_start_hp.tv_nsec; + } + /*assert(t < 1000000000);*/ + if (f->opt & FORMAT_FLAG_TIME_MSEC_FRAC) { + ns += 999999; /* ceil */ + ns /= 1000000; + buffer_append_string_len(b, CONST_STR_LEN("000")); + } else if (f->opt & FORMAT_FLAG_TIME_USEC_FRAC) { + ns += 999; /* ceil */ + ns /= 1000; + buffer_append_string_len(b, CONST_STR_LEN("000000")); + } else {/*(f->opt & FORMAT_FLAG_TIME_NSEC_FRAC)*/ + buffer_append_string_len(b, CONST_STR_LEN("000000000")); + } + for (ptr = b->ptr + buffer_string_length(b); ns > 0; ns /= 10) + *--ptr = (ns % 10) + '0'; + } + } else if (!(f->opt & FORMAT_FLAG_TIME_BEGIN) && srv->cur_ts == *(p->conf.last_generated_accesslog_ts_ptr)) { + buffer_append_string_buffer(b, p->conf.ts_accesslog_str); + } else { + /* cache the generated timestamp (only if ! FORMAT_FLAG_TIME_BEGIN) */ + struct tm *tmptr; + time_t t; + #if defined(HAVE_STRUCT_TM_GMTOFF) + # ifdef HAVE_LOCALTIME_R + struct tm tm; + # endif /* HAVE_LOCALTIME_R */ + #else /* HAVE_STRUCT_TM_GMTOFF */ + # ifdef HAVE_GMTIME_R + struct tm tm; + # endif /* HAVE_GMTIME_R */ + #endif /* HAVE_STRUCT_TM_GMTOFF */ + + if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) { + t = *(p->conf.last_generated_accesslog_ts_ptr) = srv->cur_ts; + newts = 1; + } else { + t = con->request_start; + } + + #if defined(HAVE_STRUCT_TM_GMTOFF) + # ifdef HAVE_LOCALTIME_R + tmptr = localtime_r(&t, &tm); + # else /* HAVE_LOCALTIME_R */ + tmptr = localtime(&t); + # endif /* HAVE_LOCALTIME_R */ + #else /* HAVE_STRUCT_TM_GMTOFF */ + # ifdef HAVE_GMTIME_R + tmptr = gmtime_r(&t, &tm); + # else /* HAVE_GMTIME_R */ + tmptr = gmtime(&t); + # endif /* HAVE_GMTIME_R */ + #endif /* HAVE_STRUCT_TM_GMTOFF */ + + buffer_clear(p->conf.ts_accesslog_str); + + if (buffer_string_is_empty(f->string)) { + #if defined(HAVE_STRUCT_TM_GMTOFF) + long scd, hrs, min; + buffer_append_strftime(p->conf.ts_accesslog_str, "[%d/%b/%Y:%H:%M:%S ", tmptr); + buffer_append_string_len(p->conf.ts_accesslog_str, tmptr->tm_gmtoff >= 0 ? "+" : "-", 1); + + scd = labs(tmptr->tm_gmtoff); + hrs = scd / 3600; + min = (scd % 3600) / 60; + + /* hours */ + if (hrs < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0")); + buffer_append_int(p->conf.ts_accesslog_str, hrs); + + if (min < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0")); + buffer_append_int(p->conf.ts_accesslog_str, min); + buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("]")); + #else + buffer_append_strftime(p->conf.ts_accesslog_str, "[%d/%b/%Y:%H:%M:%S +0000]", tmptr); + #endif /* HAVE_STRUCT_TM_GMTOFF */ + } else { + buffer_append_strftime(p->conf.ts_accesslog_str, f->string->ptr, tmptr); + } + + buffer_append_string_buffer(b, p->conf.ts_accesslog_str); + } + break; + case FORMAT_TIME_USED: + case FORMAT_TIME_USED_US: + if (f->opt & FORMAT_FLAG_TIME_SEC) { + buffer_append_int(b, srv->cur_ts - con->request_start); + } else { + const struct timespec * const bs = &con->request_start_hp; + off_t tdiff; /*(expected to be 64-bit since large file support enabled)*/ + if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts); + tdiff = (off_t)(ts.tv_sec - bs->tv_sec)*1000000000 + (ts.tv_nsec - bs->tv_nsec); + if (tdiff <= 0) { + /* sanity check for time moving backwards + * (daylight savings adjustment or leap seconds or ?) */ + tdiff = -1; + } else if (f->opt & FORMAT_FLAG_TIME_MSEC) { + tdiff += 999999; /* ceil */ + tdiff /= 1000000; + } else if (f->opt & FORMAT_FLAG_TIME_USEC) { + tdiff += 999; /* ceil */ + tdiff /= 1000; + } /* else (f->opt & FORMAT_FLAG_TIME_NSEC) */ + buffer_append_int(b, (intmax_t)tdiff); + } + break; + case FORMAT_REMOTE_ADDR: + case FORMAT_REMOTE_HOST: + buffer_append_string_buffer(b, con->dst_addr_buf); + break; + case FORMAT_REMOTE_IDENT: + /* ident */ + buffer_append_string_len(b, CONST_STR_LEN("-")); + break; + case FORMAT_REMOTE_USER: + if (NULL != (vb = http_header_env_get(con, CONST_STR_LEN("REMOTE_USER")))) { + accesslog_append_escaped(b, vb); + } else { + buffer_append_string_len(b, CONST_STR_LEN("-")); + } + break; + case FORMAT_REQUEST_LINE: + if (!buffer_string_is_empty(con->request.request_line)) { + accesslog_append_escaped(b, con->request.request_line); + } + break; + case FORMAT_STATUS: + buffer_append_int(b, con->http_status); + break; + + case FORMAT_BYTES_OUT_NO_HEADER: + if (con->bytes_written > 0) { + buffer_append_int(b, + con->bytes_written - con->bytes_header <= 0 ? 0 : con->bytes_written - con->bytes_header); + } else { + buffer_append_string_len(b, CONST_STR_LEN("-")); + } + break; + case FORMAT_HEADER: + if (NULL != (vb = http_header_request_get(con, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(f->string)))) { + accesslog_append_escaped(b, vb); + } else { + buffer_append_string_len(b, CONST_STR_LEN("-")); + } + break; + case FORMAT_RESPONSE_HEADER: + if (NULL != (vb = http_header_response_get(con, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(f->string)))) { + accesslog_append_escaped(b, vb); + } else { + buffer_append_string_len(b, CONST_STR_LEN("-")); + } + break; + case FORMAT_ENV: + case FORMAT_NOTE: + if (NULL != (vb = http_header_env_get(con, CONST_BUF_LEN(f->string)))) { + accesslog_append_escaped(b, vb); + } else { + buffer_append_string_len(b, CONST_STR_LEN("-")); + } + break; + case FORMAT_FILENAME: + if (!buffer_string_is_empty(con->physical.path)) { + buffer_append_string_buffer(b, con->physical.path); + } else { + buffer_append_string_len(b, CONST_STR_LEN("-")); + } + break; + case FORMAT_BYTES_OUT: + if (con->bytes_written > 0) { + buffer_append_int(b, con->bytes_written); + } else { + buffer_append_string_len(b, CONST_STR_LEN("-")); + } + break; + case FORMAT_BYTES_IN: + if (con->bytes_read > 0) { + buffer_append_int(b, con->bytes_read); + } else { + buffer_append_string_len(b, CONST_STR_LEN("-")); + } + break; + case FORMAT_SERVER_NAME: + if (!buffer_string_is_empty(con->server_name)) { + buffer_append_string_buffer(b, con->server_name); + } else { + buffer_append_string_len(b, CONST_STR_LEN("-")); + } + break; + case FORMAT_HTTP_HOST: + if (!buffer_string_is_empty(con->uri.authority)) { + accesslog_append_escaped(b, con->uri.authority); + } else { + buffer_append_string_len(b, CONST_STR_LEN("-")); + } + break; + case FORMAT_REQUEST_PROTOCOL: + buffer_append_string_len(b, + con->request.http_version == HTTP_VERSION_1_1 ? "HTTP/1.1" : "HTTP/1.0", 8); + break; + case FORMAT_REQUEST_METHOD: + http_method_append(b, con->request.http_method); + break; + case FORMAT_PERCENT: + buffer_append_string_len(b, CONST_STR_LEN("%")); + break; + case FORMAT_LOCAL_ADDR: + { + /* (perf: not using getsockname() and inet_ntop_cache_get_ip()) + * (still useful if admin has configured explicit listen IPs) */ + const char *colon; + buffer *srvtoken = con->srv_socket->srv_token; + if (srvtoken->ptr[0] == '[') { + colon = strstr(srvtoken->ptr, "]:"); + } else { + colon = strchr(srvtoken->ptr, ':'); + } + if (colon) { + buffer_append_string_len(b, srvtoken->ptr, (size_t)(colon - srvtoken->ptr)); + } else { + buffer_append_string_buffer(b, srvtoken); + } + } + break; + case FORMAT_SERVER_PORT: + if (f->opt & FORMAT_FLAG_PORT_REMOTE) { + buffer_append_int(b, sock_addr_get_port(&con->dst_addr)); + } else { /* if (f->opt & FORMAT_FLAG_PORT_LOCAL) *//*(default)*/ + const char *colon; + buffer *srvtoken = ((server_socket*)(con->srv_socket))->srv_token; + if (srvtoken->ptr[0] == '[') { + colon = strstr(srvtoken->ptr, "]:"); + } else { + colon = strchr(srvtoken->ptr, ':'); + } + if (colon) { + buffer_append_string(b, colon+1); + } else { + buffer_append_int(b, srv->srvconf.port); + } + } + break; + case FORMAT_QUERY_STRING: + accesslog_append_escaped(b, con->uri.query); + break; + case FORMAT_URL: + accesslog_append_escaped(b, con->uri.path_raw); + break; + case FORMAT_CONNECTION_STATUS: + if (con->state == CON_STATE_RESPONSE_END) { + if (0 == con->keep_alive) { + buffer_append_string_len(b, CONST_STR_LEN("-")); + } else { + buffer_append_string_len(b, CONST_STR_LEN("+")); + } + } else { /* CON_STATE_ERROR */ + buffer_append_string_len(b, CONST_STR_LEN("X")); + } + break; + case FORMAT_KEEPALIVE_COUNT: + if (con->request_count > 1) { + buffer_append_int(b, (intmax_t)(con->request_count-1)); + } else { + buffer_append_string_len(b, CONST_STR_LEN("0")); + } + break; + case FORMAT_COOKIE: + if (NULL != (vb = http_header_request_get(con, HTTP_HEADER_COOKIE, CONST_STR_LEN("Cookie")))) { + char *str = vb->ptr; + size_t len = buffer_string_length(f->string); + do { + while (*str == ' ' || *str == '\t') ++str; + if (0 == strncmp(str, f->string->ptr, len) && str[len] == '=') { + char *v = str+len+1; + buffer *bstr; + for (str = v; *str != '\0' && *str != ';'; ++str) ; + if (str == v) break; + do { --str; } while (str > v && (*str == ' ' || *str == '\t')); + bstr = buffer_init(); + buffer_copy_string_len(bstr, v, str - v + 1); + accesslog_append_escaped(b, bstr); + buffer_free(bstr); + break; + } else { + do { ++str; } while (*str != ' ' && *str != '\t' && *str != '\0'); + } + while (*str == ' ' || *str == '\t') ++str; + } while (*str++ == ';'); + } + break; + default: + break; + } + break; + default: + break; + } + } + + if (p->conf.use_syslog) { /* syslog doesn't cache */ +#ifdef HAVE_SYSLOG_H + if (!buffer_string_is_empty(b)) { + /*(syslog appends a \n on its own)*/ + syslog(p->conf.syslog_level, "%s", b->ptr); + } +#endif + buffer_clear(b); + return HANDLER_GO_ON; + } + + buffer_append_string_len(b, CONST_STR_LEN("\n")); + + if ((!buffer_string_is_empty(p->conf.access_logfile) && p->conf.access_logfile->ptr[0] == '|') || /* pipes don't cache */ + newts || + buffer_string_length(b) >= BUFFER_MAX_REUSE_SIZE) { + if (p->conf.log_access_fd >= 0) { + accesslog_write_all(srv, p->conf.access_logfile, p->conf.log_access_fd, CONST_BUF_LEN(b)); + } + buffer_clear(b); + } + + return HANDLER_GO_ON; +} + + +int mod_accesslog_plugin_init(plugin *p); +int mod_accesslog_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("accesslog"); + + p->init = mod_accesslog_init; + p->set_defaults= log_access_open; + p->cleanup = mod_accesslog_free; + + p->handle_request_done = log_access_write; + p->handle_trigger = log_access_periodic_flush; + p->handle_sighup = log_access_cycle; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_alias.c b/data/lighttpd/lighttpd-1.4.53/src/mod_alias.c new file mode 100644 index 000000000..cd9414ff5 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_alias.c @@ -0,0 +1,219 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" + +#include "plugin.h" + +#include <stdlib.h> +#include <string.h> + +/* plugin config for all request/connections */ +typedef struct { + array *alias; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_alias_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_alias_free) { + plugin_data *p = p_d; + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->alias); + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_alias_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "alias.url", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->alias = array_init(); + cv[0].destination = s->alias; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_kvstring(s->alias)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for alias.url; expected list of \"urlpath\" => \"filepath\""); + return HANDLER_ERROR; + } + + if (s->alias->used >= 2) { + const array *a = s->alias; + size_t j, k; + + for (j = 0; j < a->used; j ++) { + const buffer *prefix = a->data[a->sorted[j]]->key; + for (k = j + 1; k < a->used; k ++) { + const buffer *key = a->data[a->sorted[k]]->key; + + if (buffer_string_length(key) < buffer_string_length(prefix)) { + break; + } + if (memcmp(key->ptr, prefix->ptr, buffer_string_length(prefix)) != 0) { + break; + } + /* ok, they have same prefix. check position */ + if (a->sorted[j] < a->sorted[k]) { + log_error_write(srv, __FILE__, __LINE__, "SBSBS", + "url.alias: `", key, "' will never match as `", prefix, "' matched first"); + return HANDLER_ERROR; + } + } + } + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_alias_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(alias); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("alias.url"))) { + PATCH(alias); + } + } + } + + return 0; +} +#undef PATCH + +PHYSICALPATH_FUNC(mod_alias_physical_handler) { + plugin_data *p = p_d; + char *uri_ptr; + size_t uri_len = buffer_string_length(con->physical.path); + size_t basedir_len, alias_len; + data_string *ds; + + if (0 == uri_len) return HANDLER_GO_ON; + + mod_alias_patch_connection(srv, con, p); + + /* do not include trailing slash on basedir */ + basedir_len = buffer_string_length(con->physical.basedir); + if ('/' == con->physical.basedir->ptr[basedir_len-1]) --basedir_len; + uri_len -= basedir_len; + uri_ptr = con->physical.path->ptr + basedir_len; + + ds = (!con->conf.force_lowercase_filenames) + ? (data_string *)array_match_key_prefix_klen(p->conf.alias, uri_ptr, uri_len) + : (data_string *)array_match_key_prefix_nc_klen(p->conf.alias, uri_ptr, uri_len); + if (NULL == ds) { return HANDLER_GO_ON; } + + /* matched */ + + /* check for path traversal in url-path following alias if key + * does not end in slash, but replacement value ends in slash */ + alias_len = buffer_string_length(ds->key); + if (uri_ptr[alias_len] == '.') { + char *s = uri_ptr + alias_len + 1; + if (*s == '.') ++s; + if (*s == '/' || *s == '\0') { + size_t vlen = buffer_string_length(ds->value); + if (0 != alias_len && ds->key->ptr[alias_len-1] != '/' + && 0 != vlen && ds->value->ptr[vlen-1] == '/') { + con->http_status = 403; + return HANDLER_FINISHED; + } + } + } + + buffer_copy_buffer(con->physical.basedir, ds->value); + buffer_copy_buffer(srv->tmp_buf, ds->value); + buffer_append_string(srv->tmp_buf, uri_ptr + alias_len); + buffer_copy_buffer(con->physical.path, srv->tmp_buf); + + return HANDLER_GO_ON; +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_alias_plugin_init(plugin *p); +int mod_alias_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("alias"); + + p->init = mod_alias_init; + p->handle_physical= mod_alias_physical_handler; + p->set_defaults = mod_alias_set_defaults; + p->cleanup = mod_alias_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_auth.c b/data/lighttpd/lighttpd-1.4.53/src/mod_auth.c new file mode 100644 index 000000000..977b5c2dd --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_auth.c @@ -0,0 +1,888 @@ +#include "first.h" + +#include "base.h" +#include "plugin.h" +#include "http_auth.h" +#include "http_header.h" +#include "log.h" + +#include <stdlib.h> +#include <string.h> + +/** + * auth framework + */ + +typedef struct { + /* auth */ + array *auth_require; + buffer *auth_backend_conf; + unsigned short auth_extern_authn; + + /* generated */ + const http_auth_backend_t *auth_backend; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +static handler_t mod_auth_check_basic(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); +static handler_t mod_auth_check_digest(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); +static handler_t mod_auth_check_extern(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); + +INIT_FUNC(mod_auth_init) { + static const http_auth_scheme_t http_auth_scheme_basic = { "basic", mod_auth_check_basic, NULL }; + static const http_auth_scheme_t http_auth_scheme_digest = { "digest", mod_auth_check_digest, NULL }; + static const http_auth_scheme_t http_auth_scheme_extern = { "extern", mod_auth_check_extern, NULL }; + plugin_data *p; + + /* register http_auth_scheme_* */ + http_auth_scheme_set(&http_auth_scheme_basic); + http_auth_scheme_set(&http_auth_scheme_digest); + http_auth_scheme_set(&http_auth_scheme_extern); + + p = calloc(1, sizeof(*p)); + + return p; +} + +FREE_FUNC(mod_auth_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->auth_require); + buffer_free(s->auth_backend_conf); + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +/* data type for mod_auth structured data + * (parsed from auth.require array of strings) */ +typedef struct { + DATA_UNSET; + http_auth_require_t *require; +} data_auth; + +static void data_auth_free(data_unset *d) +{ + data_auth * const dauth = (data_auth *)d; + buffer_free(dauth->key); + http_auth_require_free(dauth->require); + free(dauth); +} + +static data_auth *data_auth_init(void) +{ + static const struct data_methods fn = { + NULL, /* reset must not be called on this data */ + NULL, /* copy must not be called on this data */ + data_auth_free, + NULL, /* insert_dup must not be called on this data */ + NULL /* print must not be called on this data */ + }; + data_auth * const dauth = calloc(1, sizeof(*dauth)); + force_assert(NULL != dauth); + dauth->type = TYPE_OTHER; + dauth->fn = &fn; + + dauth->key = buffer_init(); + dauth->require = http_auth_require_init(); + + return dauth; +} + +static int mod_auth_require_parse (server *srv, http_auth_require_t * const require, const buffer *b) +{ + /* user=name1|user=name2|group=name3|host=name4 */ + + const char *str = b->ptr; + const char *p; + + if (buffer_is_equal_string(b, CONST_STR_LEN("valid-user"))) { + require->valid_user = 1; + return 1; /* success */ + } + + do { + const char *eq; + size_t len; + p = strchr(str, '|'); + len = NULL != p ? (size_t)(p - str) : strlen(str); + eq = memchr(str, '=', len); + if (NULL == eq) { + log_error_write(srv, __FILE__, __LINE__, "sssbss", + "error parsing auth.require 'require' field: missing '='", + "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\").", + "error value:", b, "error near:", str); + return 0; + } + if (p-1 == eq) { + log_error_write(srv, __FILE__, __LINE__, "sssbss", + "error parsing auth.require 'require' field: missing token after '='", + "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\").", + "error value:", b, "error near:", str); + return 0; + } + + switch ((int)(eq - str)) { + case 4: + if (0 == memcmp(str, CONST_STR_LEN("user"))) { + /*("user=" is 5)*/ + array_set_key_value(require->user, str+5, len-5, CONST_STR_LEN("")); + continue; + } + else if (0 == memcmp(str, CONST_STR_LEN("host"))) { + /*("host=" is 5)*/ + array_set_key_value(require->host, str+5, len-5, CONST_STR_LEN("")); + log_error_write(srv, __FILE__, __LINE__, "ssb", + "warning parsing auth.require 'require' field: 'host' not implemented;", + "field value:", b); + continue; + } + break; /* to error */ + case 5: + if (0 == memcmp(str, CONST_STR_LEN("group"))) { + /*("group=" is 6)*/ + array_set_key_value(require->group, str+6, len-6, CONST_STR_LEN("")); + #if 0/*(supported by mod_authn_ldap, but not all other backends)*/ + log_error_write(srv, __FILE__, __LINE__, "ssb", + "warning parsing auth.require 'require' field: 'group' not implemented;", + "field value:", b); + #endif + continue; + } + break; /* to error */ + case 10: + if (0 == memcmp(str, CONST_STR_LEN("valid-user"))) { + log_error_write(srv, __FILE__, __LINE__, "sssb", + "error parsing auth.require 'require' field: valid user can not be combined with other require rules", + "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\").", + "error value:", b); + return 0; + } + break; /* to error */ + default: + break; /* to error */ + } + + log_error_write(srv, __FILE__, __LINE__, "sssbss", + "error parsing auth.require 'require' field: invalid/unsupported token", + "(expecting \"valid-user\" or \"user=a|user=b|group=g|host=h\").", + "error value:", b, "error near:", str); + return 0; + + } while (p && *((str = p+1))); + + return 1; /* success */ +} + +SETDEFAULTS_FUNC(mod_auth_set_defaults) { + plugin_data *p = p_d; + size_t i; + + config_values_t cv[] = { + { "auth.backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "auth.require", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "auth.extern-authn", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },/* 2 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + size_t n; + data_array *da; + + s = calloc(1, sizeof(plugin_config)); + s->auth_backend_conf = buffer_init(); + + s->auth_require = array_init(); + + cv[0].destination = s->auth_backend_conf; + cv[1].destination = s->auth_require; /* T_CONFIG_LOCAL; not modified by config_insert_values_global() */ + cv[2].destination = &s->auth_extern_authn; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!buffer_string_is_empty(s->auth_backend_conf)) { + s->auth_backend = http_auth_backend_get(s->auth_backend_conf); + if (NULL == s->auth_backend) { + log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not supported:", s->auth_backend_conf); + + return HANDLER_ERROR; + } + } + + /* no auth.require for this section */ + if (NULL == (da = (data_array *)array_get_element(config->value, "auth.require"))) continue; + + if (da->type != TYPE_ARRAY || !array_is_kvarray(da->value)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "unexpected value for auth.require; expected ", + "auth.require = ( \"urlpath\" => ( \"option\" => \"value\" ) )"); + return HANDLER_ERROR; + } + + + for (n = 0; n < da->value->used; n++) { + size_t m; + data_array *da_file = (data_array *)da->value->data[n]; + const buffer *method = NULL, *realm = NULL, *require = NULL; + const http_auth_scheme_t *auth_scheme; + + if (!array_is_kvstring(da_file->value)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "unexpected value for auth.require; expected ", + "auth.require = ( \"urlpath\" => ( \"option\" => \"value\" ) )"); + + return HANDLER_ERROR; + } + + for (m = 0; m < da_file->value->used; m++) { + if (da_file->value->data[m]->type == TYPE_STRING) { + data_string *ds = (data_string *)da_file->value->data[m]; + if (buffer_is_equal_string(ds->key, CONST_STR_LEN("method"))) { + method = ds->value; + } else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("realm"))) { + realm = ds->value; + } else if (buffer_is_equal_string(ds->key, CONST_STR_LEN("require"))) { + require = ds->value; + } else { + log_error_write(srv, __FILE__, __LINE__, "ssbs", + "the field is unknown in:", + "auth.require = ( \"...\" => ( ..., -> \"", + da_file->value->data[m]->key, + "\" <- => \"...\" ) )"); + + return HANDLER_ERROR; + } + } else { + log_error_write(srv, __FILE__, __LINE__, "ssbs", + "a string was expected for:", + "auth.require = ( \"...\" => ( ..., -> \"", + da_file->value->data[m]->key, + "\" <- => \"...\" ) )"); + + return HANDLER_ERROR; + } + } + + if (buffer_string_is_empty(method)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "the method field is missing or blank in:", + "auth.require = ( \"...\" => ( ..., \"method\" => \"...\" ) )"); + return HANDLER_ERROR; + } else { + auth_scheme = http_auth_scheme_get(method); + if (NULL == auth_scheme) { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "unknown method", method, "(e.g. \"basic\", \"digest\" or \"extern\") in", + "auth.require = ( \"...\" => ( ..., \"method\" => \"...\") )"); + return HANDLER_ERROR; + } + } + + if (buffer_is_empty(realm)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "the realm field is missing in:", + "auth.require = ( \"...\" => ( ..., \"realm\" => \"...\" ) )"); + return HANDLER_ERROR; + } + + if (buffer_string_is_empty(require)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "the require field is missing or blank in:", + "auth.require = ( \"...\" => ( ..., \"require\" => \"...\" ) )"); + return HANDLER_ERROR; + } + + if (require) { /*(always true at this point)*/ + data_auth * const dauth = data_auth_init(); + buffer_copy_buffer(dauth->key, da_file->key); + dauth->require->scheme = auth_scheme; + buffer_copy_buffer(dauth->require->realm, realm); + if (!mod_auth_require_parse(srv, dauth->require, require)) { + dauth->fn->free((data_unset *)dauth); + return HANDLER_ERROR; + } + array_insert_unique(s->auth_require, (data_unset *)dauth); + } + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_auth_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(auth_backend); + PATCH(auth_require); + PATCH(auth_extern_authn); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend"))) { + PATCH(auth_backend); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.require"))) { + PATCH(auth_require); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.extern-authn"))) { + PATCH(auth_extern_authn); + } + } + } + + return 0; +} +#undef PATCH + +static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + data_auth *dauth; + + mod_auth_patch_connection(srv, con, p); + + if (p->conf.auth_require == NULL) return HANDLER_GO_ON; + + /* search auth directives for first prefix match against URL path */ + /* if we have a case-insensitive FS we have to lower-case the URI here too */ + dauth = (!con->conf.force_lowercase_filenames) + ? (data_auth *)array_match_key_prefix(p->conf.auth_require, con->uri.path) + : (data_auth *)array_match_key_prefix_nc(p->conf.auth_require, con->uri.path); + if (NULL == dauth) return HANDLER_GO_ON; + + { + const http_auth_scheme_t * const scheme = dauth->require->scheme; + if (p->conf.auth_extern_authn) { + buffer *vb = http_header_env_get(con, CONST_STR_LEN("REMOTE_USER")); + if (NULL != vb && http_auth_match_rules(dauth->require, vb->ptr, NULL, NULL)) { + return HANDLER_GO_ON; + } + } + return scheme->checkfn(srv, con, scheme->p_d, dauth->require, p->conf.auth_backend); + } +} + +int mod_auth_plugin_init(plugin *p); +int mod_auth_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("auth"); + p->init = mod_auth_init; + p->set_defaults = mod_auth_set_defaults; + p->handle_uri_clean = mod_auth_uri_handler; + p->cleanup = mod_auth_free; + + p->data = NULL; + + return 0; +} + + + + +/* + * auth schemes (basic, digest, extern) + * + * (could be in separate file from mod_auth.c as long as registration occurs) + */ + +#include "base64.h" +#include "md5.h" +#include "rand.h" +#include "http_header.h" + +static handler_t mod_auth_send_400_bad_request(server *srv, connection *con) { + UNUSED(srv); + + /* a field was missing or invalid */ + con->http_status = 400; /* Bad Request */ + con->mode = DIRECT; + + return HANDLER_FINISHED; +} + +static handler_t mod_auth_send_401_unauthorized_basic(server *srv, connection *con, buffer *realm) { + con->http_status = 401; + con->mode = DIRECT; + + buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("Basic realm=\"")); + buffer_append_string_buffer(srv->tmp_buf, realm); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\"")); + + http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(srv->tmp_buf)); + + return HANDLER_FINISHED; +} + +static handler_t mod_auth_check_basic(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend) { + buffer *b = http_header_request_get(con, HTTP_HEADER_AUTHORIZATION, CONST_STR_LEN("Authorization")); + buffer *username; + char *pw; + handler_t rc = HANDLER_UNSET; + + UNUSED(p_d); + + if (NULL == backend) { + log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not configured for", con->uri.path); + con->http_status = 500; + con->mode = DIRECT; + return HANDLER_FINISHED; + } + + if (NULL == b) { + return mod_auth_send_401_unauthorized_basic(srv, con, require->realm); + } + + if (0 != strncasecmp(b->ptr, "Basic ", sizeof("Basic ")-1)) { + return mod_auth_send_401_unauthorized_basic(srv, con, require->realm); + } + #ifdef __COVERITY__ + if (buffer_string_length(b) < sizeof("Basic ")-1) { + return mod_auth_send_400_bad_request(srv, con); + } + #endif + + username = buffer_init(); + + /* coverity[overflow_sink : FALSE] */ + if (!buffer_append_base64_decode(username, b->ptr+sizeof("Basic ")-1, buffer_string_length(b)-(sizeof("Basic ")-1), BASE64_STANDARD)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "decoding base64-string failed", username); + + buffer_free(username); + return mod_auth_send_400_bad_request(srv, con); + } + + /* r2 == user:password */ + if (NULL == (pw = strchr(username->ptr, ':'))) { + log_error_write(srv, __FILE__, __LINE__, "sb", "missing ':' in", username); + + buffer_free(username); + return mod_auth_send_400_bad_request(srv, con); + } + + buffer_string_set_length(username, pw - username->ptr); + pw++; + + rc = backend->basic(srv, con, backend->p_d, require, username, pw); + switch (rc) { + case HANDLER_GO_ON: + http_auth_setenv(con, CONST_BUF_LEN(username), CONST_STR_LEN("Basic")); + break; + case HANDLER_WAIT_FOR_EVENT: + case HANDLER_FINISHED: + break; + case HANDLER_ERROR: + default: + log_error_write(srv, __FILE__, __LINE__, "sbsBsB", "password doesn't match for", con->uri.path, "username:", username, ", IP:", con->dst_addr_buf); + rc = HANDLER_UNSET; + break; + } + + buffer_free(username); + return (HANDLER_UNSET != rc) ? rc : mod_auth_send_401_unauthorized_basic(srv, con, require->realm); +} + +#define HASHLEN 16 +#define HASHHEXLEN 32 +typedef unsigned char HASH[HASHLEN]; +typedef char HASHHEX[HASHHEXLEN+1]; + +static void CvtHex(const HASH Bin, char (*Hex)[33]) { + li_tohex(*Hex, sizeof(*Hex), (const char*) Bin, 16); +} + +typedef struct { + const char *key; + int key_len; + char **ptr; +} digest_kv; + +static handler_t mod_auth_send_401_unauthorized_digest(server *srv, connection *con, buffer *realm, int nonce_stale); + +static handler_t mod_auth_check_digest(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend) { + buffer *vb = http_header_request_get(con, HTTP_HEADER_AUTHORIZATION, CONST_STR_LEN("Authorization")); + + char a1[33]; + char a2[33]; + + char *username = NULL; + char *realm = NULL; + char *nonce = NULL; + char *uri = NULL; + char *algorithm = NULL; + char *qop = NULL; + char *cnonce = NULL; + char *nc = NULL; + char *respons = NULL; + + char *e, *c; + const char *m = NULL; + int i; + buffer *b; + + li_MD5_CTX Md5Ctx; + HASH HA1; + HASH HA2; + HASH RespHash; + HASHHEX HA2Hex; + + + /* init pointers */ +#define S(x) \ + x, sizeof(x)-1, NULL + digest_kv dkv[10] = { + { S("username=") }, + { S("realm=") }, + { S("nonce=") }, + { S("uri=") }, + { S("algorithm=") }, + { S("qop=") }, + { S("cnonce=") }, + { S("nc=") }, + { S("response=") }, + + { NULL, 0, NULL } + }; +#undef S + + dkv[0].ptr = &username; + dkv[1].ptr = &realm; + dkv[2].ptr = &nonce; + dkv[3].ptr = &uri; + dkv[4].ptr = &algorithm; + dkv[5].ptr = &qop; + dkv[6].ptr = &cnonce; + dkv[7].ptr = &nc; + dkv[8].ptr = &respons; + + UNUSED(p_d); + + if (NULL == backend) { + log_error_write(srv, __FILE__, __LINE__, "sb", "auth.backend not configured for", con->uri.path); + con->http_status = 500; + con->mode = DIRECT; + return HANDLER_FINISHED; + } + + if (NULL == vb) { + return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0); + } + + if (0 != strncasecmp(vb->ptr, "Digest ", sizeof("Digest ")-1)) { + return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0); + } else { + size_t n = buffer_string_length(vb); + #ifdef __COVERITY__ + if (n < sizeof("Digest ")-1) { + return mod_auth_send_400_bad_request(srv, con); + } + #endif + n -= (sizeof("Digest ")-1); + b = buffer_init(); + buffer_copy_string_len(b,vb->ptr+sizeof("Digest ")-1,n); + } + + /* parse credentials from client */ + for (c = b->ptr; *c; c++) { + /* skip whitespaces */ + while (*c == ' ' || *c == '\t') c++; + if (!*c) break; + + for (i = 0; dkv[i].key; i++) { + if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) { + if ((c[dkv[i].key_len] == '"') && + (NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) { + /* value with "..." */ + *(dkv[i].ptr) = c + dkv[i].key_len + 1; + c = e; + + *e = '\0'; + } else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) { + /* value without "...", terminated by ',' */ + *(dkv[i].ptr) = c + dkv[i].key_len; + c = e; + + *e = '\0'; + } else { + /* value without "...", terminated by EOL */ + *(dkv[i].ptr) = c + dkv[i].key_len; + c += strlen(c) - 1; + } + break; + } + } + } + + /* check if everything is transmitted */ + if (!username || + !realm || + !nonce || + !uri || + (qop && (!nc || !cnonce)) || + !respons ) { + /* missing field */ + + log_error_write(srv, __FILE__, __LINE__, "s", + "digest: missing field"); + + buffer_free(b); + return mod_auth_send_400_bad_request(srv, con); + } + + if (!buffer_is_equal_string(require->realm, realm, strlen(realm))) { + log_error_write(srv, __FILE__, __LINE__, "s", + "digest: realm mismatch"); + buffer_free(b); + return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0); + } + + /** + * protect the md5-sess against missing cnonce and nonce + */ + if (algorithm && + 0 == strcasecmp(algorithm, "md5-sess") && + (!nonce || !cnonce)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "digest: (md5-sess: missing field"); + + buffer_free(b); + return mod_auth_send_400_bad_request(srv, con); + } + + if (qop && strcasecmp(qop, "auth-int") == 0) { + log_error_write(srv, __FILE__, __LINE__, "s", + "digest: qop=auth-int not supported"); + + buffer_free(b); + return mod_auth_send_400_bad_request(srv, con); + } + + m = get_http_method_name(con->request.http_method); + force_assert(m); + + /* detect if attacker is attempting to reuse valid digest for one uri + * on a different request uri. Might also happen if intermediate proxy + * altered client request line. (Altered request would not result in + * the same digest as that calculated by the client.) + * Internal redirects such as with mod_rewrite will modify request uri. + * Reauthentication is done to detect crossing auth realms, but this + * uri validation step is bypassed. con->request.orig_uri is original + * uri sent in client request. */ + { + const size_t ulen = strlen(uri); + const size_t rlen = buffer_string_length(con->request.orig_uri); + if (!buffer_is_equal_string(con->request.orig_uri, uri, ulen) + && !(rlen < ulen && 0 == memcmp(con->request.orig_uri->ptr, uri, rlen) && uri[rlen] == '?')) { + log_error_write(srv, __FILE__, __LINE__, "sbsssB", + "digest: auth failed: uri mismatch (", con->request.orig_uri, "!=", uri, "), IP:", con->dst_addr_buf); + buffer_free(b); + return mod_auth_send_400_bad_request(srv, con); + } + } + + /* password-string == HA1 */ + switch (backend->digest(srv, con, backend->p_d, username, realm, HA1)) { + case HANDLER_GO_ON: + break; + case HANDLER_WAIT_FOR_EVENT: + buffer_free(b); + return HANDLER_WAIT_FOR_EVENT; + case HANDLER_FINISHED: + buffer_free(b); + return HANDLER_FINISHED; + case HANDLER_ERROR: + default: + buffer_free(b); + return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0); + } + + if (algorithm && + strcasecmp(algorithm, "md5-sess") == 0) { + li_MD5_Init(&Md5Ctx); + /* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */ + CvtHex(HA1, &a1); + li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); + li_MD5_Final(HA1, &Md5Ctx); + } + + CvtHex(HA1, &a1); + + /* calculate H(A2) */ + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri)); + /* qop=auth-int not supported, already checked above */ +/* + if (qop && strcasecmp(qop, "auth-int") == 0) { + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *) [body checksum], HASHHEXLEN); + } +*/ + li_MD5_Final(HA2, &Md5Ctx); + CvtHex(HA2, &HA2Hex); + + /* calculate response */ + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + if (qop && *qop) { + li_MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + }; + li_MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN); + li_MD5_Final(RespHash, &Md5Ctx); + CvtHex(RespHash, &a2); + + if (0 != strcmp(a2, respons)) { + /* digest not ok */ + log_error_write(srv, __FILE__, __LINE__, "sssB", + "digest: auth failed for ", username, ": wrong password, IP:", con->dst_addr_buf); + + buffer_free(b); + return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0); + } + + /* value is our allow-rules */ + if (!http_auth_match_rules(require, username, NULL, NULL)) { + buffer_free(b); + return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0); + } + + /* check age of nonce. Note, random data is used in nonce generation + * in mod_auth_send_401_unauthorized_digest(). If that were replaced + * with nanosecond time, then nonce secret would remain unique enough + * for the purposes of Digest auth, and would be reproducible (and + * verifiable) if nanoseconds were inclued with seconds as part of the + * nonce "timestamp:secret". Since that is not done, timestamp in + * nonce could theoretically be modified and still produce same md5sum, + * but that is highly unlikely within a 10 min (moving) window of valid + * time relative to current time (now) */ + { + time_t ts = 0; + const unsigned char * const nonce_uns = (unsigned char *)nonce; + for (i = 0; i < 8 && light_isxdigit(nonce_uns[i]); ++i) { + ts = (ts << 4) + hex2int(nonce_uns[i]); + } + if (nonce[i] != ':' + || ts > srv->cur_ts || srv->cur_ts - ts > 600) { /*(10 mins)*/ + /* nonce is stale; have client regenerate digest */ + buffer_free(b); + return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 1); + } /*(future: might send nextnonce when expiration is imminent)*/ + } + + http_auth_setenv(con, username, strlen(username), CONST_STR_LEN("Digest")); + + buffer_free(b); + + return HANDLER_GO_ON; +} + +static handler_t mod_auth_send_401_unauthorized_digest(server *srv, connection *con, buffer *realm, int nonce_stale) { + li_MD5_CTX Md5Ctx; + HASH h; + char hh[33]; + + force_assert(33 >= LI_ITOSTRING_LENGTH); /*(buffer used for both li_itostrn() and CvtHex())*/ + + /* generate nonce */ + + /* generate shared-secret */ + li_MD5_Init(&Md5Ctx); + + li_itostrn(hh, sizeof(hh), srv->cur_ts); + li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); + li_itostrn(hh, sizeof(hh), li_rand_pseudo()); + li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); + + li_MD5_Final(h, &Md5Ctx); + + CvtHex(h, &hh); + + /* generate WWW-Authenticate */ + + con->http_status = 401; + con->mode = DIRECT; + + buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("Digest realm=\"")); + buffer_append_string_buffer(srv->tmp_buf, realm); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\", charset=\"UTF-8\", nonce=\"")); + buffer_append_uint_hex(srv->tmp_buf, (uintmax_t)srv->cur_ts); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(":")); + buffer_append_string_len(srv->tmp_buf, hh, HASHHEXLEN); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\", qop=\"auth\"")); + if (nonce_stale) { + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(", stale=true")); + } + + http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(srv->tmp_buf)); + + return HANDLER_FINISHED; +} + +static handler_t mod_auth_check_extern(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend) { + /* require REMOTE_USER already set */ + buffer *vb = http_header_env_get(con, CONST_STR_LEN("REMOTE_USER")); + UNUSED(srv); + UNUSED(p_d); + UNUSED(backend); + if (NULL != vb && http_auth_match_rules(require, vb->ptr, NULL, NULL)) { + return HANDLER_GO_ON; + } else { + con->http_status = 401; + con->mode = DIRECT; + return HANDLER_FINISHED; + } +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_authn_file.c b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_file.c new file mode 100644 index 000000000..db1a241cb --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_file.c @@ -0,0 +1,734 @@ +#include "first.h" + + +/*(htpasswd)*/ +#ifdef HAVE_CRYPT_H +# include <crypt.h> +#elif defined(__linux__) +/* linux needs _XOPEN_SOURCE */ +# define _XOPEN_SOURCE +#endif + +#if defined(HAVE_LIBCRYPT) && !defined(HAVE_CRYPT) +/* always assume crypt() is present if we have -lcrypt */ +# define HAVE_CRYPT +#endif + +#include "sys-crypto.h" +#ifdef USE_OPENSSL_CRYPTO +#include <openssl/md4.h> +#endif + +#include "safe_memclear.h" +/*(htpasswd)*/ + + +#include "base.h" +#include "plugin.h" +#include "http_auth.h" +#include "log.h" + +#include "algo_sha1.h" +#include "base64.h" +#include "md5.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +/* + * htdigest, htpasswd, plain auth backends + */ + +typedef struct { + buffer *auth_plain_groupfile; + buffer *auth_plain_userfile; + buffer *auth_htdigest_userfile; + buffer *auth_htpasswd_userfile; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +static handler_t mod_authn_file_htdigest_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]); +static handler_t mod_authn_file_htdigest_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); +static handler_t mod_authn_file_plain_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]); +static handler_t mod_authn_file_plain_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); +static handler_t mod_authn_file_htpasswd_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); + +INIT_FUNC(mod_authn_file_init) { + static http_auth_backend_t http_auth_backend_htdigest = + { "htdigest", mod_authn_file_htdigest_basic, mod_authn_file_htdigest_digest, NULL }; + static http_auth_backend_t http_auth_backend_htpasswd = + { "htpasswd", mod_authn_file_htpasswd_basic, NULL, NULL }; + static http_auth_backend_t http_auth_backend_plain = + { "plain", mod_authn_file_plain_basic, mod_authn_file_plain_digest, NULL }; + plugin_data *p = calloc(1, sizeof(*p)); + + /* register http_auth_backend_htdigest */ + http_auth_backend_htdigest.p_d = p; + http_auth_backend_set(&http_auth_backend_htdigest); + + /* register http_auth_backend_htpasswd */ + http_auth_backend_htpasswd.p_d = p; + http_auth_backend_set(&http_auth_backend_htpasswd); + + /* register http_auth_backend_plain */ + http_auth_backend_plain.p_d = p; + http_auth_backend_set(&http_auth_backend_plain); + + return p; +} + +FREE_FUNC(mod_authn_file_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->auth_plain_groupfile); + buffer_free(s->auth_plain_userfile); + buffer_free(s->auth_htdigest_userfile); + buffer_free(s->auth_htpasswd_userfile); + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_authn_file_set_defaults) { + plugin_data *p = p_d; + size_t i; + + config_values_t cv[] = { + { "auth.backend.plain.groupfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "auth.backend.plain.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "auth.backend.htdigest.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "auth.backend.htpasswd.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->auth_plain_groupfile = buffer_init(); + s->auth_plain_userfile = buffer_init(); + s->auth_htdigest_userfile = buffer_init(); + s->auth_htpasswd_userfile = buffer_init(); + + cv[0].destination = s->auth_plain_groupfile; + cv[1].destination = s->auth_plain_userfile; + cv[2].destination = s->auth_htdigest_userfile; + cv[3].destination = s->auth_htpasswd_userfile; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_authn_file_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(auth_plain_groupfile); + PATCH(auth_plain_userfile); + PATCH(auth_htdigest_userfile); + PATCH(auth_htpasswd_userfile); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.groupfile"))) { + PATCH(auth_plain_groupfile); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.userfile"))) { + PATCH(auth_plain_userfile); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htdigest.userfile"))) { + PATCH(auth_htdigest_userfile); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htpasswd.userfile"))) { + PATCH(auth_htpasswd_userfile); + } + } + } + + return 0; +} +#undef PATCH + + +static int mod_authn_file_htdigest_get(server *srv, const buffer *auth_fn, const buffer *username, const buffer *realm, unsigned char HA1[16]) { + FILE *fp; + char f_user[1024]; + + if (buffer_string_is_empty(auth_fn)) return -1; + if (buffer_is_empty(username) || buffer_is_empty(realm)) return -1; + + fp = fopen(auth_fn->ptr, "r"); + if (NULL == fp) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "opening digest-userfile", auth_fn, "failed:", strerror(errno)); + + return -1; + } + + while (NULL != fgets(f_user, sizeof(f_user), fp)) { + char *f_pwd, *f_realm; + size_t u_len, r_len; + + /* skip blank lines and comment lines (beginning '#') */ + if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue; + + /* + * htdigest format + * + * user:realm:md5(user:realm:password) + */ + + if (NULL == (f_realm = strchr(f_user, ':'))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "parsed error in", auth_fn, + "expected 'username:realm:hashed password'"); + + continue; /* skip bad lines */ + } + + if (NULL == (f_pwd = strchr(f_realm + 1, ':'))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "parsed error in", auth_fn, + "expected 'username:realm:hashed password'"); + + continue; /* skip bad lines */ + } + + /* get pointers to the fields */ + u_len = f_realm - f_user; + f_realm++; + r_len = f_pwd - f_realm; + f_pwd++; + + if (buffer_string_length(username) == u_len && + (buffer_string_length(realm) == r_len) && + (0 == strncmp(username->ptr, f_user, u_len)) && + (0 == strncmp(realm->ptr, f_realm, r_len))) { + /* found */ + + size_t pwd_len = strlen(f_pwd); + if (f_pwd[pwd_len-1] == '\n') --pwd_len; + + fclose(fp); + + return http_auth_md5_hex2bin(f_pwd, pwd_len, HA1); + } + } + + fclose(fp); + return -1; +} + +static handler_t mod_authn_file_htdigest_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]) { + plugin_data *p = (plugin_data *)p_d; + buffer *username_buf = buffer_init_string(username); + buffer *realm_buf = buffer_init_string(realm); + int rc; + mod_authn_file_patch_connection(srv, con, p); + rc = mod_authn_file_htdigest_get(srv, p->conf.auth_htdigest_userfile, username_buf, realm_buf, HA1); + buffer_free(realm_buf); + buffer_free(username_buf); + UNUSED(con); + return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR; +} + +static handler_t mod_authn_file_htdigest_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw) { + plugin_data *p = (plugin_data *)p_d; + li_MD5_CTX Md5Ctx; + unsigned char HA1[16]; + unsigned char htdigest[16]; + + mod_authn_file_patch_connection(srv, con, p); + if (mod_authn_file_htdigest_get(srv, p->conf.auth_htdigest_userfile, username, require->realm, htdigest)) return HANDLER_ERROR; + + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(username)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(require->realm)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw)); + li_MD5_Final(HA1, &Md5Ctx); + + UNUSED(con); + return (0 == memcmp(HA1, htdigest, sizeof(HA1)) + && http_auth_match_rules(require, username->ptr, NULL, NULL)) + ? HANDLER_GO_ON + : HANDLER_ERROR; +} + + + + +static int mod_authn_file_htpasswd_get(server *srv, const buffer *auth_fn, const buffer *username, buffer *password) { + FILE *fp; + char f_user[1024]; + + if (buffer_is_empty(username)) return -1; + + if (buffer_string_is_empty(auth_fn)) return -1; + fp = fopen(auth_fn->ptr, "r"); + if (NULL == fp) { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "opening plain-userfile", auth_fn, "failed:", strerror(errno)); + + return -1; + } + + while (NULL != fgets(f_user, sizeof(f_user), fp)) { + char *f_pwd; + size_t u_len; + + /* skip blank lines and comment lines (beginning '#') */ + if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue; + + /* + * htpasswd format + * + * user:crypted passwd + */ + + if (NULL == (f_pwd = strchr(f_user, ':'))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "parsed error in", auth_fn, + "expected 'username:hashed password'"); + + continue; /* skip bad lines */ + } + + /* get pointers to the fields */ + u_len = f_pwd - f_user; + f_pwd++; + + if (buffer_string_length(username) == u_len && + (0 == strncmp(username->ptr, f_user, u_len))) { + /* found */ + + size_t pwd_len = strlen(f_pwd); + if (f_pwd[pwd_len-1] == '\n') --pwd_len; + + buffer_copy_string_len(password, f_pwd, pwd_len); + + fclose(fp); + return 0; + } + } + + fclose(fp); + return -1; +} + +static handler_t mod_authn_file_plain_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]) { + plugin_data *p = (plugin_data *)p_d; + buffer *username_buf = buffer_init_string(username); + buffer *password_buf = buffer_init();/* password-string from auth-backend */ + int rc; + mod_authn_file_patch_connection(srv, con, p); + rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_plain_userfile, username_buf, password_buf); + if (0 == rc) { + /* generate password from plain-text */ + li_MD5_CTX Md5Ctx; + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)username_buf->ptr, buffer_string_length(username_buf)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *)password_buf->ptr, buffer_string_length(password_buf)); + li_MD5_Final(HA1, &Md5Ctx); + } + buffer_free(password_buf); + buffer_free(username_buf); + UNUSED(con); + return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR; +} + +static handler_t mod_authn_file_plain_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw) { + plugin_data *p = (plugin_data *)p_d; + buffer *password_buf = buffer_init();/* password-string from auth-backend */ + int rc; + mod_authn_file_patch_connection(srv, con, p); + rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_plain_userfile, username, password_buf); + if (0 == rc) { + rc = http_auth_const_time_memeq(CONST_BUF_LEN(password_buf), pw, strlen(pw)) ? 0 : -1; + } + buffer_free(password_buf); + UNUSED(con); + return 0 == rc && http_auth_match_rules(require, username->ptr, NULL, NULL) + ? HANDLER_GO_ON + : HANDLER_ERROR; +} + + + + +/** + * the $apr1$ handling is taken from apache 1.3.x + */ + +/* + * The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0 + * MD5 crypt() function, which is licenced as follows: + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + */ + +#define APR_MD5_DIGESTSIZE 16 +#define APR1_ID "$apr1$" + +/* + * The following MD5 password encryption code was largely borrowed from + * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is + * licenced as stated above. + */ + +static void to64(char *s, unsigned long v, int n) +{ + static const unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) { + /* + * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL, + * plus 4 for the '$' separators, plus the password hash itself. + * Let's leave a goodly amount of leeway. + */ + + char passwd[120], *p; + const char *sp, *ep; + unsigned char final[APR_MD5_DIGESTSIZE]; + ssize_t sl, pl, i; + li_MD5_CTX ctx, ctx1; + unsigned long l; + + /* + * Refine the salt first. It's possible we were given an already-hashed + * string as the salt argument, so extract the actual salt value from it + * if so. Otherwise just use the string up to the first '$' as the salt. + */ + sp = salt; + + /* + * If it starts with the magic string, then skip that. + */ + if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) { + sp += strlen(APR1_ID); + } + + /* + * It stops at the first '$' or 8 chars, whichever comes first + */ + for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) { + continue; + } + + /* + * Get the length of the true salt + */ + sl = ep - sp; + + /* + * 'Time to make the doughnuts..' + */ + li_MD5_Init(&ctx); + + /* + * The password first, since that is what is most unknown + */ + li_MD5_Update(&ctx, pw, strlen(pw)); + + /* + * Then our magic string + */ + li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID)); + + /* + * Then the raw salt + */ + li_MD5_Update(&ctx, sp, sl); + + /* + * Then just as many characters of the MD5(pw, salt, pw) + */ + li_MD5_Init(&ctx1); + li_MD5_Update(&ctx1, pw, strlen(pw)); + li_MD5_Update(&ctx1, sp, sl); + li_MD5_Update(&ctx1, pw, strlen(pw)); + li_MD5_Final(final, &ctx1); + for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) { + li_MD5_Update(&ctx, final, + (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl); + } + + /* + * Don't leave anything around in vm they could use. + */ + memset(final, 0, sizeof(final)); + + /* + * Then something really weird... + */ + for (i = strlen(pw); i != 0; i >>= 1) { + if (i & 1) { + li_MD5_Update(&ctx, final, 1); + } + else { + li_MD5_Update(&ctx, pw, 1); + } + } + + /* + * Now make the output string. We know our limitations, so we + * can use the string routines without bounds checking. + */ + strcpy(passwd, APR1_ID); + strncat(passwd, sp, sl); + strcat(passwd, "$"); + + li_MD5_Final(final, &ctx); + + /* + * And now, just to make sure things don't run too fast.. + * On a 60 Mhz Pentium this takes 34 msec, so you would + * need 30 seconds to build a 1000 entry dictionary... + */ + for (i = 0; i < 1000; i++) { + li_MD5_Init(&ctx1); + if (i & 1) { + li_MD5_Update(&ctx1, pw, strlen(pw)); + } + else { + li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); + } + if (i % 3) { + li_MD5_Update(&ctx1, sp, sl); + } + + if (i % 7) { + li_MD5_Update(&ctx1, pw, strlen(pw)); + } + + if (i & 1) { + li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); + } + else { + li_MD5_Update(&ctx1, pw, strlen(pw)); + } + li_MD5_Final(final,&ctx1); + } + + p = passwd + strlen(passwd); + + l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4; + l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4; + l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4; + l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4; + l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4; + l = final[11] ; to64(p, l, 2); p += 2; + *p = '\0'; + + /* + * Don't leave anything around in vm they could use. + */ + safe_memclear(final, sizeof(final)); + + /* FIXME + */ +#define apr_cpystrn strncpy + apr_cpystrn(result, passwd, nbytes - 1); +} + +static void apr_sha_encode(const char *pw, char *result, size_t nbytes) { + unsigned char digest[20]; + size_t base64_written; + SHA_CTX sha1; + + SHA1_Init(&sha1); + SHA1_Update(&sha1, (const unsigned char *) pw, strlen(pw)); + SHA1_Final(digest, &sha1); + + memset(result, 0, nbytes); + + /* need 5 bytes for "{SHA}", 28 for base64 (3 bytes -> 4 bytes) of SHA1 (20 bytes), 1 terminating */ + if (nbytes < 5 + 28 + 1) return; + + memcpy(result, "{SHA}", 5); + base64_written = li_to_base64(result + 5, nbytes - 5, digest, 20, BASE64_STANDARD); + force_assert(base64_written == 28); + result[5 + base64_written] = '\0'; /* terminate string */ +} + +static handler_t mod_authn_file_htpasswd_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw) { + plugin_data *p = (plugin_data *)p_d; + buffer *password = buffer_init();/* password-string from auth-backend */ + int rc; + mod_authn_file_patch_connection(srv, con, p); + rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_htpasswd_userfile, username, password); + if (0 == rc) { + char sample[256]; + rc = -1; + if (!strncmp(password->ptr, APR1_ID, strlen(APR1_ID))) { + /* + * The hash was created using $apr1$ custom algorithm. + */ + apr_md5_encode(pw, password->ptr, sample, sizeof(sample)); + rc = strcmp(sample, password->ptr); + } + else if (0 == strncmp(password->ptr, "{SHA}", 5)) { + apr_sha_encode(pw, sample, sizeof(sample)); + rc = strcmp(sample, password->ptr); + } + #if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT) + /* a simple DES password is 2 + 11 characters. everything else should be longer. */ + else if (buffer_string_length(password) >= 13) { + char *crypted; + #if defined(HAVE_CRYPT_R) + struct crypt_data crypt_tmp_data; + #ifdef _AIX + memset(&crypt_tmp_data, 0, sizeof(crypt_tmp_data)); + #else + crypt_tmp_data.initialized = 0; + #endif + #endif + #ifdef USE_OPENSSL_CRYPTO /* (for MD4_*() (e.g. MD4_Update())) */ + #ifndef NO_MD4 /*(e.g. wolfSSL built without MD4)*/ + if (0 == memcmp(password->ptr, CONST_STR_LEN("$1+ntlm$"))) { + /* CRYPT-MD5-NTLM algorithm + * This algorithm allows for the construction of (slight more) + * secure, salted password hashes from an environment where only + * legacy NTLM hashes are available and where it is not feasible + * to re-hash all the passwords with the MD5-based crypt(). */ + /* Note: originally, LM password were limited to 14 chars. + * NTLM passwords limited to 127 chars, and encoding to UCS-2LE + * requires double that, so sample[256] buf is large enough. + * Prior sample[120] size likely taken from apr_md5_encode(). */ + char *b = password->ptr+sizeof("$1+ntlm$")-1; + char *e = strchr(b, '$'); + size_t slen = (NULL != e) ? (size_t)(e - b) : sizeof(sample); + size_t pwlen = strlen(pw) * 2; + if (slen < sizeof(sample) - (sizeof("$1$")-1) + && pwlen < sizeof(sample)) { + /* compute NTLM hash and convert to lowercase hex chars + * (require lc hex chars from li_tohex()) */ + char ntlmhash[16]; + char ntlmhex[33]; /*(sizeof(ntlmhash)*2 + 1)*/ + MD4_CTX c; + MD4_Init(&c); + if (pwlen) { + /*(reuse sample buffer to encode pw into UCS-2LE) + *(Note: assumes pw input in ISO-8859-1) */ + /*(buffer sizes checked above)*/ + for (int i=0; i < (int)pwlen; i+=2) { + sample[i] = pw[(i >> 1)]; + sample[i+1] = 0; + } + MD4_Update(&c, (unsigned char *)sample, pwlen); + } + MD4_Final((unsigned char *)ntlmhash, &c); + li_tohex(ntlmhex,sizeof(ntlmhex),ntlmhash,sizeof(ntlmhash)); + + /*(reuse sample buffer for salt (FYI: expect slen == 8))*/ + memcpy(sample, "$1$", sizeof("$1$")-1); + memcpy(sample+sizeof("$1$")-1, b, slen); + sample[sizeof("$1$")-1+slen] = '\0'; + #if defined(HAVE_CRYPT_R) + crypted = crypt_r(ntlmhex, sample, &crypt_tmp_data); + #else + crypted = crypt(ntlmhex, sample); + #endif + if (NULL != crypted + && 0 == strncmp(crypted, "$1$", sizeof("$1$")-1)) { + rc = strcmp(b, crypted+3); /*skip crypted "$1$" prefix*/ + } + } + } + else + #endif + #endif + { + #if defined(HAVE_CRYPT_R) + crypted = crypt_r(pw, password->ptr, &crypt_tmp_data); + #else + crypted = crypt(pw, password->ptr); + #endif + if (NULL != crypted) { + rc = strcmp(password->ptr, crypted); + } + } + } + #endif + } + buffer_free(password); + UNUSED(con); + return 0 == rc && http_auth_match_rules(require, username->ptr, NULL, NULL) + ? HANDLER_GO_ON + : HANDLER_ERROR; +} + + +int mod_authn_file_plugin_init(plugin *p); +int mod_authn_file_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("authn_file"); + p->init = mod_authn_file_init; + p->set_defaults= mod_authn_file_set_defaults; + p->cleanup = mod_authn_file_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_authn_gssapi.c b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_gssapi.c new file mode 100644 index 000000000..6ed61ee92 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_gssapi.c @@ -0,0 +1,801 @@ +#include "first.h" + +/* mod_authn_gssapi + * + * - provides http_auth_backend_t "gssapi" for HTTP auth Basic realm="Kerberos" + * - provides http_auth_scheme_t "Negotiate" + * - (does not provide http_auth_backend_t for HTTP auth Digest) + * + * Note: Credentials cache (KRB5CCNAME) is exported into CGI and SSI environment + * as well as passed to FastCGI and SCGI (useful if on same machine + * and running under same user account with access to KRB5CCNAME file). + * Credentials are clean up at the end of each request. + * + * LIMITATIONS: + * - no rate limiting of auth requests, so remote attacker can send many auth + * requests very quickly if attempting brute force password cracking attack + * + * FUTURE POTENTIAL PERFORMANCE ENHANCEMENTS: + * - Kerberos auth is synchronous and blocks waiting for response + * TODO: attempt async? + */ + +#include "plugin.h" + +#include <krb5.h> +#include <gssapi.h> +#include <gssapi/gssapi_krb5.h> + +#include "http_auth.h" +#include "http_header.h" +#include "base.h" +#include "log.h" +#include "md5.h" +#include "base64.h" + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +typedef struct { + buffer *auth_gssapi_keytab; + buffer *auth_gssapi_principal; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +static handler_t mod_authn_gssapi_check(server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend); +static handler_t mod_authn_gssapi_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); + +INIT_FUNC(mod_authn_gssapi_init) { + static http_auth_scheme_t http_auth_scheme_gssapi = + { "gssapi", mod_authn_gssapi_check, NULL }; + static http_auth_backend_t http_auth_backend_gssapi = + { "gssapi", mod_authn_gssapi_basic, NULL, NULL }; + plugin_data *p = calloc(1, sizeof(*p)); + + /* register http_auth_scheme_gssapi and http_auth_backend_gssapi */ + http_auth_scheme_gssapi.p_d = p; + http_auth_scheme_set(&http_auth_scheme_gssapi); + http_auth_backend_gssapi.p_d = p; + http_auth_backend_set(&http_auth_backend_gssapi); + + return p; +} + +FREE_FUNC(mod_authn_gssapi_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->auth_gssapi_keytab); + buffer_free(s->auth_gssapi_principal); + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_authn_gssapi_set_defaults) { + plugin_data *p = p_d; + size_t i; + config_values_t cv[] = { + { "auth.backend.gssapi.keytab", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "auth.backend.gssapi.principal", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + + s->auth_gssapi_keytab = buffer_init(); + s->auth_gssapi_principal = buffer_init(); + + cv[0].destination = s->auth_gssapi_keytab; + cv[1].destination = s->auth_gssapi_principal; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_authn_gssapi_patch_connection(server *srv, connection *con, plugin_data *p) +{ + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(auth_gssapi_keytab); + PATCH(auth_gssapi_principal); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.gssapi.keytab"))) { + PATCH(auth_gssapi_keytab); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.gssapi.principal"))) { + PATCH(auth_gssapi_principal); + } + } + } + + return 0; +} +#undef PATCH + +static handler_t mod_authn_gssapi_send_400_bad_request (server *srv, connection *con) +{ + UNUSED(srv); + con->http_status = 400; + con->mode = DIRECT; + return HANDLER_FINISHED; +} + +static void mod_authn_gssapi_log_gss_error(server *srv, const char *file, unsigned int line, const char *func, const char *extra, OM_uint32 err_maj, OM_uint32 err_min) +{ + buffer * const msg = buffer_init_string(func); + OM_uint32 maj_stat, min_stat; + OM_uint32 msg_ctx = 0; + gss_buffer_desc status_string; + + buffer_append_string_len(msg, CONST_STR_LEN("(")); + if (extra) buffer_append_string(msg, extra); + buffer_append_string_len(msg, CONST_STR_LEN("):")); + + do { + maj_stat = gss_display_status(&min_stat, err_maj, GSS_C_GSS_CODE, + GSS_C_NO_OID, &msg_ctx, &status_string); + if (GSS_ERROR(maj_stat)) + break; + + buffer_append_string(msg, status_string.value); + gss_release_buffer(&min_stat, &status_string); + + maj_stat = gss_display_status(&min_stat, err_min, GSS_C_MECH_CODE, + GSS_C_NULL_OID, &msg_ctx, &status_string); + if (!GSS_ERROR(maj_stat)) { + buffer_append_string_len(msg, CONST_STR_LEN(" (")); + buffer_append_string(msg, status_string.value); + buffer_append_string_len(msg, CONST_STR_LEN(")")); + gss_release_buffer(&min_stat, &status_string); + } + } while (!GSS_ERROR(maj_stat) && msg_ctx != 0); + + log_error_write(srv, file, line, "b", msg); + buffer_free(msg); +} + +static void mod_authn_gssapi_log_krb5_error(server *srv, const char *file, unsigned int line, const char *func, const char *extra, krb5_context context, int code) +{ + UNUSED(context); + /*(extra might be NULL)*/ + log_error_write(srv, file, line, "sssss", func, "(", extra, "):", + error_message(code)); +} + +static int mod_authn_gssapi_create_krb5_ccache(server *srv, connection *con, plugin_data *p, krb5_context kcontext, krb5_principal princ, krb5_ccache *ccache) +{ + buffer * const kccname = buffer_init_string("FILE:/tmp/krb5cc_gssapi_XXXXXX"); + char * const ccname = kccname->ptr + sizeof("FILE:")-1; + const size_t ccnamelen = buffer_string_length(kccname)-(sizeof("FILE:")-1); + /*(future: might consider using server.upload-dirs instead of /tmp)*/ + #ifdef __COVERITY__ + /* POSIX-2008 requires mkstemp create file with 0600 perms */ + umask(0600); + #endif + /* coverity[secure_temp : FALSE] */ + int fd = mkstemp(ccname); + if (fd < 0) { + log_error_write(srv, __FILE__, __LINE__, "sss", "mkstemp():", ccname, strerror(errno)); + buffer_free(kccname); + return -1; + } + close(fd); + + do { + krb5_error_code problem; + + problem = krb5_cc_resolve(kcontext, kccname->ptr, ccache); + if (problem) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_cc_resolve", NULL, kcontext, problem); + break; + } + + problem = krb5_cc_initialize(kcontext, *ccache, princ); + if (problem) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_cc_initialize", kccname->ptr, kcontext, problem); + break; + } + + con->plugin_ctx[p->id] = kccname; + + http_header_env_set(con, CONST_STR_LEN("KRB5CCNAME"), ccname, ccnamelen); + http_header_request_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("X-Forwarded-Keytab"), ccname, ccnamelen); + + return 0; + + } while (0); + + if (*ccache) { + krb5_cc_destroy(kcontext, *ccache); + *ccache = NULL; + } + unlink(ccname); + buffer_free(kccname); + + return -1; +} + +/* + * HTTP auth Negotiate + */ + +static handler_t mod_authn_gssapi_send_401_unauthorized_negotiate (connection *con) +{ + con->http_status = 401; + con->mode = DIRECT; + http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("WWW-Authenticate"), CONST_STR_LEN("Negotiate")); + return HANDLER_FINISHED; +} + +static int mod_authn_gssapi_store_gss_creds(server *srv, connection *con, plugin_data *p, char *princ_name, gss_cred_id_t delegated_cred) +{ + OM_uint32 maj_stat, min_stat; + krb5_principal princ = NULL; + krb5_ccache ccache = NULL; + krb5_error_code problem; + krb5_context context; + + problem = krb5_init_context(&context); + if (problem) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_init_context", NULL, context, problem); + return 0; + } + + problem = krb5_parse_name(context, princ_name, &princ); + if (problem) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_parse_name", NULL, context, problem); + goto end; + } + + if (mod_authn_gssapi_create_krb5_ccache(srv, con, p, context, princ, &ccache)) + goto end; + + maj_stat = gss_krb5_copy_ccache(&min_stat, delegated_cred, ccache); + if (GSS_ERROR(maj_stat)) { + mod_authn_gssapi_log_gss_error(srv, __FILE__, __LINE__, "gss_krb5_copy_ccache", princ_name, maj_stat, min_stat); + goto end; + } + + krb5_cc_close(context, ccache); + krb5_free_principal(context, princ); + krb5_free_context(context); + return 1; + + end: + if (princ) + krb5_free_principal(context, princ); + if (ccache) + krb5_cc_destroy(context, ccache); + krb5_free_context(context); + + return 0; +} + +static handler_t mod_authn_gssapi_check_spnego(server *srv, connection *con, plugin_data *p, const http_auth_require_t *require, const char *realm_str) +{ + OM_uint32 st_major, st_minor, acc_flags; + gss_buffer_desc token_s = GSS_C_EMPTY_BUFFER; + gss_buffer_desc token_in = GSS_C_EMPTY_BUFFER; + gss_buffer_desc token_out = GSS_C_EMPTY_BUFFER; + gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL; + gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL; + gss_ctx_id_t context = GSS_C_NO_CONTEXT; + gss_name_t server_name = GSS_C_NO_NAME; + gss_name_t client_name = GSS_C_NO_NAME; + + buffer *sprinc; + int ret = 0; + + buffer *t_in = buffer_init(); + if (!buffer_append_base64_decode(t_in, realm_str, strlen(realm_str), BASE64_STANDARD)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "decoding GSSAPI authentication header failed", realm_str); + buffer_free(t_in); + return mod_authn_gssapi_send_400_bad_request(srv, con); + } + + mod_authn_gssapi_patch_connection(srv, con, p); + + { + /* ??? Should code = krb5_kt_resolve(kcontext, p->conf.auth_gssapi_keytab->ptr, &keytab); + * be used, instead of putenv() of KRB5_KTNAME=...? See mod_authn_gssapi_basic() */ + /* ??? Should KRB5_KTNAME go into con->environment instead ??? */ + /* ??? Should KRB5_KTNAME be added to mod_authn_gssapi_basic(), too? */ + buffer ktname; + memset(&ktname, 0, sizeof(ktname)); + buffer_copy_string(&ktname, "KRB5_KTNAME="); + buffer_append_string_buffer(&ktname, p->conf.auth_gssapi_keytab); + putenv(ktname.ptr); + /* ktname.ptr becomes part of the environment, do not free */ + } + + sprinc = buffer_init_buffer(p->conf.auth_gssapi_principal); + if (strchr(sprinc->ptr, '/') == NULL) { + /*(copy HTTP Host, omitting port if port is present)*/ + /* ??? Should con->server_name be used if http_host not present? + * ??? What if con->server_name is not set? + * ??? Will this work below if IPv6 provided in Host? probably not */ + if (!buffer_is_empty(con->request.http_host)) { + buffer_append_string_len(sprinc, CONST_STR_LEN("/")); + buffer_append_string_len(sprinc, con->request.http_host->ptr, strcspn(con->request.http_host->ptr, ":")); + } + } + if (strchr(sprinc->ptr, '@') == NULL) { + buffer_append_string_len(sprinc, CONST_STR_LEN("@")); + buffer_append_string_buffer(sprinc, require->realm); + } + /*#define GSS_C_NT_USER_NAME gss_nt_user_name*/ + /*#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name*/ + #define GSS_KRB5_NT_PRINCIPAL_NAME gss_nt_krb5_name + + token_s.value = sprinc->ptr; + token_s.length = buffer_string_length(sprinc); + st_major = gss_import_name(&st_minor, &token_s, (gss_OID) GSS_KRB5_NT_PRINCIPAL_NAME, &server_name); + if (GSS_ERROR(st_major)) { + mod_authn_gssapi_log_gss_error(srv, __FILE__, __LINE__, "gss_import_name", NULL, st_major, st_minor); + goto end; + } + + memset(&token_s, 0, sizeof(token_s)); + st_major = gss_display_name(&st_minor, server_name, &token_s, NULL); + if (GSS_ERROR(st_major)) { + mod_authn_gssapi_log_gss_error(srv, __FILE__, __LINE__, "gss_display_name", NULL, st_major, st_minor); + goto end; + } + + /* acquire server's own credentials */ + st_major = gss_acquire_cred(&st_minor, server_name, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_cred, NULL, NULL); + if (GSS_ERROR(st_major)) { + mod_authn_gssapi_log_gss_error(srv, __FILE__, __LINE__, "gss_acquire_cred", sprinc->ptr, st_major, st_minor); + goto end; + } + + /* accept the user's context */ + token_in.length = buffer_string_length(t_in); + token_in.value = t_in->ptr; + st_major = gss_accept_sec_context(&st_minor, &context, server_cred, &token_in, GSS_C_NO_CHANNEL_BINDINGS, + &client_name, NULL, &token_out, &acc_flags, NULL, &client_cred); + if (GSS_ERROR(st_major)) { + mod_authn_gssapi_log_gss_error(srv, __FILE__, __LINE__, "gss_accept_sec_context", NULL, st_major, st_minor); + goto end; + } + + /* fetch the username */ + st_major = gss_display_name(&st_minor, client_name, &token_out, NULL); + if (GSS_ERROR(st_major)) { + mod_authn_gssapi_log_gss_error(srv, __FILE__, __LINE__, "gss_display_name", NULL, st_major, st_minor); + goto end; + } + + if (!(acc_flags & GSS_C_CONF_FLAG)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "No confidentiality for user:", token_out.value); + goto end; + } + + if (!(acc_flags & GSS_C_DELEG_FLAG)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "Unable to delegate credentials for user:", token_out.value); + goto end; + } + + /* check the allow-rules */ + if (!http_auth_match_rules(require, token_out.value, NULL, NULL)) { + goto end; + } + + ret = mod_authn_gssapi_store_gss_creds(srv, con, p, token_out.value, client_cred); + if (ret) + http_auth_setenv(con, token_out.value, token_out.length, CONST_STR_LEN("GSSAPI")); + + end: + buffer_free(t_in); + buffer_free(sprinc); + + if (context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&st_minor, &context, GSS_C_NO_BUFFER); + + if (client_cred != GSS_C_NO_CREDENTIAL) + gss_release_cred(&st_minor, &client_cred); + if (server_cred != GSS_C_NO_CREDENTIAL) + gss_release_cred(&st_minor, &server_cred); + + if (client_name != GSS_C_NO_NAME) + gss_release_name(&st_minor, &client_name); + if (server_name != GSS_C_NO_NAME) + gss_release_name(&st_minor, &server_name); + + if (token_s.length) + gss_release_buffer(&st_minor, &token_s); + /* if (token_in.length) + * gss_release_buffer(&st_minor, &token_in); */ + if (token_out.length) + gss_release_buffer(&st_minor, &token_out); + + return ret ? HANDLER_GO_ON : mod_authn_gssapi_send_401_unauthorized_negotiate(con); +} + +static handler_t mod_authn_gssapi_check (server *srv, connection *con, void *p_d, const struct http_auth_require_t *require, const struct http_auth_backend_t *backend) +{ + buffer *vb = http_header_request_get(con, HTTP_HEADER_AUTHORIZATION, CONST_STR_LEN("Authorization")); + + UNUSED(backend); + if (NULL == vb) { + return mod_authn_gssapi_send_401_unauthorized_negotiate(con); + } + + if (0 != strncasecmp(vb->ptr, "Negotiate ", sizeof("Negotiate ")-1)) { + return mod_authn_gssapi_send_400_bad_request(srv, con); + } + + return mod_authn_gssapi_check_spnego(srv, con, (plugin_data *)p_d, require, vb->ptr+sizeof("Negotiate ")-1); +} + +/* + * HTTP auth Basic realm="kerberos" + */ + +static krb5_error_code mod_authn_gssapi_verify_krb5_init_creds(server *srv, krb5_context context, krb5_creds *creds, krb5_principal ap_req_server, krb5_keytab ap_req_keytab) +{ + krb5_error_code ret; + krb5_data req; + krb5_ccache local_ccache = NULL; + krb5_creds *new_creds = NULL; + krb5_auth_context auth_context = NULL; + krb5_keytab keytab = NULL; + char *server_name; + + memset(&req, 0, sizeof(req)); + + if (ap_req_keytab == NULL) { + ret = krb5_kt_default(context, &keytab); + if (ret) + return ret; + } else + keytab = ap_req_keytab; + + ret = krb5_cc_resolve(context, "MEMORY:", &local_ccache); + if (ret) { + log_error_write(srv, __FILE__, __LINE__, "s", "krb5_cc_resolve() failed when verifying KDC"); + /* return ret; */ + goto end; + } + + ret = krb5_cc_initialize(context, local_ccache, creds->client); + if (ret) { + log_error_write(srv, __FILE__, __LINE__, "s", "krb5_cc_initialize() failed when verifying KDC"); + goto end; + } + + ret = krb5_cc_store_cred(context, local_ccache, creds); + if (ret) { + log_error_write(srv, __FILE__, __LINE__, "s", "krb5_cc_store_cred() failed when verifying KDC"); + goto end; + } + + ret = krb5_unparse_name(context, ap_req_server, &server_name); + if (ret) { + log_error_write(srv, __FILE__, __LINE__, "s", "krb5_unparse_name() failed when verifying KDC"); + goto end; + } + krb5_free_unparsed_name(context, server_name); + + if (!krb5_principal_compare(context, ap_req_server, creds->server)) { + krb5_creds match_cred; + + memset(&match_cred, 0, sizeof(match_cred)); + + match_cred.client = creds->client; + match_cred.server = ap_req_server; + + ret = krb5_get_credentials(context, 0, local_ccache, &match_cred, &new_creds); + if (ret) { + log_error_write(srv, __FILE__, __LINE__, "s", "krb5_get_credentials() failed when verifying KDC"); + goto end; + } + creds = new_creds; + } + + ret = krb5_mk_req_extended(context, &auth_context, 0, NULL, creds, &req); + if (ret) { + log_error_write(srv, __FILE__, __LINE__, "s", "krb5_mk_req_extended() failed when verifying KDC"); + goto end; + } + + krb5_auth_con_free(context, auth_context); + auth_context = NULL; + ret = krb5_auth_con_init(context, &auth_context); + if (ret) { + log_error_write(srv, __FILE__, __LINE__, "s", "krb5_auth_con_init() failed when verifying KDC"); + goto end; + } + + /* use KRB5_AUTH_CONTEXT_DO_SEQUENCE to skip replay cache checks */ + krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); + ret = krb5_rd_req(context, &auth_context, &req, ap_req_server, keytab, 0, NULL); + if (ret) { + log_error_write(srv, __FILE__, __LINE__, "s", "krb5_rd_req() failed when verifying KDC"); + goto end; + } + + end: + krb5_free_data_contents(context, &req); + if (auth_context) + krb5_auth_con_free(context, auth_context); + if (new_creds) + krb5_free_creds(context, new_creds); + if (ap_req_keytab == NULL && keytab) + krb5_kt_close(context, keytab); + if (local_ccache) + krb5_cc_destroy(context, local_ccache); + + return ret; +} + +static int mod_authn_gssapi_store_krb5_creds(server *srv, connection *con, plugin_data *p, + krb5_context kcontext, krb5_ccache delegated_cred) +{ + krb5_error_code problem; + krb5_principal princ = NULL; + krb5_ccache ccache = NULL; + + problem = krb5_cc_get_principal(kcontext, delegated_cred, &princ); + if (problem) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_cc_get_principal", NULL, kcontext, problem); + goto end; + } + + if (mod_authn_gssapi_create_krb5_ccache(srv, con, p, kcontext, princ, &ccache)) { + goto end; + } + + problem = krb5_cc_copy_creds(kcontext, delegated_cred, ccache); + if (problem) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_cc_copy_creds", NULL, kcontext, problem); + goto end; + } + + krb5_free_principal(kcontext, princ); + krb5_cc_close(kcontext, ccache); + return 0; + + end: + if (princ) + krb5_free_principal(kcontext, princ); + if (ccache) + krb5_cc_destroy(kcontext, ccache); + return -1; +} + +static handler_t mod_authn_gssapi_send_401_unauthorized_basic (connection *con) +{ + con->http_status = 401; + con->mode = DIRECT; + http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("WWW-Authenticate"), CONST_STR_LEN("Basic realm=\"Kerberos\"")); + return HANDLER_FINISHED; +} + +static handler_t mod_authn_gssapi_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw) +{ + krb5_context kcontext = NULL; + krb5_keytab keytab = NULL; + krb5_principal s_princ = NULL; + krb5_principal c_princ = NULL; + krb5_creds c_creds; + krb5_ccache c_ccache = NULL; + krb5_ccache ret_ccache = NULL; + krb5_error_code code; + int ret; + buffer *sprinc; + buffer *user_at_realm = NULL; + plugin_data * const p = (plugin_data *)p_d; + + if (*pw == '\0') { + log_error_write(srv, __FILE__, __LINE__, "s", "Empty passwords are not accepted"); + return mod_authn_gssapi_send_401_unauthorized_basic(con); + } + + mod_authn_gssapi_patch_connection(srv, con, p); + + code = krb5_init_context(&kcontext); + if (code) { + log_error_write(srv, __FILE__, __LINE__, "sd", "krb5_init_context():", code); + return mod_authn_gssapi_send_401_unauthorized_basic(con); /*(well, should be 500)*/ + } + + code = krb5_kt_resolve(kcontext, p->conf.auth_gssapi_keytab->ptr, &keytab); + if (code) { + log_error_write(srv, __FILE__, __LINE__, "sdb", "krb5_kt_resolve():", code, p->conf.auth_gssapi_keytab); + return mod_authn_gssapi_send_401_unauthorized_basic(con); /*(well, should be 500)*/ + } + + sprinc = buffer_init_buffer(p->conf.auth_gssapi_principal); + if (strchr(sprinc->ptr, '/') == NULL) { + /*(copy HTTP Host, omitting port if port is present)*/ + /* ??? Should con->server_name be used if http_host not present? + * ??? What if con->server_name is not set? + * ??? Will this work below if IPv6 provided in Host? probably not */ + if (!buffer_is_empty(con->request.http_host)) { + buffer_append_string_len(sprinc, CONST_STR_LEN("/")); + buffer_append_string_len(sprinc, con->request.http_host->ptr, strcspn(con->request.http_host->ptr, ":")); + } + } + + /*(init c_creds before anything which might krb5_free_cred_contents())*/ + memset(&c_creds, 0, sizeof(c_creds)); + + ret = krb5_parse_name(kcontext, sprinc->ptr, &s_princ); + if (ret) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_parse_name", sprinc->ptr, kcontext, ret); + ret = -1; + goto end; + } + + if (strchr(username->ptr, '@') == NULL) { + user_at_realm = buffer_init_buffer(username); + BUFFER_APPEND_STRING_CONST(user_at_realm, "@"); + buffer_append_string_buffer(user_at_realm, require->realm); + } + + ret = krb5_parse_name(kcontext, (user_at_realm ? user_at_realm->ptr : username->ptr), &c_princ); + if (ret) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_parse_name", (user_at_realm ? user_at_realm->ptr : username->ptr), kcontext, ret); + if (user_at_realm) buffer_free(user_at_realm); + ret = -1; + goto end; + } + if (user_at_realm) buffer_free(user_at_realm); + /* XXX: if the qualified username with @realm should be in REMOTE_USER, + * then http_auth_backend_t basic interface needs to change to pass + * modifiable buffer *username, but note that const char *pw follows + * in the truncated buffer *username, so pw would need to be copied + * before modifying buffer *username */ + + /* + * char *name = NULL; + * ret = krb5_unparse_name(kcontext, c_princ, &name); + * if (ret == 0) { + * log_error_write(srv, __FILE__, __LINE__, "sbss", "Trying to get TGT for user:", username, "password:", pw); + * } + * krb5_free_unparsed_name(kcontext, name); + */ + + ret = krb5_get_init_creds_password(kcontext, &c_creds, c_princ, pw, NULL, NULL, 0, NULL, NULL); + if (ret) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_get_init_creds_password", NULL, kcontext, ret); + goto end; + } + + ret = mod_authn_gssapi_verify_krb5_init_creds(srv, kcontext, &c_creds, s_princ, keytab); + if (ret) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "mod_authn_gssapi_verify_krb5_init_creds", NULL, kcontext, ret); + goto end; + } + + ret = krb5_cc_resolve(kcontext, "MEMORY:", &ret_ccache); + if (ret) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_cc_resolve", NULL, kcontext, ret); + goto end; + } + + ret = krb5_cc_initialize(kcontext, ret_ccache, c_princ); + if (ret) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_cc_initialize", NULL, kcontext, ret); + goto end; + } + + ret = krb5_cc_store_cred(kcontext, ret_ccache, &c_creds); + if (ret) { + mod_authn_gssapi_log_krb5_error(srv, __FILE__, __LINE__, "krb5_cc_store_cred", NULL, kcontext, ret); + goto end; + } + + c_ccache = ret_ccache; + ret_ccache = NULL; + + end: + + krb5_free_cred_contents(kcontext, &c_creds); + if (ret_ccache) + krb5_cc_destroy(kcontext, ret_ccache); + + if (!ret && c_ccache && (ret = mod_authn_gssapi_store_krb5_creds(srv, con, p, kcontext, c_ccache))) { + log_error_write(srv, __FILE__, __LINE__, "sb", "mod_authn_gssapi_store_krb5_creds failed for", username); + } + + buffer_free(sprinc); + if (c_princ) + krb5_free_principal(kcontext, c_princ); + if (s_princ) + krb5_free_principal(kcontext, s_princ); + if (c_ccache) + krb5_cc_destroy(kcontext, c_ccache); + if (keytab) + krb5_kt_close(kcontext, keytab); + + krb5_free_context(kcontext); + + if (0 == ret && http_auth_match_rules(require,username->ptr,NULL,NULL)){ + return HANDLER_GO_ON; + } + else { + /* ret == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN or no authz rules match */ + log_error_write(srv, __FILE__, __LINE__, "sbsBsB", "password doesn't match for", con->uri.path, "username:", username, ", IP:", con->dst_addr_buf); + return mod_authn_gssapi_send_401_unauthorized_basic(con); + } +} + + +CONNECTION_FUNC(mod_authn_gssapi_handle_reset) { + plugin_data *p = (plugin_data *)p_d; + buffer *kccname = (buffer *)con->plugin_ctx[p->id]; + if (NULL != kccname) { + con->plugin_ctx[p->id] = NULL; + unlink(kccname->ptr+sizeof("FILE:")-1); + buffer_free(kccname); + } + + UNUSED(srv); + return HANDLER_GO_ON; +} + +int mod_authn_gssapi_plugin_init(plugin *p); +int mod_authn_gssapi_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("authn_gssapi"); + p->init = mod_authn_gssapi_init; + p->set_defaults= mod_authn_gssapi_set_defaults; + p->cleanup = mod_authn_gssapi_free; + p->connection_reset = mod_authn_gssapi_handle_reset; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_authn_ldap.c b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_ldap.c new file mode 100644 index 000000000..233fc239c --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_ldap.c @@ -0,0 +1,694 @@ +#include "first.h" + +#include <ldap.h> + +#include "base.h" +#include "http_auth.h" +#include "log.h" +#include "plugin.h" + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +typedef struct { + LDAP *ldap; + server *srv; + + buffer *auth_ldap_hostname; + buffer *auth_ldap_basedn; + buffer *auth_ldap_binddn; + buffer *auth_ldap_bindpw; + buffer *auth_ldap_filter; + buffer *auth_ldap_cafile; + buffer *auth_ldap_groupmember; + unsigned short auth_ldap_starttls; + unsigned short auth_ldap_allow_empty_pw; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf, *anon_conf; /* this is only used as long as no handler_ctx is setup */ + + buffer *ldap_filter; +} plugin_data; + +static handler_t mod_authn_ldap_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); + +INIT_FUNC(mod_authn_ldap_init) { + static http_auth_backend_t http_auth_backend_ldap = + { "ldap", mod_authn_ldap_basic, NULL, NULL }; + plugin_data *p = calloc(1, sizeof(*p)); + p->ldap_filter = buffer_init(); + + /* register http_auth_backend_ldap */ + http_auth_backend_ldap.p_d = p; + http_auth_backend_set(&http_auth_backend_ldap); + + return p; +} + +FREE_FUNC(mod_authn_ldap_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + buffer_free(p->ldap_filter); + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->auth_ldap_hostname); + buffer_free(s->auth_ldap_basedn); + buffer_free(s->auth_ldap_binddn); + buffer_free(s->auth_ldap_bindpw); + buffer_free(s->auth_ldap_filter); + buffer_free(s->auth_ldap_cafile); + buffer_free(s->auth_ldap_groupmember); + + if (NULL != s->ldap) ldap_unbind_ext_s(s->ldap, NULL, NULL); + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +/*(copied from mod_vhostdb_ldap.c)*/ +static void mod_authn_add_scheme (server *srv, buffer *host) +{ + if (!buffer_string_is_empty(host)) { + /* reformat hostname(s) as LDAP URIs (scheme://host:port) */ + static const char *schemes[] = { + "ldap://", "ldaps://", "ldapi://", "cldap://" + }; + char *b, *e = host->ptr; + buffer_clear(srv->tmp_buf); + while (*(b = e)) { + unsigned int j; + while (*b==' '||*b=='\t'||*b=='\r'||*b=='\n'||*b==',') ++b; + if (*b == '\0') break; + e = b; + while (*e!=' '&&*e!='\t'&&*e!='\r'&&*e!='\n'&&*e!=','&&*e!='\0') + ++e; + if (!buffer_string_is_empty(srv->tmp_buf)) + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(",")); + for (j = 0; j < sizeof(schemes)/sizeof(char *); ++j) { + if (0 == strncasecmp(b, schemes[j], strlen(schemes[j]))) { + break; + } + } + if (j == sizeof(schemes)/sizeof(char *)) + buffer_append_string_len(srv->tmp_buf, + CONST_STR_LEN("ldap://")); + buffer_append_string_len(srv->tmp_buf, b, (size_t)(e - b)); + } + buffer_copy_buffer(host, srv->tmp_buf); + } +} + +SETDEFAULTS_FUNC(mod_authn_ldap_set_defaults) { + plugin_data *p = p_d; + size_t i; +config_values_t cv[] = { + { "auth.backend.ldap.hostname", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "auth.backend.ldap.base-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "auth.backend.ldap.filter", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "auth.backend.ldap.ca-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "auth.backend.ldap.starttls", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "auth.backend.ldap.bind-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { "auth.backend.ldap.bind-pw", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ + { "auth.backend.ldap.allow-empty-pw", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ + { "auth.backend.ldap.groupmember", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + + s->auth_ldap_hostname = buffer_init(); + s->auth_ldap_basedn = buffer_init(); + s->auth_ldap_binddn = buffer_init(); + s->auth_ldap_bindpw = buffer_init(); + s->auth_ldap_filter = buffer_init(); + s->auth_ldap_cafile = buffer_init(); + s->auth_ldap_groupmember = buffer_init_string("memberUid"); + s->auth_ldap_starttls = 0; + s->ldap = NULL; + + cv[0].destination = s->auth_ldap_hostname; + cv[1].destination = s->auth_ldap_basedn; + cv[2].destination = s->auth_ldap_filter; + cv[3].destination = s->auth_ldap_cafile; + cv[4].destination = &(s->auth_ldap_starttls); + cv[5].destination = s->auth_ldap_binddn; + cv[6].destination = s->auth_ldap_bindpw; + cv[7].destination = &(s->auth_ldap_allow_empty_pw); + cv[8].destination = s->auth_ldap_groupmember; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!buffer_string_is_empty(s->auth_ldap_filter)) { + if (*s->auth_ldap_filter->ptr != ',') { + /*(translate '$' to '?' for consistency with other modules)*/ + char *d = s->auth_ldap_filter->ptr; + for (; NULL != (d = strchr(d, '$')); ++d) *d = '?'; + if (NULL == strchr(s->auth_ldap_filter->ptr, '?')) { + log_error_write(srv, __FILE__, __LINE__, "s", "ldap: auth.backend.ldap.filter is missing a replace-operator '?'"); + return HANDLER_ERROR; + } + } + } + + mod_authn_add_scheme(srv, s->auth_ldap_hostname); + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_authn_ldap_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(auth_ldap_hostname); + PATCH(auth_ldap_basedn); + PATCH(auth_ldap_binddn); + PATCH(auth_ldap_bindpw); + PATCH(auth_ldap_filter); + PATCH(auth_ldap_cafile); + PATCH(auth_ldap_starttls); + PATCH(auth_ldap_allow_empty_pw); + PATCH(auth_ldap_groupmember); + p->anon_conf = s; + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.hostname"))) { + PATCH(auth_ldap_hostname); + p->anon_conf = s; + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.base-dn"))) { + PATCH(auth_ldap_basedn); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.filter"))) { + PATCH(auth_ldap_filter); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.ca-file"))) { + PATCH(auth_ldap_cafile); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.starttls"))) { + PATCH(auth_ldap_starttls); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-dn"))) { + PATCH(auth_ldap_binddn); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-pw"))) { + PATCH(auth_ldap_bindpw); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.allow-empty-pw"))) { + PATCH(auth_ldap_allow_empty_pw); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.groupmember"))) { + PATCH(auth_ldap_groupmember); + } + } + } + + return 0; +} +#undef PATCH + +static void mod_authn_ldap_err(server *srv, const char *file, unsigned long line, const char *fn, int err) +{ + log_error_write(srv,file,line,"sSss","ldap:",fn,":",ldap_err2string(err)); +} + +static void mod_authn_ldap_opt_err(server *srv, const char *file, unsigned long line, const char *fn, LDAP *ld) +{ + int err; + ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err); + mod_authn_ldap_err(srv, file, line, fn, err); +} + +static void mod_authn_append_ldap_dn_escape(buffer * const filter, const buffer * const raw) { + /* [RFC4514] 2.4 Converting an AttributeValue from ASN.1 to a String + * + * https://www.ldap.com/ldap-dns-and-rdns + * http://social.technet.microsoft.com/wiki/contents/articles/5312.active-directory-characters-to-escape.aspx + */ + const char * const b = raw->ptr; + const size_t rlen = buffer_string_length(raw); + if (0 == rlen) return; + + if (b[0] == ' ') { /* || b[0] == '#' handled below for MS Active Directory*/ + /* escape leading ' ' */ + buffer_append_string_len(filter, CONST_STR_LEN("\\")); + } + + for (size_t i = 0; i < rlen; ++i) { + size_t len = i; + int bs = 0; + do { + /* encode all UTF-8 chars with high bit set + * (instead of validating UTF-8 and escaping only invalid UTF-8) */ + if (((unsigned char *)b)[len] > 0x7f) + break; + switch (b[len]) { + default: + continue; + case '"': case '+': case ',': case ';': case '\\': + case '<': case '>': + case '=': case '#': /* (for MS Active Directory) */ + bs = 1; + break; + case '\0': + break; + } + break; + } while (++len < rlen); + len -= i; + + if (len) { + buffer_append_string_len(filter, b+i, len); + if ((i += len) == rlen) break; + } + + if (bs) { + buffer_append_string_len(filter, CONST_STR_LEN("\\")); + buffer_append_string_len(filter, b+i, 1); + } + else { + /* escape NUL ('\0') (and all UTF-8 chars with high bit set) */ + char *f; + buffer_string_prepare_append(filter, 3); + f = filter->ptr + buffer_string_length(filter); + f[0] = '\\'; + f[1] = "0123456789abcdef"[(((unsigned char *)b)[i] >> 4) & 0xf]; + f[2] = "0123456789abcdef"[(((unsigned char *)b)[i] ) & 0xf]; + buffer_commit(filter, 3); + } + } + + if (rlen > 1 && b[rlen-1] == ' ') { + /* escape trailing ' ' */ + filter->ptr[buffer_string_length(filter)-1] = '\\'; + buffer_append_string_len(filter, CONST_STR_LEN(" ")); + } +} + +static void mod_authn_append_ldap_filter_escape(buffer * const filter, const buffer * const raw) { + /* [RFC4515] 3. String Search Filter Definition + * + * [...] + * + * The <valueencoding> rule ensures that the entire filter string is a + * valid UTF-8 string and provides that the octets that represent the + * ASCII characters "*" (ASCII 0x2a), "(" (ASCII 0x28), ")" (ASCII + * 0x29), "\" (ASCII 0x5c), and NUL (ASCII 0x00) are represented as a + * backslash "\" (ASCII 0x5c) followed by the two hexadecimal digits + * representing the value of the encoded octet. + * + * [...] + * + * As indicated by the <valueencoding> rule, implementations MUST escape + * all octets greater than 0x7F that are not part of a valid UTF-8 + * encoding sequence when they generate a string representation of a + * search filter. Implementations SHOULD accept as input strings that + * are not valid UTF-8 strings. This is necessary because RFC 2254 did + * not clearly define the term "string representation" (and in + * particular did not mention that the string representation of an LDAP + * search filter is a string of UTF-8-encoded Unicode characters). + * + * + * https://www.ldap.com/ldap-filters + * Although not required, you may escape any other characters that you want + * in the assertion value (or substring component) of a filter. This may be + * accomplished by prefixing the hexadecimal representation of each byte of + * the UTF-8 encoding of the character to escape with a backslash character. + */ + const char * const b = raw->ptr; + const size_t rlen = buffer_string_length(raw); + for (size_t i = 0; i < rlen; ++i) { + size_t len = i; + char *f; + do { + /* encode all UTF-8 chars with high bit set + * (instead of validating UTF-8 and escaping only invalid UTF-8) */ + if (((unsigned char *)b)[len] > 0x7f) + break; + switch (b[len]) { + default: + continue; + case '\0': case '(': case ')': case '*': case '\\': + break; + } + break; + } while (++len < rlen); + len -= i; + + if (len) { + buffer_append_string_len(filter, b+i, len); + if ((i += len) == rlen) break; + } + + /* escape * ( ) \ NUL ('\0') (and all UTF-8 chars with high bit set) */ + buffer_string_prepare_append(filter, 3); + f = filter->ptr + buffer_string_length(filter); + f[0] = '\\'; + f[1] = "0123456789abcdef"[(((unsigned char *)b)[i] >> 4) & 0xf]; + f[2] = "0123456789abcdef"[(((unsigned char *)b)[i] ) & 0xf]; + buffer_commit(filter, 3); + } +} + +static LDAP * mod_authn_ldap_host_init(server *srv, plugin_config *s) { + LDAP *ld; + int ret; + + if (buffer_string_is_empty(s->auth_ldap_hostname)) return NULL; + + if (LDAP_SUCCESS != ldap_initialize(&ld, s->auth_ldap_hostname->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sss", "ldap:", + "ldap_initialize():", strerror(errno)); + return NULL; + } + + ret = LDAP_VERSION3; + ret = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ret); + if (LDAP_OPT_SUCCESS != ret) { + mod_authn_ldap_err(srv, __FILE__, __LINE__, "ldap_set_option()", ret); + ldap_destroy(ld); + return NULL; + } + + if (s->auth_ldap_starttls) { + /* if no CA file is given, it is ok, as we will use encryption + * if the server requires a CAfile it will tell us */ + if (!buffer_string_is_empty(s->auth_ldap_cafile)) { + ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, + s->auth_ldap_cafile->ptr); + if (LDAP_OPT_SUCCESS != ret) { + mod_authn_ldap_err(srv, __FILE__, __LINE__, + "ldap_set_option(LDAP_OPT_X_TLS_CACERTFILE)", + ret); + ldap_destroy(ld); + return NULL; + } + } + + ret = ldap_start_tls_s(ld, NULL, NULL); + if (LDAP_OPT_SUCCESS != ret) { + mod_authn_ldap_err(srv,__FILE__,__LINE__,"ldap_start_tls_s()",ret); + ldap_destroy(ld); + return NULL; + } + } + + return ld; +} + +static int mod_authn_ldap_bind(server *srv, LDAP *ld, const char *dn, const char *pw) { + struct berval creds; + int ret; + + if (NULL != pw) { + *((const char **)&creds.bv_val) = pw; /*(cast away const)*/ + creds.bv_len = strlen(pw); + } else { + creds.bv_val = NULL; + creds.bv_len = 0; + } + + /* RFE: add functionality: LDAP_SASL_EXTERNAL (or GSS-SPNEGO, etc.) */ + + ret = ldap_sasl_bind_s(ld,dn,LDAP_SASL_SIMPLE,&creds,NULL,NULL,NULL); + if (ret != LDAP_SUCCESS) { + mod_authn_ldap_err(srv, __FILE__, __LINE__, "ldap_sasl_bind_s()", ret); + } + + return ret; +} + +static int mod_authn_ldap_rebind_proc (LDAP *ld, LDAP_CONST char *url, ber_tag_t ldap_request, ber_int_t msgid, void *params) { + plugin_config *s = (plugin_config *)params; + UNUSED(url); + UNUSED(ldap_request); + UNUSED(msgid); + return !buffer_string_is_empty(s->auth_ldap_binddn) + ? mod_authn_ldap_bind(s->srv, ld, + s->auth_ldap_binddn->ptr, + s->auth_ldap_bindpw->ptr) + : mod_authn_ldap_bind(s->srv, ld, NULL, NULL); +} + +static LDAPMessage * mod_authn_ldap_search(server *srv, plugin_config *s, char *base, char *filter) { + LDAPMessage *lm = NULL; + char *attrs[] = { LDAP_NO_ATTRS, NULL }; + int ret; + + /* + * 1. connect anonymously (if not already connected) + * (ldap connection is kept open unless connection-level error occurs) + * 2. issue search using filter + */ + + if (s->ldap != NULL) { + ret = ldap_search_ext_s(s->ldap, base, LDAP_SCOPE_SUBTREE, filter, + attrs, 0, NULL, NULL, NULL, 0, &lm); + if (LDAP_SUCCESS == ret) { + return lm; + } else if (LDAP_SERVER_DOWN != ret) { + /* try again (or initial request); + * ldap lib sometimes fails for the first call but reconnects */ + ret = ldap_search_ext_s(s->ldap, base, LDAP_SCOPE_SUBTREE, filter, + attrs, 0, NULL, NULL, NULL, 0, &lm); + if (LDAP_SUCCESS == ret) { + return lm; + } + } + + ldap_unbind_ext_s(s->ldap, NULL, NULL); + } + + s->ldap = mod_authn_ldap_host_init(srv, s); + if (NULL == s->ldap) { + return NULL; + } + + ldap_set_rebind_proc(s->ldap, mod_authn_ldap_rebind_proc, s); + ret = mod_authn_ldap_rebind_proc(s->ldap, NULL, 0, 0, s); + if (LDAP_SUCCESS != ret) { + ldap_destroy(s->ldap); + s->ldap = NULL; + return NULL; + } + + ret = ldap_search_ext_s(s->ldap, base, LDAP_SCOPE_SUBTREE, filter, + attrs, 0, NULL, NULL, NULL, 0, &lm); + if (LDAP_SUCCESS != ret) { + log_error_write(srv, __FILE__, __LINE__, "sSss", + "ldap:", ldap_err2string(ret), "; filter:", filter); + ldap_unbind_ext_s(s->ldap, NULL, NULL); + s->ldap = NULL; + return NULL; + } + + return lm; +} + +static char * mod_authn_ldap_get_dn(server *srv, plugin_config *s, char *base, char *filter) { + LDAP *ld; + LDAPMessage *lm, *first; + char *dn; + int count; + + lm = mod_authn_ldap_search(srv, s, base, filter); + if (NULL == lm) { + return NULL; + } + + ld = s->ldap; /*(must be after mod_authn_ldap_search(); might reconnect)*/ + + count = ldap_count_entries(ld, lm); + if (0 == count) { /*(no entires found)*/ + ldap_msgfree(lm); + return NULL; + } else if (count > 1) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ldap:", "more than one record returned. " + "you might have to refine the filter:", filter); + } + + if (NULL == (first = ldap_first_entry(ld, lm))) { + mod_authn_ldap_opt_err(srv,__FILE__,__LINE__,"ldap_first_entry()",ld); + ldap_msgfree(lm); + return NULL; + } + + if (NULL == (dn = ldap_get_dn(ld, first))) { + mod_authn_ldap_opt_err(srv,__FILE__,__LINE__,"ldap_get_dn()",ld); + ldap_msgfree(lm); + return NULL; + } + + ldap_msgfree(lm); + return dn; +} + +static handler_t mod_authn_ldap_memberOf(server *srv, plugin_config *s, const http_auth_require_t *require, const buffer *username, const char *userdn) { + array *groups = require->group; + buffer *filter = buffer_init(); + handler_t rc = HANDLER_ERROR; + + buffer_copy_string_len(filter, CONST_STR_LEN("(")); + buffer_append_string_buffer(filter, s->auth_ldap_groupmember); + buffer_append_string_len(filter, CONST_STR_LEN("=")); + if (buffer_is_equal_string(s->auth_ldap_groupmember, + CONST_STR_LEN("member"))) { + buffer_append_string(filter, userdn); + } else { /*(assume "memberUid"; consider validating in SETDEFAULTS_FUNC)*/ + mod_authn_append_ldap_filter_escape(filter, username); + } + buffer_append_string_len(filter, CONST_STR_LEN(")")); + + for (size_t i = 0; i < groups->used; ++i) { + char *base = groups->data[i]->key->ptr; + LDAPMessage *lm = mod_authn_ldap_search(srv, s, base, filter->ptr); + if (NULL != lm) { + int count = ldap_count_entries(s->ldap, lm); + ldap_msgfree(lm); + if (count > 0) { + rc = HANDLER_GO_ON; + break; + } + } + } + + buffer_free(filter); + return rc; +} + +static handler_t mod_authn_ldap_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw) { + plugin_data *p = (plugin_data *)p_d; + LDAP *ld; + char *dn; + buffer *template; + handler_t rc; + + mod_authn_ldap_patch_connection(srv, con, p); + p->anon_conf->srv = srv; + p->conf.srv = srv; + + if (pw[0] == '\0' && !p->conf.auth_ldap_allow_empty_pw) + return HANDLER_ERROR; + + template = p->conf.auth_ldap_filter; + if (buffer_string_is_empty(template)) { + return HANDLER_ERROR; + } + + /* build filter to get DN for uid = username */ + buffer_clear(p->ldap_filter); + if (*template->ptr == ',') { + /* special-case filter template beginning with ',' to be explicit DN */ + buffer_append_string_len(p->ldap_filter, CONST_STR_LEN("uid=")); + mod_authn_append_ldap_dn_escape(p->ldap_filter, username); + buffer_append_string_buffer(p->ldap_filter, template); + dn = p->ldap_filter->ptr; + } else { + for (char *b = template->ptr, *d; *b; b = d+1) { + if (NULL != (d = strchr(b, '?'))) { + buffer_append_string_len(p->ldap_filter, b, (size_t)(d - b)); + mod_authn_append_ldap_filter_escape(p->ldap_filter, username); + } else { + d = template->ptr + buffer_string_length(template); + buffer_append_string_len(p->ldap_filter, b, (size_t)(d - b)); + break; + } + } + + /* ldap_search for DN (synchronous; blocking) */ + dn = mod_authn_ldap_get_dn(srv, p->anon_conf, + p->conf.auth_ldap_basedn->ptr, + p->ldap_filter->ptr); + if (NULL == dn) { + return HANDLER_ERROR; + } + } + + /* auth against LDAP server (synchronous; blocking) */ + + ld = mod_authn_ldap_host_init(srv, &p->conf); + if (NULL == ld) { + if (dn != p->ldap_filter->ptr) ldap_memfree(dn); + return HANDLER_ERROR; + } + + /* Disable referral tracking. Target user should be in provided scope */ + { + int ret = ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); + if (LDAP_OPT_SUCCESS != ret) { + mod_authn_ldap_err(srv,__FILE__,__LINE__,"ldap_set_option()",ret); + ldap_destroy(ld); + if (dn != p->ldap_filter->ptr) ldap_memfree(dn); + return HANDLER_ERROR; + } + } + + if (LDAP_SUCCESS != mod_authn_ldap_bind(srv, ld, dn, pw)) { + ldap_destroy(ld); + if (dn != p->ldap_filter->ptr) ldap_memfree(dn); + return HANDLER_ERROR; + } + + ldap_unbind_ext_s(ld, NULL, NULL); /* disconnect */ + + if (http_auth_match_rules(require, username->ptr, NULL, NULL)) { + rc = HANDLER_GO_ON; /* access granted */ + } else { + rc = HANDLER_ERROR; + if (require->group->used) { + /*(must not re-use p->ldap_filter, since it might be used for dn)*/ + rc = mod_authn_ldap_memberOf(srv, &p->conf, require, username, dn); + } + } + + if (dn != p->ldap_filter->ptr) ldap_memfree(dn); + return rc; +} + +int mod_authn_ldap_plugin_init(plugin *p); +int mod_authn_ldap_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("authn_ldap"); + p->init = mod_authn_ldap_init; + p->set_defaults = mod_authn_ldap_set_defaults; + p->cleanup = mod_authn_ldap_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_authn_mysql.c b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_mysql.c new file mode 100644 index 000000000..3a41cd4bb --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_mysql.c @@ -0,0 +1,550 @@ +#include "first.h" + +/* mod_authn_mysql + * + * KNOWN LIMITATIONS: + * - no mechanism provided to configure SSL connection to a remote MySQL db + * + * FUTURE POTENTIAL PERFORMANCE ENHANCEMENTS: + * - database response is not cached + * TODO: db response caching (for limited time) to reduce load on db + * (only cache successful logins to prevent cache bloat?) + * (or limit number of entries (size) of cache) + * (maybe have negative cache (limited size) of names not found in database) + * - database query is synchronous and blocks waiting for response + * TODO: https://mariadb.com/kb/en/mariadb/using-the-non-blocking-library/ + * - opens and closes connection to MySQL db for each request (inefficient) + * (fixed) one-element cache for persistent connection open to last used db + * TODO: db connection pool (if asynchronous requests) + */ + +#include <mysql.h> + +#include "base.h" +#include "http_auth.h" +#include "log.h" +#include "md5.h" +#include "plugin.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT) +#include <unistd.h> /* crypt() */ +#endif +#ifdef HAVE_CRYPT_H +#include <crypt.h> +#endif + +typedef struct { + MYSQL *mysql_conn; + buffer *mysql_conn_host; + buffer *mysql_conn_user; + buffer *mysql_conn_pass; + buffer *mysql_conn_db; + int mysql_conn_port; + int auth_mysql_port; + buffer *auth_mysql_host; + buffer *auth_mysql_user; + buffer *auth_mysql_pass; + buffer *auth_mysql_db; + buffer *auth_mysql_socket; + buffer *auth_mysql_users_table; + buffer *auth_mysql_col_user; + buffer *auth_mysql_col_pass; + buffer *auth_mysql_col_realm; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +static void mod_authn_mysql_sock_close(plugin_config *pconf) { + if (NULL != pconf->mysql_conn) { + mysql_close(pconf->mysql_conn); + pconf->mysql_conn = NULL; + } +} + +static MYSQL * mod_authn_mysql_sock_connect(server *srv, plugin_config *pconf) { + if (NULL != pconf->mysql_conn) { + /* reuse open db connection if same ptrs to host user pass db port */ + if ( pconf->mysql_conn_host == pconf->auth_mysql_host + && pconf->mysql_conn_user == pconf->auth_mysql_user + && pconf->mysql_conn_pass == pconf->auth_mysql_pass + && pconf->mysql_conn_db == pconf->auth_mysql_db + && pconf->mysql_conn_port == pconf->auth_mysql_port) { + return pconf->mysql_conn; + } + mod_authn_mysql_sock_close(pconf); + } + + /* !! mysql_init() is not thread safe !! (see MySQL doc) */ + pconf->mysql_conn = mysql_init(NULL); + if (mysql_real_connect(pconf->mysql_conn, + pconf->auth_mysql_host->ptr, + pconf->auth_mysql_user->ptr, + pconf->auth_mysql_pass->ptr, + pconf->auth_mysql_db->ptr, + pconf->auth_mysql_port, + !buffer_string_is_empty(pconf->auth_mysql_socket) + ? pconf->auth_mysql_socket->ptr + : NULL, + CLIENT_IGNORE_SIGPIPE)) { + /* (copy ptrs to config data (has lifetime until server shutdown)) */ + pconf->mysql_conn_host = pconf->auth_mysql_host; + pconf->mysql_conn_user = pconf->auth_mysql_user; + pconf->mysql_conn_pass = pconf->auth_mysql_pass; + pconf->mysql_conn_db = pconf->auth_mysql_db; + pconf->mysql_conn_port = pconf->auth_mysql_port; + return pconf->mysql_conn; + } + else { + /*(note: any of these params might be buffers with b->ptr == NULL)*/ + log_error_write(srv, __FILE__, __LINE__, "sbsb"/*sb*/"sbss", + "opening connection to mysql:", pconf->auth_mysql_host, + "user:", pconf->auth_mysql_user, + /*"pass:", pconf->auth_mysql_pass,*//*(omit from logs)*/ + "db:", pconf->auth_mysql_db, + "failed:", mysql_error(pconf->mysql_conn)); + mod_authn_mysql_sock_close(pconf); + return NULL; + } +} + +static MYSQL * mod_authn_mysql_sock_acquire(server *srv, plugin_config *pconf) { + return mod_authn_mysql_sock_connect(srv, pconf); +} + +static void mod_authn_mysql_sock_release(server *srv, plugin_config *pconf) { + UNUSED(srv); + UNUSED(pconf); + /*(empty; leave db connection open)*/ + /* Note: mod_authn_mysql_result() calls mod_authn_mysql_sock_error() + * on error, so take that into account if making changes here. + * Must check if (NULL == pconf->mysql_conn) */ +} + +static void mod_authn_mysql_sock_error(server *srv, plugin_config *pconf) { + UNUSED(srv); + mod_authn_mysql_sock_close(pconf); +} + +static handler_t mod_authn_mysql_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); +static handler_t mod_authn_mysql_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]); + +INIT_FUNC(mod_authn_mysql_init) { + static http_auth_backend_t http_auth_backend_mysql = + { "mysql", mod_authn_mysql_basic, mod_authn_mysql_digest, NULL }; + plugin_data *p = calloc(1, sizeof(*p)); + + /* register http_auth_backend_mysql */ + http_auth_backend_mysql.p_d = p; + http_auth_backend_set(&http_auth_backend_mysql); + + return p; +} + +FREE_FUNC(mod_authn_mysql_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->auth_mysql_host); + buffer_free(s->auth_mysql_user); + buffer_free(s->auth_mysql_pass); + buffer_free(s->auth_mysql_db); + buffer_free(s->auth_mysql_socket); + buffer_free(s->auth_mysql_users_table); + buffer_free(s->auth_mysql_col_user); + buffer_free(s->auth_mysql_col_pass); + buffer_free(s->auth_mysql_col_realm); + + if (s->mysql_conn) mod_authn_mysql_sock_close(s); + + free(s); + } + free(p->config_storage); + } + mod_authn_mysql_sock_close(&p->conf); + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_authn_mysql_set_defaults) { + plugin_data *p = p_d; + size_t i; + config_values_t cv[] = { + { "auth.backend.mysql.host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "auth.backend.mysql.user", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "auth.backend.mysql.pass", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "auth.backend.mysql.db", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "auth.backend.mysql.port", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, + { "auth.backend.mysql.socket", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "auth.backend.mysql.users_table", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "auth.backend.mysql.col_user", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "auth.backend.mysql.col_pass", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "auth.backend.mysql.col_realm", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + + s->mysql_conn = NULL; + s->auth_mysql_host = buffer_init(); + s->auth_mysql_user = buffer_init(); + s->auth_mysql_pass = buffer_init(); + s->auth_mysql_db = buffer_init(); + s->auth_mysql_socket = buffer_init(); + s->auth_mysql_users_table = buffer_init(); + s->auth_mysql_col_user = buffer_init(); + s->auth_mysql_col_pass = buffer_init(); + s->auth_mysql_col_realm = buffer_init(); + + cv[0].destination = s->auth_mysql_host; + cv[1].destination = s->auth_mysql_user; + cv[2].destination = s->auth_mysql_pass; + cv[3].destination = s->auth_mysql_db; + cv[4].destination = &s->auth_mysql_port; + cv[5].destination = s->auth_mysql_socket; + cv[6].destination = s->auth_mysql_users_table; + cv[7].destination = s->auth_mysql_col_user; + cv[8].destination = s->auth_mysql_col_pass; + cv[9].destination = s->auth_mysql_col_realm; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!buffer_is_empty(s->auth_mysql_col_user) + && buffer_string_is_empty(s->auth_mysql_col_user)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "auth.backend.mysql.col_user must not be blank"); + return HANDLER_ERROR; + } + if (!buffer_is_empty(s->auth_mysql_col_pass) + && buffer_string_is_empty(s->auth_mysql_col_pass)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "auth.backend.mysql.col_pass must not be blank"); + return HANDLER_ERROR; + } + if (!buffer_is_empty(s->auth_mysql_col_realm) + && buffer_string_is_empty(s->auth_mysql_col_realm)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "auth.backend.mysql.col_realm must not be blank"); + return HANDLER_ERROR; + } + } + + if (p->config_storage[0]) { /*(always true)*/ + plugin_config *s = p->config_storage[0]; + if (buffer_is_empty(s->auth_mysql_col_user)) { + buffer_copy_string_len(s->auth_mysql_col_user, CONST_STR_LEN("user")); + } + if (buffer_is_empty(s->auth_mysql_col_pass)) { + buffer_copy_string_len(s->auth_mysql_col_pass, CONST_STR_LEN("password")); + } + if (buffer_is_empty(s->auth_mysql_col_realm)) { + buffer_copy_string_len(s->auth_mysql_col_realm, CONST_STR_LEN("realm")); + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_authn_mysql_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(auth_mysql_host); + PATCH(auth_mysql_user); + PATCH(auth_mysql_pass); + PATCH(auth_mysql_db); + PATCH(auth_mysql_port); + PATCH(auth_mysql_socket); + PATCH(auth_mysql_users_table); + PATCH(auth_mysql_col_user); + PATCH(auth_mysql_col_pass); + PATCH(auth_mysql_col_realm); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.host"))) { + PATCH(auth_mysql_host); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.user"))) { + PATCH(auth_mysql_user); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.pass"))) { + PATCH(auth_mysql_pass); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.db"))) { + PATCH(auth_mysql_db); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.port"))) { + PATCH(auth_mysql_port); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.socket"))) { + PATCH(auth_mysql_socket); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.users_table"))) { + PATCH(auth_mysql_users_table); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.col_user"))) { + PATCH(auth_mysql_col_user); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.col_pass"))) { + PATCH(auth_mysql_col_pass); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.mysql.col_realm"))) { + PATCH(auth_mysql_col_realm); + } + } + } + + return 0; +} +#undef PATCH + +static int mod_authn_mysql_password_cmp(const char *userpw, unsigned long userpwlen, const char *reqpw) { + #if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT) + if (userpwlen >= 3 && userpw[0] == '$' && userpw[2] == '$') { + /* md5 crypt() + * request by Nicola Tiling <nti@w4w.net> */ + const char *saltb = userpw+3; + const char *salte = strchr(saltb, '$'); + char salt[32]; + size_t slen = (NULL != salte) ? (size_t)(salte - saltb) : sizeof(salt); + + if (slen < sizeof(salt)) { + char *crypted; + #if defined(HAVE_CRYPT_R) + struct crypt_data crypt_tmp_data; + #ifdef _AIX + memset(&crypt_tmp_data, 0, sizeof(crypt_tmp_data)); + #else + crypt_tmp_data.initialized = 0; + #endif + #endif + memcpy(salt, saltb, slen); + salt[slen] = '\0'; + + #if defined(HAVE_CRYPT_R) + crypted = crypt_r(reqpw, salt, &crypt_tmp_data); + #else + crypted = crypt(reqpw, salt); + #endif + if (NULL != crypted) { + return strcmp(userpw, crypted); + } + } + } + else + #endif + if (32 == userpwlen) { + /* plain md5 */ + li_MD5_CTX Md5Ctx; + unsigned char HA1[16]; + unsigned char md5pw[16]; + + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)reqpw, strlen(reqpw)); + li_MD5_Final(HA1, &Md5Ctx); + + /*(compare 16-byte MD5 binary instead of converting to hex strings + * in order to then have to do case-insensitive hex str comparison)*/ + return (0 == http_auth_md5_hex2bin(userpw, 32 /*(userpwlen)*/, md5pw)) + ? memcmp(HA1, md5pw, sizeof(md5pw)) + : -1; + } + + return -1; +} + +static int mod_authn_mysql_result(server *srv, plugin_data *p, const char *pw, unsigned char HA1[16]) { + MYSQL_RES *result = mysql_store_result(p->conf.mysql_conn); + int rc = -1; + my_ulonglong num_rows; + + if (NULL == result) { + /*(future: might log mysql_error() string)*/ + #if 0 + log_error_write(srv, __FILE__, __LINE__, "ss", "mysql_store_result:", + mysql_error(p->conf.mysql_conn)); + #endif + mod_authn_mysql_sock_error(srv, &p->conf); + return -1; + } + + num_rows = mysql_num_rows(result); + if (1 == num_rows) { + MYSQL_ROW row = mysql_fetch_row(result); + unsigned long *lengths = mysql_fetch_lengths(result); + if (NULL == lengths) { + /*(error; should not happen)*/ + } + else if (pw) { /* used with HTTP Basic auth */ + rc = mod_authn_mysql_password_cmp(row[0], lengths[0], pw); + } + else { /* used with HTTP Digest auth */ + rc = http_auth_md5_hex2bin(row[0], lengths[0], HA1); + } + } + else if (0 == num_rows) { + /* user,realm not found */ + } + else { + /* (multiple rows returned, which should not happen) */ + /* (future: might log if multiple rows returned; unexpected result) */ + } + mysql_free_result(result); + return rc; +} + +static handler_t mod_authn_mysql_query(server *srv, connection *con, void *p_d, const char *username, const char *realm, const char *pw, unsigned char HA1[16]) { + plugin_data *p = (plugin_data *)p_d; + int rc = -1; + + mod_authn_mysql_patch_connection(srv, con, p); + + if (buffer_string_is_empty(p->conf.auth_mysql_users_table)) { + /*(auth.backend.mysql.host, auth.backend.mysql.db might be NULL; do not log)*/ + log_error_write(srv, __FILE__, __LINE__, "sb", + "auth config missing auth.backend.mysql.users_table for uri:", + con->request.uri); + return HANDLER_ERROR; + } + + do { + size_t unamelen = strlen(username); + size_t urealmlen = strlen(realm); + char q[1024], uname[512], urealm[512]; + unsigned long mrc; + + if (unamelen > sizeof(uname)/2-1) + return HANDLER_ERROR; + if (urealmlen > sizeof(urealm)/2-1) + return HANDLER_ERROR; + + if (!mod_authn_mysql_sock_acquire(srv, &p->conf)) { + return HANDLER_ERROR; + } + + #if 0 + mrc = mysql_real_escape_string_quote(p->conf.mysql_conn,uname,username, + (unsigned long)unamelen, '\''); + if ((unsigned long)~0 == mrc) break; + + mrc = mysql_real_escape_string_quote(p->conf.mysql_conn,urealm,realm, + (unsigned long)urealmlen, '\''); + if ((unsigned long)~0 == mrc) break; + #else + mrc = mysql_real_escape_string(p->conf.mysql_conn, uname, + username, (unsigned long)unamelen); + if ((unsigned long)~0 == mrc) break; + + mrc = mysql_real_escape_string(p->conf.mysql_conn, urealm, + realm, (unsigned long)urealmlen); + if ((unsigned long)~0 == mrc) break; + #endif + + rc = snprintf(q, sizeof(q), + "SELECT %s FROM %s WHERE %s='%s' AND %s='%s'", + p->conf.auth_mysql_col_pass->ptr, + p->conf.auth_mysql_users_table->ptr, + p->conf.auth_mysql_col_user->ptr, + uname, + p->conf.auth_mysql_col_realm->ptr, + urealm); + + if (rc >= (int)sizeof(q)) { + rc = -1; + break; + } + + /* for now we stay synchronous */ + if (0 != mysql_query(p->conf.mysql_conn, q)) { + /* reconnect to db and retry once if query error occurs */ + mod_authn_mysql_sock_error(srv, &p->conf); + if (!mod_authn_mysql_sock_acquire(srv, &p->conf)) { + rc = -1; + break; + } + if (0 != mysql_query(p->conf.mysql_conn, q)) { + /*(note: any of these params might be bufs w/ b->ptr == NULL)*/ + log_error_write(srv, __FILE__, __LINE__, "sbsb"/*sb*/"sbssss", + "mysql_query host:", p->conf.auth_mysql_host, + "user:", p->conf.auth_mysql_user, + /*(omit pass from logs)*/ + /*"pass:", p->conf.auth_mysql_pass,*/ + "db:", p->conf.auth_mysql_db, + "query:", q, + "failed:", mysql_error(p->conf.mysql_conn)); + rc = -1; + break; + } + } + + rc = mod_authn_mysql_result(srv, p, pw, HA1); + + } while (0); + + mod_authn_mysql_sock_release(srv, &p->conf); + + return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR; +} + +static handler_t mod_authn_mysql_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw) { + /*(HA1 is not written since pw passed should not be NULL; + * avoid passing NULL since subroutine expects unsigned char HA1[16] arg)*/ + static unsigned char HA1[16]; + char *realm = require->realm->ptr; + handler_t rc =mod_authn_mysql_query(srv,con,p_d,username->ptr,realm,pw,HA1); + if (HANDLER_GO_ON != rc) return rc; + return http_auth_match_rules(require, username->ptr, NULL, NULL) + ? HANDLER_GO_ON /* access granted */ + : HANDLER_ERROR; +} + +static handler_t mod_authn_mysql_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]) { + return mod_authn_mysql_query(srv,con,p_d,username,realm,NULL,HA1); +} + +int mod_authn_mysql_plugin_init(plugin *p); +int mod_authn_mysql_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("authn_mysql"); + p->init = mod_authn_mysql_init; + p->set_defaults= mod_authn_mysql_set_defaults; + p->cleanup = mod_authn_mysql_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_authn_pam.c b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_pam.c new file mode 100644 index 000000000..2439f1dfe --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_pam.c @@ -0,0 +1,185 @@ +#include "first.h" + +/* mod_authn_pam + * + * FUTURE POTENTIAL PERFORMANCE ENHANCEMENTS: + * - database response is not cached + * TODO: db response caching (for limited time) to reduce load on db + * (only cache successful logins to prevent cache bloat?) + * (or limit number of entries (size) of cache) + * (maybe have negative cache (limited size) of names not found in database) + * - database query is synchronous and blocks waiting for response + */ + +#include <security/pam_appl.h> + +#include "base.h" +#include "http_auth.h" +#include "log.h" +#include "plugin.h" + +#include <stdlib.h> +#include <string.h> + +typedef struct { + array *opts; + const char *service; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +static handler_t mod_authn_pam_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); + +INIT_FUNC(mod_authn_pam_init) { + static http_auth_backend_t http_auth_backend_pam = + { "pam", mod_authn_pam_basic, NULL, NULL }; + plugin_data *p = calloc(1, sizeof(*p)); + + /* register http_auth_backend_pam */ + http_auth_backend_pam.p_d = p; + http_auth_backend_set(&http_auth_backend_pam); + + return p; +} + +FREE_FUNC(mod_authn_pam_free) { + plugin_data *p = p_d; + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + for (size_t i = 0; i < srv->config_context->used; ++i) { + plugin_config *s = p->config_storage[i]; + if (NULL == s) continue; + array_free(s->opts); + free(s); + } + free(p->config_storage); + } + free(p); + UNUSED(srv); + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_authn_pam_set_defaults) { + plugin_data *p = p_d; + config_values_t cv[] = { + { "auth.backend.pam.opts", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (size_t i = 0; i < srv->config_context->used; ++i) { + data_config const *config = (data_config const*)srv->config_context->data[i]; + data_string *ds; + plugin_config *s = calloc(1, sizeof(plugin_config)); + s->opts = array_init(); + + cv[0].destination = s->opts; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (0 == s->opts->used) continue; + + ds = (data_string *) + array_get_element_klen(s->opts, CONST_STR_LEN("service")); + s->service = (NULL != ds) ? ds->value->ptr : "http"; + } + + if (p->config_storage[0]->service == NULL) + p->config_storage[0]->service = "http"; + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_authn_pam_patch_connection(server *srv, connection *con, plugin_data *p) { + plugin_config *s = p->config_storage[0]; + PATCH(service); + + /* skip the first, the global context */ + for (size_t i = 1; i < srv->config_context->used; ++i) { + data_config *dc = (data_config *)srv->config_context->data[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + s = p->config_storage[i]; + for (size_t j = 0; j < dc->value->used; ++j) { + data_unset *du = dc->value->data[j]; + if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.pam.opts"))) { + PATCH(service); + } + } + } + + return 0; +} +#undef PATCH + +static int mod_authn_pam_fn_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { + const char * const pw = (char *)appdata_ptr; + struct pam_response * const pr = *resp = + (struct pam_response *)malloc(num_msg * sizeof(struct pam_response)); + for (int i = 0; i < num_msg; ++i) { + const int style = msg[i]->msg_style; + pr[i].resp_retcode = 0; + pr[i].resp = (style==PAM_PROMPT_ECHO_OFF || style==PAM_PROMPT_ECHO_ON) + ? strdup(pw) + : NULL; + } + return PAM_SUCCESS; +} + +static handler_t mod_authn_pam_query(server *srv, connection *con, void *p_d, const buffer *username, const char *realm, const char *pw) { + plugin_data *p = (plugin_data *)p_d; + pam_handle_t *pamh = NULL; + struct pam_conv conv = { mod_authn_pam_fn_conv, NULL }; + const int flags = PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK; + int rc; + UNUSED(realm); + *(const char **)&conv.appdata_ptr = pw; /*(cast away const)*/ + + mod_authn_pam_patch_connection(srv, con, p); + + rc = pam_start(p->conf.service, username->ptr, &conv, &pamh); + if (PAM_SUCCESS != rc + || PAM_SUCCESS !=(rc = pam_set_item(pamh,PAM_RHOST,con->dst_addr_buf->ptr)) + || PAM_SUCCESS !=(rc = pam_authenticate(pamh, flags)) + || PAM_SUCCESS !=(rc = pam_acct_mgmt(pamh, flags))) + log_error_write(srv, __FILE__, __LINE__, "ss", + "pam:", pam_strerror(pamh, rc)); + pam_end(pamh, rc); + return (PAM_SUCCESS == rc) ? HANDLER_GO_ON : HANDLER_ERROR; +} + +static handler_t mod_authn_pam_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw) { + char *realm = require->realm->ptr; + handler_t rc = mod_authn_pam_query(srv, con, p_d, username, realm, pw); + if (HANDLER_GO_ON != rc) return rc; + return http_auth_match_rules(require, username->ptr, NULL, NULL) + ? HANDLER_GO_ON /* access granted */ + : HANDLER_ERROR; +} + +int mod_authn_pam_plugin_init(plugin *p); +int mod_authn_pam_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("authn_pam"); + p->data = NULL; + p->init = mod_authn_pam_init; + p->cleanup = mod_authn_pam_free; + p->set_defaults= mod_authn_pam_set_defaults; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_authn_sasl.c b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_sasl.c new file mode 100644 index 000000000..d4c46e3a9 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_sasl.c @@ -0,0 +1,284 @@ +#include "first.h" + +/* mod_authn_sasl + * + * FUTURE POTENTIAL PERFORMANCE ENHANCEMENTS: + * - database response is not cached + * TODO: db response caching (for limited time) to reduce load on db + * (only cache successful logins to prevent cache bloat?) + * (or limit number of entries (size) of cache) + * (maybe have negative cache (limited size) of names not found in database) + * - database query is synchronous and blocks waiting for response + */ + +#include <sasl/sasl.h> + +#include "base.h" +#include "http_auth.h" +#include "log.h" +#include "plugin.h" + +#include <sys/utsname.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +typedef struct { + array *opts; + const char *service; + const char *fqdn; + const buffer *pwcheck_method; + const buffer *sasldb_path; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; + buffer *fqdn; + int initonce; +} plugin_data; + +static handler_t mod_authn_sasl_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw); + +INIT_FUNC(mod_authn_sasl_init) { + static http_auth_backend_t http_auth_backend_sasl = + { "sasl", mod_authn_sasl_basic, NULL, NULL }; + plugin_data *p = calloc(1, sizeof(*p)); + + /* register http_auth_backend_sasl */ + http_auth_backend_sasl.p_d = p; + http_auth_backend_set(&http_auth_backend_sasl); + + return p; +} + +FREE_FUNC(mod_authn_sasl_free) { + plugin_data *p = p_d; + if (!p) return HANDLER_GO_ON; + + if (p->initonce) sasl_done(); + + if (p->config_storage) { + for (size_t i = 0; i < srv->config_context->used; ++i) { + plugin_config *s = p->config_storage[i]; + if (NULL == s) continue; + array_free(s->opts); + free(s); + } + free(p->config_storage); + } + buffer_free(p->fqdn); + free(p); + UNUSED(srv); + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_authn_sasl_set_defaults) { + plugin_data *p = p_d; + size_t i; + config_values_t cv[] = { + { "auth.backend.sasl.opts", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const *config = (data_config const*)srv->config_context->data[i]; + data_string *ds; + plugin_config *s = calloc(1, sizeof(plugin_config)); + s->opts = array_init(); + + cv[0].destination = s->opts; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (0 == s->opts->used) continue; + + ds = (data_string *) + array_get_element_klen(s->opts, CONST_STR_LEN("service")); + s->service = (NULL != ds) ? ds->value->ptr : "http"; + + ds = (data_string *) + array_get_element_klen(s->opts, CONST_STR_LEN("fqdn")); + if (NULL != ds) s->fqdn = ds->value->ptr; + if (NULL == s->fqdn) { + if (NULL == p->fqdn) { + struct utsname uts; + if (0 != uname(&uts)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "uname():", strerror(errno)); + return HANDLER_ERROR; + } + p->fqdn = buffer_init_string(uts.nodename); + } + s->fqdn = p->fqdn->ptr; + } + + ds = (data_string *) + array_get_element_klen(s->opts, CONST_STR_LEN("pwcheck_method")); + if (NULL != ds) { + s->pwcheck_method = ds->value; + if (!buffer_is_equal_string(ds->value, CONST_STR_LEN("saslauthd")) + && !buffer_is_equal_string(ds->value, CONST_STR_LEN("auxprop")) + && !buffer_is_equal_string(ds->value, CONST_STR_LEN("sasldb"))){ + log_error_write(srv, __FILE__, __LINE__, "sb", + "sasl pwcheck_method must be one of saslauthd, " + "sasldb, or auxprop, not:", ds->value); + return HANDLER_ERROR; + } + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("sasldb"))) { + /* Cyrus libsasl2 expects "auxprop" instead of "sasldb" + * (mod_authn_sasl_cb_getopt auxprop_plugin returns "sasldb") */ + buffer_copy_string_len(ds->value, CONST_STR_LEN("auxprop")); + } + } + + ds = (data_string *) + array_get_element_klen(s->opts, CONST_STR_LEN("sasldb_path")); + if (NULL != ds) s->sasldb_path = ds->value; + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_authn_sasl_patch_connection(server *srv, connection *con, plugin_data *p) { + plugin_config *s = p->config_storage[0]; + PATCH(service); + PATCH(fqdn); + PATCH(pwcheck_method); + PATCH(sasldb_path); + + /* skip the first, the global context */ + for (size_t i = 1; i < srv->config_context->used; ++i) { + data_config *dc = (data_config *)srv->config_context->data[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + s = p->config_storage[i]; + for (size_t j = 0; j < dc->value->used; ++j) { + data_unset *du = dc->value->data[j]; + if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.sasl.opts"))) { + PATCH(service); + PATCH(fqdn); + PATCH(pwcheck_method); + PATCH(sasldb_path); + } + } + } + + return 0; +} +#undef PATCH + +static int mod_authn_sasl_cb_getopt(void *p_d, const char *plugin_name, const char *opt, const char **res, unsigned *len) { + plugin_data *p = (plugin_data *)p_d; + size_t sz; + + if (0 == strcmp(opt, "pwcheck_method")) { + if (!buffer_string_is_empty(p->conf.pwcheck_method)) { + *res = p->conf.pwcheck_method->ptr; + sz = buffer_string_length(p->conf.pwcheck_method); + } + else { /* default */ + *res = "saslauthd"; + sz = sizeof("saslauthd")-1; + } + } + else if (0 == strcmp(opt, "sasldb_path") + && !buffer_string_is_empty(p->conf.sasldb_path)) { + *res = p->conf.sasldb_path->ptr; + sz = buffer_string_length(p->conf.sasldb_path); + } + else if (0 == strcmp(opt, "auxprop_plugin")) { + *res = "sasldb"; + sz = sizeof("sasldb")-1; + } + else { + UNUSED(plugin_name); + return SASL_FAIL; + } + + if (len) *len = (unsigned int)sz; + return SASL_OK; +} + +static int mod_authn_sasl_cb_log(void *vsrv, int level, const char *message) { + switch (level) { + #if 0 + case SASL_LOG_NONE: + case SASL_LOG_NOTE: + case SASL_LOG_DEBUG: + case SASL_LOG_TRACE: + case SASL_LOG_PASS: + #endif + default: + break; + case SASL_LOG_ERR: + case SASL_LOG_FAIL: + case SASL_LOG_WARN: /* (might omit SASL_LOG_WARN if too noisy in logs) */ + log_error_write((server *)vsrv, __FILE__, __LINE__, "s", message); + break; + } + return SASL_OK; +} + +static handler_t mod_authn_sasl_query(server *srv, connection *con, void *p_d, const buffer *username, const char *realm, const char *pw) { + plugin_data *p = (plugin_data *)p_d; + sasl_conn_t *sc; + sasl_callback_t const cb[] = { + { SASL_CB_GETOPT, (int(*)())mod_authn_sasl_cb_getopt, (void *) p }, + { SASL_CB_LOG, (int(*)())mod_authn_sasl_cb_log, (void *) srv }, + { SASL_CB_LIST_END, NULL, NULL } + }; + int rc; + + mod_authn_sasl_patch_connection(srv, con, p); + + if (!p->initonce) { + /* must be done once, but after fork() if multiple lighttpd workers */ + rc = sasl_server_init(cb, NULL); + if (SASL_OK != rc) return HANDLER_ERROR; + p->initonce = 1; + } + + rc = sasl_server_new(p->conf.service, p->conf.fqdn, + realm, NULL, NULL, cb, 0, &sc); + if (SASL_OK == rc) { + rc = sasl_checkpass(sc, CONST_BUF_LEN(username), pw, strlen(pw)); + sasl_dispose(&sc); + } + + return (SASL_OK == rc) ? HANDLER_GO_ON : HANDLER_ERROR; +} + +static handler_t mod_authn_sasl_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw) { + char *realm = require->realm->ptr; + handler_t rc = mod_authn_sasl_query(srv, con, p_d, username, realm, pw); + if (HANDLER_GO_ON != rc) return rc; + return http_auth_match_rules(require, username->ptr, NULL, NULL) + ? HANDLER_GO_ON /* access granted */ + : HANDLER_ERROR; +} + +int mod_authn_sasl_plugin_init(plugin *p); +int mod_authn_sasl_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("authn_sasl"); + p->init = mod_authn_sasl_init; + p->set_defaults= mod_authn_sasl_set_defaults; + p->cleanup = mod_authn_sasl_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_cgi.c b/data/lighttpd/lighttpd-1.4.53/src/mod_cgi.c new file mode 100644 index 000000000..aeec06f98 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_cgi.c @@ -0,0 +1,1081 @@ +#include "first.h" + +#include "base.h" +#include "stat_cache.h" +#include "http_kv.h" +#include "log.h" +#include "connections.h" +#include "joblist.h" +#include "response.h" +#include "http_chunk.h" +#include "http_header.h" + +#include "plugin.h" + +#include <sys/types.h> +#include "sys-mmap.h" +#include "sys-socket.h" +# include <sys/wait.h> + +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <fdevent.h> + +#include <fcntl.h> +#include <signal.h> + +static int pipe_cloexec(int pipefd[2]) { + #ifdef HAVE_PIPE2 + if (0 == pipe2(pipefd, O_CLOEXEC)) return 0; + #endif + return 0 == pipe(pipefd) + #ifdef FD_CLOEXEC + && 0 == fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) + && 0 == fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) + #endif + ? 0 + : -1; +} + +typedef struct { + char *ptr; + size_t used; + size_t size; + size_t *offsets; + size_t osize; + size_t oused; + char **eptr; + size_t esize; + buffer *ld_preload; + buffer *ld_library_path; + #ifdef __CYGWIN__ + buffer *systemroot; + #endif +} env_accum; + +typedef struct { + struct { pid_t pid; void *ctx; } *ptr; + size_t used; + size_t size; +} buffer_pid_t; + +typedef struct { + array *cgi; + unsigned short execute_x_only; + unsigned short local_redir; + unsigned short xsendfile_allow; + unsigned short upgrade; + array *xsendfile_docroot; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; + buffer_pid_t cgi_pid; + env_accum env; +} plugin_data; + +typedef struct { + pid_t pid; + int fd; + int fdtocgi; + int fde_ndx; /* index into the fd-event buffer */ + int fde_ndx_tocgi; /* index into the fd-event buffer */ + + connection *remote_conn; /* dumb pointer */ + plugin_data *plugin_data; /* dumb pointer */ + + buffer *response; + buffer *cgi_handler; /* dumb pointer */ + http_response_opts opts; + plugin_config conf; +} handler_ctx; + +static handler_ctx * cgi_handler_ctx_init(void) { + handler_ctx *hctx = calloc(1, sizeof(*hctx)); + + force_assert(hctx); + + hctx->response = chunk_buffer_acquire(); + hctx->fd = -1; + hctx->fdtocgi = -1; + + return hctx; +} + +static void cgi_handler_ctx_free(handler_ctx *hctx) { + chunk_buffer_release(hctx->response); + free(hctx); +} + +INIT_FUNC(mod_cgi_init) { + plugin_data *p; + const char *s; + + p = calloc(1, sizeof(*p)); + + force_assert(p); + + /* for valgrind */ + s = getenv("LD_PRELOAD"); + if (s) p->env.ld_preload = buffer_init_string(s); + s = getenv("LD_LIBRARY_PATH"); + if (s) p->env.ld_library_path = buffer_init_string(s); + #ifdef __CYGWIN__ + /* CYGWIN needs SYSTEMROOT */ + s = getenv("SYSTEMROOT"); + if (s) p->env.systemroot = buffer_init_string(s); + #endif + + return p; +} + + +FREE_FUNC(mod_cgi_free) { + plugin_data *p = p_d; + buffer_pid_t *r = &(p->cgi_pid); + + UNUSED(srv); + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->cgi); + array_free(s->xsendfile_docroot); + + free(s); + } + free(p->config_storage); + } + + + if (r->ptr) free(r->ptr); + free(p->env.ptr); + free(p->env.offsets); + free(p->env.eptr); + buffer_free(p->env.ld_preload); + buffer_free(p->env.ld_library_path); + #ifdef __CYGWIN__ + buffer_free(p->env.systemroot); + #endif + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "cgi.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "cgi.execute-x-only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "cgi.x-sendfile", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "cgi.x-sendfile-docroot", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "cgi.local-redir", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "cgi.upgrade", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET} + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + force_assert(p->config_storage); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + force_assert(s); + + s->cgi = array_init(); + s->execute_x_only = 0; + s->local_redir = 0; + s->xsendfile_allow= 0; + s->xsendfile_docroot = array_init(); + s->upgrade = 0; + + cv[0].destination = s->cgi; + cv[1].destination = &(s->execute_x_only); + cv[2].destination = &(s->xsendfile_allow); + cv[3].destination = s->xsendfile_docroot; + cv[4].destination = &(s->local_redir); + cv[5].destination = &(s->upgrade); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_kvstring(s->cgi)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for cgi.assign; expected list of \"ext\" => \"exepath\""); + return HANDLER_ERROR; + } + + if (s->xsendfile_docroot->used) { + size_t j; + for (j = 0; j < s->xsendfile_docroot->used; ++j) { + data_string *ds = (data_string *)s->xsendfile_docroot->data[j]; + if (ds->type != TYPE_STRING) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected type for key cgi.x-sendfile-docroot; expected: cgi.x-sendfile-docroot = ( \"/allowed/path\", ... )"); + return HANDLER_ERROR; + } + if (ds->value->ptr[0] != '/') { + log_error_write(srv, __FILE__, __LINE__, "SBs", + "cgi.x-sendfile-docroot paths must begin with '/'; invalid: \"", ds->value, "\""); + return HANDLER_ERROR; + } + buffer_path_simplify(ds->value, ds->value); + buffer_append_slash(ds->value); + } + } + } + + return HANDLER_GO_ON; +} + + +static void cgi_pid_add(plugin_data *p, pid_t pid, void *ctx) { + buffer_pid_t *r = &(p->cgi_pid); + + if (r->size == 0) { + r->size = 16; + r->ptr = malloc(sizeof(*r->ptr) * r->size); + force_assert(r->ptr); + } else if (r->used == r->size) { + r->size += 16; + r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size); + force_assert(r->ptr); + } + + r->ptr[r->used].pid = pid; + r->ptr[r->used].ctx = ctx; + ++r->used; +} + +static void cgi_pid_kill(plugin_data *p, pid_t pid) { + buffer_pid_t *r = &(p->cgi_pid); + for (size_t i = 0; i < r->used; ++i) { + if (r->ptr[i].pid == pid) { + r->ptr[i].ctx = NULL; + kill(pid, SIGTERM); + return; + } + } +} + +static void cgi_pid_del(plugin_data *p, size_t i) { + buffer_pid_t *r = &(p->cgi_pid); + + if (i != r->used - 1) { + r->ptr[i] = r->ptr[r->used - 1]; + } + r->used--; +} + + +static void cgi_connection_close_fdtocgi(server *srv, handler_ctx *hctx) { + /*(closes only hctx->fdtocgi)*/ + fdevent_event_del(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi); + /*fdevent_unregister(srv->ev, hctx->fdtocgi);*//*(handled below)*/ + fdevent_sched_close(srv->ev, hctx->fdtocgi, 0); + hctx->fdtocgi = -1; +} + +static void cgi_connection_close(server *srv, handler_ctx *hctx) { + plugin_data *p = hctx->plugin_data; + connection *con = hctx->remote_conn; + + /* the connection to the browser went away, but we still have a connection + * to the CGI script + * + * close cgi-connection + */ + + if (hctx->fd != -1) { + /* close connection to the cgi-script */ + fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); + /*fdevent_unregister(srv->ev, hctx->fd);*//*(handled below)*/ + fdevent_sched_close(srv->ev, hctx->fd, 0); + } + + if (hctx->fdtocgi != -1) { + cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/ + } + + if (hctx->pid > 0) { + cgi_pid_kill(p, hctx->pid); + } + + con->plugin_ctx[p->id] = NULL; + + cgi_handler_ctx_free(hctx); + + /* finish response (if not already con->file_started, con->file_finished) */ + if (con->mode == p->id) { + http_response_backend_done(srv, con); + } +} + +static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (hctx) cgi_connection_close(srv, hctx); + + return HANDLER_GO_ON; +} + + +static int cgi_write_request(server *srv, handler_ctx *hctx, int fd); + + +static handler_t cgi_handle_fdevent_send (server *srv, void *ctx, int revents) { + handler_ctx *hctx = ctx; + connection *con = hctx->remote_conn; + + /*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/ + joblist_append(srv, con); + + if (revents & FDEVENT_OUT) { + if (0 != cgi_write_request(srv, hctx, hctx->fdtocgi)) { + cgi_connection_close(srv, hctx); + return HANDLER_ERROR; + } + /* more request body to be sent to CGI */ + } + + if (revents & FDEVENT_HUP) { + /* skip sending remaining data to CGI */ + if (con->request.content_length) { + chunkqueue *cq = con->request_content_queue; + chunkqueue_mark_written(cq, chunkqueue_length(cq)); + if (cq->bytes_in != (off_t)con->request.content_length) { + con->keep_alive = 0; + } + } + + cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/ + } else if (revents & FDEVENT_ERR) { + /* kill all connections to the cgi process */ +#if 1 + log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR"); +#endif + cgi_connection_close(srv, hctx); + return HANDLER_ERROR; + } + + return HANDLER_FINISHED; +} + + +static handler_t cgi_response_headers(server *srv, connection *con, struct http_response_opts_t *opts) { + /* response headers just completed */ + handler_ctx *hctx = (handler_ctx *)opts->pdata; + + if (con->response.htags & HTTP_HEADER_UPGRADE) { + if (hctx->conf.upgrade && con->http_status == 101) { + /* 101 Switching Protocols; transition to transparent proxy */ + http_response_upgrade_read_body_unknown(srv, con); + } + else { + con->response.htags &= ~HTTP_HEADER_UPGRADE; + #if 0 + /* preserve prior questionable behavior; likely broken behavior + * anyway if backend thinks connection is being upgraded but client + * does not receive Connection: upgrade */ + http_header_response_unset(con, HTTP_HEADER_UPGRADE, + CONST_STR_LEN("Upgrade")); + #endif + } + } + + if (hctx->conf.upgrade && !(con->response.htags & HTTP_HEADER_UPGRADE)) { + chunkqueue *cq = con->request_content_queue; + hctx->conf.upgrade = 0; + if (cq->bytes_out == (off_t)con->request.content_length) { + cgi_connection_close_fdtocgi(srv, hctx); /*(closes hctx->fdtocgi)*/ + } + } + + return HANDLER_GO_ON; +} + + +static int cgi_recv_response(server *srv, handler_ctx *hctx) { + switch (http_response_read(srv, hctx->remote_conn, &hctx->opts, + hctx->response, hctx->fd, &hctx->fde_ndx)) { + default: + return HANDLER_GO_ON; + case HANDLER_ERROR: + http_response_backend_error(srv, hctx->remote_conn); + /* fall through */ + case HANDLER_FINISHED: + cgi_connection_close(srv, hctx); + return HANDLER_FINISHED; + case HANDLER_COMEBACK: + /* hctx->conf.local_redir */ + buffer_clear(hctx->response); + connection_response_reset(srv, hctx->remote_conn); /*(includes con->http_status = 0)*/ + plugins_call_connection_reset(srv, hctx->remote_conn); + /*cgi_connection_close(srv, hctx);*//*(already cleaned up and hctx is now invalid)*/ + return HANDLER_COMEBACK; + } +} + + +static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) { + handler_ctx *hctx = ctx; + connection *con = hctx->remote_conn; + + joblist_append(srv, con); + + if (revents & FDEVENT_IN) { + handler_t rc = cgi_recv_response(srv, hctx);/*(might invalidate hctx)*/ + if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ + } + + /* perhaps this issue is already handled */ + if (revents & (FDEVENT_HUP|FDEVENT_RDHUP)) { + if (con->file_started) { + /* drain any remaining data from kernel pipe buffers + * even if (con->conf.stream_response_body + * & FDEVENT_STREAM_RESPONSE_BUFMIN) + * since event loop will spin on fd FDEVENT_HUP event + * until unregistered. */ + handler_t rc; + const unsigned short flags = con->conf.stream_response_body; + con->conf.stream_response_body &= ~FDEVENT_STREAM_RESPONSE_BUFMIN; + con->conf.stream_response_body |= FDEVENT_STREAM_RESPONSE_POLLRDHUP; + do { + rc = cgi_recv_response(srv,hctx);/*(might invalidate hctx)*/ + } while (rc == HANDLER_GO_ON); /*(unless HANDLER_GO_ON)*/ + con->conf.stream_response_body = flags; + return rc; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */ + } else if (!buffer_string_is_empty(hctx->response)) { + /* unfinished header package which is a body in reality */ + con->file_started = 1; + if (0 != http_chunk_append_buffer(srv, con, hctx->response)) { + cgi_connection_close(srv, hctx); + return HANDLER_ERROR; + } + if (0 == con->http_status) con->http_status = 200; /* OK */ + } + cgi_connection_close(srv, hctx); + } else if (revents & FDEVENT_ERR) { + /* kill all connections to the cgi process */ + cgi_connection_close(srv, hctx); + return HANDLER_ERROR; + } + + return HANDLER_FINISHED; +} + + +static int cgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { + env_accum *env = venv; + char *dst; + + if (!key || !val) return -1; + + if (env->size - env->used < key_len + val_len + 2) { + if (0 == env->size) env->size = 4096; + do { env->size *= 2; } while (env->size - env->used < key_len + val_len + 2); + env->ptr = realloc(env->ptr, env->size); + force_assert(env->ptr); + } + + dst = env->ptr + env->used; + memcpy(dst, key, key_len); + dst[key_len] = '='; + memcpy(dst + key_len + 1, val, val_len); + dst[key_len + 1 + val_len] = '\0'; + + if (env->osize == env->oused) { + env->osize += 16; + env->offsets = realloc(env->offsets, env->osize * sizeof(*env->offsets)); + force_assert(env->offsets); + } + env->offsets[env->oused++] = env->used; + env->used += key_len + val_len + 2; + + return 0; +} + +/*(improved from network_write_mmap.c)*/ +static off_t mmap_align_offset(off_t start) { + static off_t pagemask = 0; + if (0 == pagemask) { + long pagesize = sysconf(_SC_PAGESIZE); + if (-1 == pagesize) pagesize = 4096; + pagemask = ~((off_t)pagesize - 1); /* pagesize always power-of-2 */ + } + return (start & pagemask); +} + +/* returns: 0: continue, -1: fatal error, -2: connection reset */ +/* similar to network_write_file_chunk_mmap, but doesn't use send on windows (because we're on pipes), + * also mmaps and sends complete chunk instead of only small parts - the files + * are supposed to be temp files with reasonable chunk sizes. + * + * Also always use mmap; the files are "trusted", as we created them. + */ +static ssize_t cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq) { + chunk* const c = cq->first; + off_t offset, toSend, file_end; + ssize_t r; + size_t mmap_offset, mmap_avail; + char *data = NULL; + + force_assert(NULL != c); + force_assert(FILE_CHUNK == c->type); + force_assert(c->offset >= 0 && c->offset <= c->file.length); + + offset = c->file.start + c->offset; + toSend = c->file.length - c->offset; + file_end = c->file.start + c->file.length; /* offset to file end in this chunk */ + + if (0 == toSend) { + chunkqueue_remove_finished_chunks(cq); + return 0; + } + + /*(simplified from chunk.c:chunkqueue_open_file_chunk())*/ + UNUSED(con); + if (-1 == c->file.fd) { + if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, O_RDONLY, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "open failed:", strerror(errno), c->mem); + return -1; + } + } + + /* (re)mmap the buffer if range is not covered completely */ + if (MAP_FAILED == c->file.mmap.start + || offset < c->file.mmap.offset + || file_end > (off_t)(c->file.mmap.offset + c->file.mmap.length)) { + + if (MAP_FAILED != c->file.mmap.start) { + munmap(c->file.mmap.start, c->file.mmap.length); + c->file.mmap.start = MAP_FAILED; + } + + c->file.mmap.offset = mmap_align_offset(offset); + c->file.mmap.length = file_end - c->file.mmap.offset; + + if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset))) { + if (toSend > 65536) toSend = 65536; + data = malloc(toSend); + force_assert(data); + if (-1 == lseek(c->file.fd, offset, SEEK_SET) + || 0 >= (toSend = read(c->file.fd, data, toSend))) { + if (-1 == toSend) { + log_error_write(srv, __FILE__, __LINE__, "ssbdo", "lseek/read failed:", + strerror(errno), c->mem, c->file.fd, offset); + } else { /*(0 == toSend)*/ + log_error_write(srv, __FILE__, __LINE__, "sbdo", "unexpected EOF (input truncated?):", + c->mem, c->file.fd, offset); + } + free(data); + return -1; + } + } + } + + if (MAP_FAILED != c->file.mmap.start) { + force_assert(offset >= c->file.mmap.offset); + mmap_offset = offset - c->file.mmap.offset; + force_assert(c->file.mmap.length > mmap_offset); + mmap_avail = c->file.mmap.length - mmap_offset; + force_assert(toSend <= (off_t) mmap_avail); + + data = c->file.mmap.start + mmap_offset; + } + + r = write(fd, data, toSend); + + if (MAP_FAILED == c->file.mmap.start) free(data); + + if (r < 0) { + switch (errno) { + case EAGAIN: + case EINTR: + return 0; + case EPIPE: + case ECONNRESET: + return -2; + default: + log_error_write(srv, __FILE__, __LINE__, "ssd", + "write failed:", strerror(errno), fd); + return -1; + } + } + + if (r >= 0) { + chunkqueue_mark_written(cq, r); + } + + return r; +} + +static int cgi_write_request(server *srv, handler_ctx *hctx, int fd) { + connection *con = hctx->remote_conn; + chunkqueue *cq = con->request_content_queue; + chunk *c; + + /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms. + * solution: if this is still a problem on windows, then substitute + * socketpair() for pipe() and closesocket() for close() on windows. + */ + + for (c = cq->first; c; c = cq->first) { + ssize_t r = -1; + + switch(c->type) { + case FILE_CHUNK: + r = cgi_write_file_chunk_mmap(srv, con, fd, cq); + break; + + case MEM_CHUNK: + if ((r = write(fd, c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) { + switch(errno) { + case EAGAIN: + case EINTR: + /* ignore and try again */ + r = 0; + break; + case EPIPE: + case ECONNRESET: + /* connection closed */ + r = -2; + break; + default: + /* fatal error */ + log_error_write(srv, __FILE__, __LINE__, "ss", "write failed due to: ", strerror(errno)); + r = -1; + break; + } + } else if (r > 0) { + chunkqueue_mark_written(cq, r); + } + break; + } + + if (0 == r) break; /*(might block)*/ + + switch (r) { + case -1: + /* fatal error */ + return -1; + case -2: + /* connection reset */ + log_error_write(srv, __FILE__, __LINE__, "s", "failed to send post data to cgi, connection closed by CGI"); + /* skip all remaining data */ + chunkqueue_mark_written(cq, chunkqueue_length(cq)); + break; + default: + break; + } + } + + if (cq->bytes_out == (off_t)con->request.content_length && !hctx->conf.upgrade) { + /* sent all request body input */ + /* close connection to the cgi-script */ + if (-1 == hctx->fdtocgi) { /*(received request body sent in initial send to pipe buffer)*/ + --srv->cur_fds; + if (close(fd)) { + log_error_write(srv, __FILE__, __LINE__, "sds", "cgi stdin close failed ", fd, strerror(errno)); + } + } else { + cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/ + } + } else { + off_t cqlen = cq->bytes_in - cq->bytes_out; + if (cq->bytes_in != con->request.content_length && cqlen < 65536 - 16384) { + /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/ + if (!(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_POLLIN)) { + con->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; + con->is_readable = 1; /* trigger optimistic read from client */ + } + } + if (-1 == hctx->fdtocgi) { /*(not registered yet)*/ + hctx->fdtocgi = fd; + hctx->fde_ndx_tocgi = -1; + fdevent_register(srv->ev, hctx->fdtocgi, cgi_handle_fdevent_send, hctx); + } + if (0 == cqlen) { /*(chunkqueue_is_empty(cq))*/ + if ((fdevent_event_get_interest(srv->ev, hctx->fdtocgi) & FDEVENT_OUT)) { + fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, 0); + } + } else { + /* more request body remains to be sent to CGI so register for fdevents */ + fdevent_event_set(srv->ev, &(hctx->fde_ndx_tocgi), hctx->fdtocgi, FDEVENT_OUT); + } + } + + return 0; +} + +static struct stat * cgi_stat(server *srv, connection *con, buffer *path, struct stat *st) { + /* CGI might be executable even if it is not readable + * (stat_cache_get_entry() currently checks file is readable)*/ + stat_cache_entry *sce; + return (HANDLER_ERROR != stat_cache_get_entry(srv, con, path, &sce)) + ? &sce->st + : (0 == stat(path->ptr, st)) ? st : NULL; +} + +static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) { + char *args[3]; + int to_cgi_fds[2]; + int from_cgi_fds[2]; + int dfd = -1; + UNUSED(p); + + if (!buffer_string_is_empty(cgi_handler)) { + /* stat the exec file */ + struct stat st; + if (NULL == cgi_stat(srv, con, cgi_handler, &st)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "stat for cgi-handler", cgi_handler, + "failed:", strerror(errno)); + return -1; + } + } + + if (pipe_cloexec(to_cgi_fds)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); + return -1; + } + if (pipe_cloexec(from_cgi_fds)) { + close(to_cgi_fds[0]); + close(to_cgi_fds[1]); + log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); + return -1; + } + fdevent_setfd_cloexec(to_cgi_fds[1]); + fdevent_setfd_cloexec(from_cgi_fds[0]); + + { + size_t i = 0; + http_cgi_opts opts = { 0, 0, NULL, NULL }; + env_accum *env = &p->env; + env->used = 0; + env->oused = 0; + + /* create environment */ + + http_cgi_headers(srv, con, &opts, cgi_env_add, env); + + /* for valgrind */ + if (p->env.ld_preload) { + cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), CONST_BUF_LEN(p->env.ld_preload)); + } + if (p->env.ld_library_path) { + cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), CONST_BUF_LEN(p->env.ld_library_path)); + } + #ifdef __CYGWIN__ + /* CYGWIN needs SYSTEMROOT */ + if (p->env.systemroot) { + cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), CONST_BUF_LEN(p->env.systemroot)); + } + #endif + + if (env->esize <= env->oused) { + env->esize = (env->oused + 1 + 0xf) & ~(0xfuL); + env->eptr = realloc(env->eptr, env->esize * sizeof(*env->eptr)); + force_assert(env->eptr); + } + for (i = 0; i < env->oused; ++i) { + env->eptr[i] = env->ptr + env->offsets[i]; + } + env->eptr[env->oused] = NULL; + + /* set up args */ + i = 0; + + if (!buffer_string_is_empty(cgi_handler)) { + args[i++] = cgi_handler->ptr; + } + args[i++] = con->physical.path->ptr; + args[i ] = NULL; + } + + dfd = fdevent_open_dirname(con->physical.path->ptr); + if (-1 == dfd) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "open dirname failed:", strerror(errno), con->physical.path); + } + + hctx->pid = (dfd >= 0) ? fdevent_fork_execve(args[0], args, p->env.eptr, to_cgi_fds[0], from_cgi_fds[1], -1, dfd) : -1; + + if (-1 == hctx->pid) { + /* log error with errno prior to calling close() (might change errno) */ + log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); + if (-1 != dfd) close(dfd); + close(from_cgi_fds[0]); + close(from_cgi_fds[1]); + close(to_cgi_fds[0]); + close(to_cgi_fds[1]); + return -1; + } else { + if (-1 != dfd) close(dfd); + close(from_cgi_fds[1]); + close(to_cgi_fds[0]); + + hctx->fd = from_cgi_fds[0]; + hctx->fde_ndx = -1; + + ++srv->cur_fds; + + cgi_pid_add(p, hctx->pid, hctx); + + if (0 == con->request.content_length) { + close(to_cgi_fds[1]); + } else { + /* there is content to send */ + if (-1 == fdevent_fcntl_set_nb(srv->ev, to_cgi_fds[1])) { + log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); + close(to_cgi_fds[1]); + cgi_connection_close(srv, hctx); + return -1; + } + + if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) { + close(to_cgi_fds[1]); + cgi_connection_close(srv, hctx); + return -1; + } + + ++srv->cur_fds; + } + + fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx); + if (-1 == fdevent_fcntl_set_nb(srv->ev, hctx->fd)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); + cgi_connection_close(srv, hctx); + return -1; + } + fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN | FDEVENT_RDHUP); + + return 0; + } +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(cgi); + PATCH(execute_x_only); + PATCH(local_redir); + PATCH(upgrade); + PATCH(xsendfile_allow); + PATCH(xsendfile_docroot); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.assign"))) { + PATCH(cgi); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) { + PATCH(execute_x_only); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.local-redir"))) { + PATCH(local_redir); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.upgrade"))) { + PATCH(upgrade); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.x-sendfile"))) { + PATCH(xsendfile_allow); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.x-sendfile-docroot"))) { + PATCH(xsendfile_docroot); + } + } + } + + return 0; +} +#undef PATCH + +URIHANDLER_FUNC(cgi_is_handled) { + plugin_data *p = p_d; + struct stat stbuf; + struct stat *st; + data_string *ds; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; + + mod_cgi_patch_connection(srv, con, p); + + ds = (data_string *)array_match_key_suffix(p->conf.cgi, con->physical.path); + if (NULL == ds) return HANDLER_GO_ON; + + st = cgi_stat(srv, con, con->physical.path, &stbuf); + if (NULL == st) return HANDLER_GO_ON; + + if (!S_ISREG(st->st_mode)) return HANDLER_GO_ON; + if (p->conf.execute_x_only == 1 && (st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; + + { + handler_ctx *hctx = cgi_handler_ctx_init(); + hctx->remote_conn = con; + hctx->plugin_data = p; + hctx->cgi_handler = ds->value; + memcpy(&hctx->conf, &p->conf, sizeof(plugin_config)); + hctx->conf.upgrade = + hctx->conf.upgrade + && con->request.http_version == HTTP_VERSION_1_1 + && NULL != http_header_request_get(con, HTTP_HEADER_UPGRADE, CONST_STR_LEN("Upgrade")); + hctx->opts.fdfmt = S_IFIFO; + hctx->opts.backend = BACKEND_CGI; + hctx->opts.authorizer = 0; + hctx->opts.local_redir = hctx->conf.local_redir; + hctx->opts.xsendfile_allow = hctx->conf.xsendfile_allow; + hctx->opts.xsendfile_docroot = hctx->conf.xsendfile_docroot; + hctx->opts.pdata = hctx; + hctx->opts.headers = cgi_response_headers; + con->plugin_ctx[p->id] = hctx; + con->mode = p->id; + } + + return HANDLER_GO_ON; +} + +/* + * - HANDLER_GO_ON : not our job + * - HANDLER_FINISHED: got response + * - HANDLER_WAIT_FOR_EVENT: waiting for response + */ +SUBREQUEST_FUNC(mod_cgi_handle_subrequest) { + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + chunkqueue *cq = con->request_content_queue; + + if (con->mode != p->id) return HANDLER_GO_ON; + if (NULL == hctx) return HANDLER_GO_ON; + + if ((con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) + && con->file_started) { + if (chunkqueue_length(con->write_queue) > 65536 - 4096) { + fdevent_event_clr(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + } else if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN)) { + /* optimistic read from backend */ + handler_t rc = cgi_recv_response(srv, hctx); /*(might invalidate hctx)*/ + if (rc != HANDLER_GO_ON) return rc; /*(unless HANDLER_GO_ON)*/ + fdevent_event_add(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); + } + } + + if (cq->bytes_in != (off_t)con->request.content_length) { + /*(64k - 4k to attempt to avoid temporary files + * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/ + if (cq->bytes_in - cq->bytes_out > 65536 - 4096 + && (con->conf.stream_request_body & FDEVENT_STREAM_REQUEST_BUFMIN)){ + con->conf.stream_request_body &= ~FDEVENT_STREAM_REQUEST_POLLIN; + if (-1 != hctx->fd) return HANDLER_WAIT_FOR_EVENT; + } else { + handler_t r = connection_handle_read_post_state(srv, con); + if (!chunkqueue_is_empty(cq)) { + if (fdevent_event_get_interest(srv->ev, hctx->fdtocgi) & FDEVENT_OUT) { + return (r == HANDLER_GO_ON) ? HANDLER_WAIT_FOR_EVENT : r; + } + } + if (r != HANDLER_GO_ON) return r; + + /* CGI environment requires that Content-Length be set. + * Send 411 Length Required if Content-Length missing. + * (occurs here if client sends Transfer-Encoding: chunked + * and module is flagged to stream request body to backend) */ + if (-1 == con->request.content_length) { + return connection_handle_read_post_error(srv, con, 411); + } + } + } + + if (-1 == hctx->fd) { + if (cgi_create_env(srv, con, p, hctx, hctx->cgi_handler)) { + con->http_status = 500; + con->mode = DIRECT; + + return HANDLER_FINISHED; + } + } else if (!chunkqueue_is_empty(con->request_content_queue)) { + if (0 != cgi_write_request(srv, hctx, hctx->fdtocgi)) { + cgi_connection_close(srv, hctx); + return HANDLER_ERROR; + } + } + + /* if not done, wait for CGI to close stdout, so we read EOF on pipe */ + return HANDLER_WAIT_FOR_EVENT; +} + + +static handler_t cgi_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) { + plugin_data *p = (plugin_data *)p_d; + for (size_t i = 0; i < p->cgi_pid.used; ++i) { + handler_ctx *hctx; + if (pid != p->cgi_pid.ptr[i].pid) continue; + + hctx = (handler_ctx *)p->cgi_pid.ptr[i].ctx; + if (hctx) hctx->pid = -1; + cgi_pid_del(p, i); + + if (WIFEXITED(status)) { + /* (skip logging (non-zero) CGI exit; might be very noisy) */ + } + else if (WIFSIGNALED(status)) { + /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/ + if (WTERMSIG(status) != SIGTERM || NULL != hctx) { + log_error_write(srv, __FILE__, __LINE__, "sdsd", "CGI pid", pid, + "died with signal", WTERMSIG(status)); + } + } + else { + log_error_write(srv, __FILE__, __LINE__, "sds", + "CGI pid", pid, "ended unexpectedly"); + } + + return HANDLER_FINISHED; + } + + return HANDLER_GO_ON; +} + + +int mod_cgi_plugin_init(plugin *p); +int mod_cgi_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("cgi"); + + p->connection_reset = cgi_connection_close_callback; + p->handle_subrequest_start = cgi_is_handled; + p->handle_subrequest = mod_cgi_handle_subrequest; + p->handle_waitpid = cgi_waitpid_cb; + p->init = mod_cgi_init; + p->cleanup = mod_cgi_free; + p->set_defaults = mod_fastcgi_set_defaults; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_cml.c b/data/lighttpd/lighttpd-1.4.53/src/mod_cml.c new file mode 100644 index 000000000..c82a4c27b --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_cml.c @@ -0,0 +1,340 @@ +#include "first.h" + +#include "mod_cml.h" + +#include "base.h" +#include "buffer.h" +#include "log.h" +#include "plugin.h" + +#include <sys/stat.h> +#include <time.h> + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +/* init the plugin data */ +INIT_FUNC(mod_cml_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->basedir = buffer_init(); + p->baseurl = buffer_init(); + p->trigger_handler = buffer_init(); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_cml_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->ext); + + buffer_free(s->mc_namespace); + buffer_free(s->power_magnet); + array_free(s->mc_hosts); + +#if defined(USE_MEMCACHED) + if (s->memc) memcached_free(s->memc); +#endif + + free(s); + } + free(p->config_storage); + } + + buffer_free(p->trigger_handler); + buffer_free(p->basedir); + buffer_free(p->baseurl); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_cml_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "cml.extension", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "cml.memcache-hosts", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "cml.memcache-namespace", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "cml.power-magnet", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->ext = buffer_init(); + s->mc_hosts = array_init(); + s->mc_namespace = buffer_init(); + s->power_magnet = buffer_init(); +#if defined(USE_MEMCACHED) + s->memc = NULL; +#endif + + cv[0].destination = s->ext; + cv[1].destination = s->mc_hosts; + cv[2].destination = s->mc_namespace; + cv[3].destination = s->power_magnet; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_vlist(s->mc_hosts)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for cml.memcache-hosts; expected list of \"host\""); + return HANDLER_ERROR; + } + + if (s->mc_hosts->used) { +#if defined(USE_MEMCACHED) + buffer *option_string = buffer_init(); + size_t k; + + { + data_string *ds = (data_string *)s->mc_hosts->data[0]; + + buffer_append_string_len(option_string, CONST_STR_LEN("--SERVER=")); + buffer_append_string_buffer(option_string, ds->value); + } + + for (k = 1; k < s->mc_hosts->used; k++) { + data_string *ds = (data_string *)s->mc_hosts->data[k]; + + buffer_append_string_len(option_string, CONST_STR_LEN(" --SERVER=")); + buffer_append_string_buffer(option_string, ds->value); + } + + s->memc = memcached(CONST_BUF_LEN(option_string)); + + if (NULL == s->memc) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "configuring memcached failed for option string:", + option_string); + } + buffer_free(option_string); + + if (NULL == s->memc) return HANDLER_ERROR; +#else + log_error_write(srv, __FILE__, __LINE__, "s", + "memcache support is not compiled in but cml.memcache-hosts is set, aborting"); + return HANDLER_ERROR; +#endif + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_cml_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(ext); +#if defined(USE_MEMCACHED) + PATCH(memc); +#endif + PATCH(mc_namespace); + PATCH(power_magnet); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.extension"))) { + PATCH(ext); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.memcache-hosts"))) { +#if defined(USE_MEMCACHED) + PATCH(memc); +#endif + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.memcache-namespace"))) { + PATCH(mc_namespace); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.power-magnet"))) { + PATCH(power_magnet); + } + } + } + + return 0; +} +#undef PATCH + +static int cache_call_lua(server *srv, connection *con, plugin_data *p, buffer *cml_file) { + buffer *b; + char *c; + + /* cleanup basedir */ + b = p->baseurl; + buffer_copy_buffer(b, con->uri.path); + for (c = b->ptr + buffer_string_length(b); c > b->ptr && *c != '/'; c--); + + if (*c == '/') { + buffer_string_set_length(b, c - b->ptr + 1); + } + + b = p->basedir; + buffer_copy_buffer(b, con->physical.path); + for (c = b->ptr + buffer_string_length(b); c > b->ptr && *c != '/'; c--); + + if (*c == '/') { + buffer_string_set_length(b, c - b->ptr + 1); + } + + + /* prepare variables + * - cookie-based + * - get-param-based + */ + return cache_parse_lua(srv, con, p, cml_file); +} + +URIHANDLER_FUNC(mod_cml_power_magnet) { + plugin_data *p = p_d; + + mod_cml_patch_connection(srv, con, p); + + buffer_clear(p->basedir); + buffer_clear(p->baseurl); + buffer_clear(p->trigger_handler); + + if (buffer_string_is_empty(p->conf.power_magnet)) return HANDLER_GO_ON; + + /* + * power-magnet: + * cml.power-magnet = server.docroot + "/rewrite.cml" + * + * is called on EACH request, take the original REQUEST_URI and modifies the + * request header as neccesary. + * + * First use: + * if file_exists("/maintainance.html") { + * output_include = ( "/maintainance.html" ) + * return CACHE_HIT + * } + * + * as we only want to rewrite HTML like requests we should cover it in a conditional + * + * */ + + switch(cache_call_lua(srv, con, p, p->conf.power_magnet)) { + case -1: + /* error */ + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "cache-error"); + } + con->http_status = 500; + return HANDLER_COMEBACK; + case 0: + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "cache-hit"); + } + /* cache-hit */ + buffer_reset(con->physical.path); + return HANDLER_FINISHED; + case 1: + /* cache miss */ + return HANDLER_GO_ON; + default: + con->http_status = 500; + return HANDLER_COMEBACK; + } +} + +URIHANDLER_FUNC(mod_cml_is_handled) { + plugin_data *p = p_d; + + if (buffer_string_is_empty(con->physical.path)) return HANDLER_ERROR; + + mod_cml_patch_connection(srv, con, p); + + buffer_clear(p->basedir); + buffer_clear(p->baseurl); + buffer_clear(p->trigger_handler); + + if (buffer_string_is_empty(p->conf.ext)) return HANDLER_GO_ON; + + if (!buffer_is_equal_right_len(con->physical.path, p->conf.ext, buffer_string_length(p->conf.ext))) { + return HANDLER_GO_ON; + } + + switch(cache_call_lua(srv, con, p, con->physical.path)) { + case -1: + /* error */ + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "cache-error"); + } + con->http_status = 500; + return HANDLER_COMEBACK; + case 0: + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "cache-hit"); + } + /* cache-hit */ + buffer_reset(con->physical.path); + return HANDLER_FINISHED; + case 1: + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "cache-miss"); + } + /* cache miss */ + return HANDLER_COMEBACK; + default: + con->http_status = 500; + return HANDLER_COMEBACK; + } +} + +int mod_cml_plugin_init(plugin *p); +int mod_cml_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("cache"); + + p->init = mod_cml_init; + p->cleanup = mod_cml_free; + p->set_defaults = mod_cml_set_defaults; + + p->handle_subrequest_start = mod_cml_is_handled; + p->handle_physical = mod_cml_power_magnet; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_cml.h b/data/lighttpd/lighttpd-1.4.53/src/mod_cml.h new file mode 100644 index 000000000..d1d0c52a8 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_cml.h @@ -0,0 +1,42 @@ +#ifndef _MOD_CACHE_H_ +#define _MOD_CACHE_H_ +#include "first.h" + +#include "base_decls.h" +#include "buffer.h" + +#include "plugin.h" + +#if defined(USE_MEMCACHED) +#include <libmemcached/memcached.h> +#endif + +#define plugin_data mod_cache_plugin_data + +typedef struct { + buffer *ext; + + array *mc_hosts; + buffer *mc_namespace; +#if defined(USE_MEMCACHED) + memcached_st *memc; +#endif + buffer *power_magnet; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + buffer *basedir; + buffer *baseurl; + + buffer *trigger_handler; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_cml_funcs.c b/data/lighttpd/lighttpd-1.4.53/src/mod_cml_funcs.c new file mode 100644 index 000000000..cc674a2f0 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_cml_funcs.c @@ -0,0 +1,269 @@ +#include "first.h" + +#include <sys/stat.h> +#include <time.h> + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> + +#include <lauxlib.h> + +#include "mod_cml_funcs.h" +#include "mod_cml.h" + +#include "buffer.h" +#include "log.h" +#include "plugin.h" + +#include "md5.h" + +#define HASHLEN 16 +typedef unsigned char HASH[HASHLEN]; +#define HASHHEXLEN 32 +typedef char HASHHEX[HASHHEXLEN+1]; + +int f_crypto_md5(lua_State *L) { + li_MD5_CTX Md5Ctx; + HASH HA1; + char hex[33]; + int n = lua_gettop(L); + size_t s_len; + const char *s; + + if (n != 1) { + lua_pushstring(L, "md5: expected one argument"); + lua_error(L); + } + + if (!lua_isstring(L, 1)) { + lua_pushstring(L, "md5: argument has to be a string"); + lua_error(L); + } + + s = lua_tolstring(L, 1, &s_len); + + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *) s, (unsigned int) s_len); + li_MD5_Final(HA1, &Md5Ctx); + + li_tohex(hex, sizeof(hex), (const char*) HA1, 16); + + lua_pushstring(L, hex); + + return 1; +} + + +int f_file_mtime(lua_State *L) { + struct stat st; + int n = lua_gettop(L); + + if (n != 1) { + lua_pushstring(L, "file_mtime: expected one argument"); + lua_error(L); + } + + if (!lua_isstring(L, 1)) { + lua_pushstring(L, "file_mtime: argument has to be a string"); + lua_error(L); + } + + if (-1 == stat(lua_tostring(L, 1), &st)) { + lua_pushnil(L); + return 1; + } + + lua_pushinteger(L, st.st_mtime); + + return 1; +} + +static int f_dir_files_iter(lua_State *L) { + DIR *d; + struct dirent *de; + + d = lua_touserdata(L, lua_upvalueindex(1)); + + if (NULL == (de = readdir(d))) { + /* EOF */ + closedir(d); + + return 0; + } else { + lua_pushstring(L, de->d_name); + return 1; + } +} + +int f_dir_files(lua_State *L) { + DIR *d; + int n = lua_gettop(L); + + if (n != 1) { + lua_pushstring(L, "dir_files: expected one argument"); + lua_error(L); + } + + if (!lua_isstring(L, 1)) { + lua_pushstring(L, "dir_files: argument has to be a string"); + lua_error(L); + } + + /* check if there is a valid DIR handle on the stack */ + if (NULL == (d = opendir(lua_tostring(L, 1)))) { + lua_pushnil(L); + return 1; + } + + /* push d into userdata */ + lua_pushlightuserdata(L, d); + lua_pushcclosure(L, f_dir_files_iter, 1); + + return 1; +} + +int f_file_isreg(lua_State *L) { + struct stat st; + int n = lua_gettop(L); + + if (n != 1) { + lua_pushstring(L, "file_isreg: expected one argument"); + lua_error(L); + } + + if (!lua_isstring(L, 1)) { + lua_pushstring(L, "file_isreg: argument has to be a string"); + lua_error(L); + } + + if (-1 == stat(lua_tostring(L, 1), &st)) { + lua_pushnil(L); + return 1; + } + + lua_pushinteger(L, S_ISREG(st.st_mode)); + + return 1; +} + +int f_file_isdir(lua_State *L) { + struct stat st; + int n = lua_gettop(L); + + if (n != 1) { + lua_pushstring(L, "file_isreg: expected one argument"); + lua_error(L); + } + + if (!lua_isstring(L, 1)) { + lua_pushstring(L, "file_isreg: argument has to be a string"); + lua_error(L); + } + + if (-1 == stat(lua_tostring(L, 1), &st)) { + lua_pushnil(L); + return 1; + } + + lua_pushinteger(L, S_ISDIR(st.st_mode)); + + return 1; +} + + + +#ifdef USE_MEMCACHED +int f_memcache_exists(lua_State *L) { + size_t key_len; + const char *key; + memcached_st *memc; + + if (!lua_islightuserdata(L, lua_upvalueindex(1))) { + lua_pushstring(L, "where is my userdata ?"); + lua_error(L); + } + + memc = lua_touserdata(L, lua_upvalueindex(1)); + + if (1 != lua_gettop(L)) { + lua_pushstring(L, "expected one argument"); + lua_error(L); + } + + key = luaL_checklstring(L, 1, &key_len); + lua_pushboolean(L, (MEMCACHED_SUCCESS == memcached_exist(memc, key, key_len))); + return 1; +} + +int f_memcache_get_string(lua_State *L) { + size_t key_len, value_len; + char *value; + const char *key; + memcached_st *memc; + + if (!lua_islightuserdata(L, lua_upvalueindex(1))) { + lua_pushstring(L, "where is my userdata ?"); + lua_error(L); + } + + memc = lua_touserdata(L, lua_upvalueindex(1)); + + if (1 != lua_gettop(L)) { + lua_pushstring(L, "expected one argument"); + lua_error(L); + } + + key = luaL_checklstring(L, 1, &key_len); + if (NULL == (value = memcached_get(memc, key, key_len, &value_len, NULL, NULL))) { + lua_pushnil(L); + return 1; + } + + lua_pushlstring(L, value, value_len); + + free(value); + + return 1; +} + +int f_memcache_get_long(lua_State *L) { + size_t key_len, value_len; + char *value; + const char *key; + memcached_st *memc; + char *endptr; + long v; + + if (!lua_islightuserdata(L, lua_upvalueindex(1))) { + lua_pushstring(L, "where is my userdata ?"); + lua_error(L); + } + + memc = lua_touserdata(L, lua_upvalueindex(1)); + + if (1 != lua_gettop(L)) { + lua_pushstring(L, "expected one argument"); + lua_error(L); + } + + key = luaL_checklstring(L, 1, &key_len); + if (NULL == (value = memcached_get(memc, key, key_len, &value_len, NULL, NULL))) { + lua_pushnil(L); + return 1; + } + + errno = 0; + v = strtol(value, &endptr, 10); + if (0 == errno && *endptr == '\0') { + lua_pushinteger(L, v); + } else { + lua_pushnil(L); + } + + free(value); + + return 1; +} +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_cml_funcs.h b/data/lighttpd/lighttpd-1.4.53/src/mod_cml_funcs.h new file mode 100644 index 000000000..49243350b --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_cml_funcs.h @@ -0,0 +1,16 @@ +#ifndef _MOD_CML_FUNCS_H_ +#define _MOD_CML_FUNCS_H_ +#include "first.h" + +#include <lua.h> + +int f_crypto_md5(lua_State *L); +int f_file_mtime(lua_State *L); +int f_file_isreg(lua_State *L); +int f_file_isdir(lua_State *L); +int f_dir_files(lua_State *L); + +int f_memcache_exists(lua_State *L); +int f_memcache_get_string(lua_State *L); +int f_memcache_get_long(lua_State *L); +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_cml_lua.c b/data/lighttpd/lighttpd-1.4.53/src/mod_cml_lua.c new file mode 100644 index 000000000..41b554363 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_cml_lua.c @@ -0,0 +1,325 @@ +#include "first.h" + +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#include <errno.h> +#include <time.h> +#include <string.h> + +#include "mod_cml_funcs.h" +#include "mod_cml.h" + +#include "base.h" +#include "chunk.h" +#include "log.h" +#include "http_header.h" +#include "response.h" +#include "stat_cache.h" + +#define HASHLEN 16 +typedef unsigned char HASH[HASHLEN]; +#define HASHHEXLEN 32 +typedef char HASHHEX[HASHHEXLEN+1]; + +static int lua_to_c_get_string(lua_State *L, const char *varname, buffer *b) { + int curelem = lua_gettop(L); + int result; + + lua_getglobal(L, varname); + + if (lua_isstring(L, curelem)) { + buffer_copy_string(b, lua_tostring(L, curelem)); + result = 0; + } else { + result = -1; + } + + lua_pop(L, 1); + force_assert(curelem == lua_gettop(L)); + return result; +} + +static int lua_to_c_is_table(lua_State *L, const char *varname) { + int curelem = lua_gettop(L); + int result; + + lua_getglobal(L, varname); + + result = lua_istable(L, curelem) ? 1 : 0; + + lua_pop(L, 1); + force_assert(curelem == lua_gettop(L)); + return result; +} + +static int c_to_lua_push(lua_State *L, int tbl, const char *key, size_t key_len, const char *val, size_t val_len) { + lua_pushlstring(L, key, key_len); + lua_pushlstring(L, val, val_len); + lua_settable(L, tbl); + + return 0; +} + +static int cache_export_get_params(lua_State *L, int tbl, buffer *qrystr) { + size_t is_key = 1; + size_t i, len, klen = 0; + char *key = NULL, *val = NULL; + + if (buffer_string_is_empty(qrystr)) return 0; + key = qrystr->ptr; + + /* we need the \0 */ + len = buffer_string_length(qrystr); + for (i = 0; i <= len; i++) { + switch(qrystr->ptr[i]) { + case '=': + if (is_key) { + val = qrystr->ptr + i + 1; + klen = (size_t)(val - key - 1); + is_key = 0; + } + + break; + case '&': + case '\0': /* fin symbol */ + if (!is_key) { + /* we need at least a = since the last & */ + c_to_lua_push(L, tbl, + key, klen, + val, (size_t)(qrystr->ptr + i - val)); + } + + key = qrystr->ptr + i + 1; + val = NULL; + is_key = 1; + break; + } + } + + return 0; +} + +int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { + lua_State *L; + int ret = -1; + buffer *b; + + b = buffer_init(); + /* push the lua file to the interpreter and see what happends */ + L = luaL_newstate(); + luaL_openlibs(L); + + /* register functions */ + lua_register(L, "md5", f_crypto_md5); + lua_register(L, "file_mtime", f_file_mtime); + lua_register(L, "file_isreg", f_file_isreg); + lua_register(L, "file_isdir", f_file_isreg); + lua_register(L, "dir_files", f_dir_files); + +#ifdef USE_MEMCACHED + lua_pushlightuserdata(L, p->conf.memc); + lua_pushcclosure(L, f_memcache_get_long, 1); + lua_setglobal(L, "memcache_get_long"); + + lua_pushlightuserdata(L, p->conf.memc); + lua_pushcclosure(L, f_memcache_get_string, 1); + lua_setglobal(L, "memcache_get_string"); + + lua_pushlightuserdata(L, p->conf.memc); + lua_pushcclosure(L, f_memcache_exists, 1); + lua_setglobal(L, "memcache_exists"); +#endif + + /* register CGI environment */ + lua_newtable(L); + { + int header_tbl = lua_gettop(L); + + c_to_lua_push(L, header_tbl, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); + c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); + c_to_lua_push(L, header_tbl, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); + c_to_lua_push(L, header_tbl, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir)); + if (!buffer_string_is_empty(con->request.pathinfo)) { + c_to_lua_push(L, header_tbl, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); + } + + c_to_lua_push(L, header_tbl, CONST_STR_LEN("CWD"), CONST_BUF_LEN(p->basedir)); + c_to_lua_push(L, header_tbl, CONST_STR_LEN("BASEURL"), CONST_BUF_LEN(p->baseurl)); + } + lua_setglobal(L, "request"); + + /* register GET parameter */ + lua_newtable(L); + cache_export_get_params(L, lua_gettop(L), con->uri.query); + lua_setglobal(L, "get"); + + /* 2 default constants */ + lua_pushinteger(L, 0); + lua_setglobal(L, "CACHE_HIT"); + + lua_pushinteger(L, 1); + lua_setglobal(L, "CACHE_MISS"); + + /* load lua program */ + ret = luaL_loadfile(L, fn->ptr); + if (0 != ret) { + log_error_write(srv, __FILE__, __LINE__, "sbsS", + "failed loading cml_lua script", + fn, + ":", + lua_tostring(L, -1)); + goto error; + } + + if (lua_pcall(L, 0, 1, 0)) { + log_error_write(srv, __FILE__, __LINE__, "sbsS", + "failed running cml_lua script", + fn, + ":", + lua_tostring(L, -1)); + goto error; + } + + /* get return value */ + ret = (int)lua_tointeger(L, -1); + lua_pop(L, 1); + + /* fetch the data from lua */ + lua_to_c_get_string(L, "trigger_handler", p->trigger_handler); + + if (0 == lua_to_c_get_string(L, "output_contenttype", b)) { + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(b)); + } + + if (ret == 0) { + /* up to now it is a cache-hit, check if all files exist */ + + int curelem; + time_t mtime = 0; + + if (!lua_to_c_is_table(L, "output_include")) { + log_error_write(srv, __FILE__, __LINE__, "s", + "output_include is missing or not a table"); + ret = -1; + + goto error; + } + + lua_getglobal(L, "output_include"); + curelem = lua_gettop(L); + + /* HOW-TO build a etag ? + * as we don't just have one file we have to take the stat() + * from all base files, merge them and build the etag from + * it later. + * + * The mtime of the content is the mtime of the freshest base file + * + * */ + + lua_pushnil(L); /* first key */ + while (lua_next(L, curelem) != 0) { + /* key' is at index -2 and value' at index -1 */ + + if (lua_isstring(L, -1)) { + const char *s = lua_tostring(L, -1); + struct stat st; + int fd; + + /* the file is relative, make it absolute */ + if (s[0] != '/') { + buffer_copy_buffer(b, p->basedir); + buffer_append_string(b, lua_tostring(L, -1)); + } else { + buffer_copy_string(b, lua_tostring(L, -1)); + } + + fd = stat_cache_open_rdonly_fstat(srv, con, b, &st); + if (fd < 0) { + /* stat failed */ + + switch(errno) { + case ENOENT: + /* a file is missing, call the handler to generate it */ + if (!buffer_string_is_empty(p->trigger_handler)) { + ret = 1; /* cache-miss */ + + log_error_write(srv, __FILE__, __LINE__, "s", + "a file is missing, calling handler"); + + break; + } else { + /* handler not set -> 500 */ + ret = -1; + + log_error_write(srv, __FILE__, __LINE__, "s", + "a file missing and no handler set"); + + break; + } + break; + default: + break; + } + } else { + chunkqueue_append_file_fd(con->write_queue, b, fd, 0, st.st_size); + if (st.st_mtime > mtime) mtime = st.st_mtime; + } + } else { + /* not a string */ + ret = -1; + log_error_write(srv, __FILE__, __LINE__, "s", + "not a string"); + break; + } + + lua_pop(L, 1); /* removes value'; keeps key' for next iteration */ + } + + lua_settop(L, curelem - 1); + + if (ret == 0) { + buffer *vb = http_header_response_get(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified")); + if (NULL == vb) { /* no Last-Modified specified */ + char timebuf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")]; + if (0 == mtime) mtime = time(NULL); /* default last-modified to now */ + strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&mtime)); + http_header_response_set(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), timebuf, sizeof(timebuf) - 1); + vb = http_header_response_get(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified")); + force_assert(NULL != vb); + } + + con->file_finished = 1; + + if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, vb)) { + /* ok, the client already has our content, + * no need to send it again */ + + chunkqueue_reset(con->write_queue); + ret = 0; /* cache-hit */ + } + } else { + chunkqueue_reset(con->write_queue); + } + } + + if (ret == 1 && !buffer_string_is_empty(p->trigger_handler)) { + /* cache-miss */ + buffer_copy_buffer(con->uri.path, p->baseurl); + buffer_append_string_buffer(con->uri.path, p->trigger_handler); + + buffer_copy_buffer(con->physical.path, p->basedir); + buffer_append_string_buffer(con->physical.path, p->trigger_handler); + + chunkqueue_reset(con->write_queue); + } + +error: + lua_close(L); + + buffer_free(b); + + return ret /* cache-error */; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_compress.c b/data/lighttpd/lighttpd-1.4.53/src/mod_compress.c new file mode 100644 index 000000000..dc8babcd8 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_compress.c @@ -0,0 +1,1034 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "http_header.h" +#include "response.h" +#include "stat_cache.h" + +#include "plugin.h" + +#include "crc32.h" +#include "etag.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include "sys-strings.h" + +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#if defined HAVE_ZLIB_H && defined HAVE_LIBZ +# define USE_ZLIB +# include <zlib.h> +#endif + +#if defined HAVE_BZLIB_H && defined HAVE_LIBBZ2 +# define USE_BZ2LIB +/* we don't need stdio interface */ +# define BZ_NO_STDIO +# include <bzlib.h> +#endif + +#if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP +#define USE_MMAP + +#include "sys-mmap.h" +#include <setjmp.h> +#include <signal.h> + +static volatile int sigbus_jmp_valid; +static sigjmp_buf sigbus_jmp; + +static void sigbus_handler(int sig) { + UNUSED(sig); + if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1); + log_failed_assert(__FILE__, __LINE__, "SIGBUS"); +} +#endif + +/* request: accept-encoding */ +#define HTTP_ACCEPT_ENCODING_IDENTITY BV(0) +#define HTTP_ACCEPT_ENCODING_GZIP BV(1) +#define HTTP_ACCEPT_ENCODING_DEFLATE BV(2) +#define HTTP_ACCEPT_ENCODING_COMPRESS BV(3) +#define HTTP_ACCEPT_ENCODING_BZIP2 BV(4) +#define HTTP_ACCEPT_ENCODING_X_GZIP BV(5) +#define HTTP_ACCEPT_ENCODING_X_BZIP2 BV(6) + +#ifdef __WIN32 +# define mkdir(x,y) mkdir(x) +#endif + +typedef struct { + buffer *compress_cache_dir; + array *compress; + off_t compress_max_filesize; /** max filesize in kb */ + int allowed_encodings; + double max_loadavg; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + buffer *ofn; + buffer *b; + + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +INIT_FUNC(mod_compress_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->ofn = buffer_init(); + p->b = buffer_init(); + + return p; +} + +FREE_FUNC(mod_compress_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + buffer_free(p->ofn); + buffer_free(p->b); + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->compress); + buffer_free(s->compress_cache_dir); + + free(s); + } + free(p->config_storage); + } + + + free(p); + + return HANDLER_GO_ON; +} + +/* 0 on success, -1 for error */ +static int mkdir_recursive(char *dir) { + char *p = dir; + + if (!dir || !dir[0]) + return 0; + + while ((p = strchr(p + 1, '/')) != NULL) { + + *p = '\0'; + if ((mkdir(dir, 0700) != 0) && (errno != EEXIST)) { + *p = '/'; + return -1; + } + + *p++ = '/'; + if (!*p) return 0; /* Ignore trailing slash */ + } + + return (mkdir(dir, 0700) != 0) && (errno != EEXIST) ? -1 : 0; +} + +/* 0 on success, -1 for error */ +static int mkdir_for_file(char *filename) { + char *p = filename; + + if (!filename || !filename[0]) + return -1; + + while ((p = strchr(p + 1, '/')) != NULL) { + + *p = '\0'; + if ((mkdir(filename, 0700) != 0) && (errno != EEXIST)) { + *p = '/'; + return -1; + } + + *p++ = '/'; + if (!*p) return -1; /* Unexpected trailing slash in filename */ + } + + return 0; +} + +SETDEFAULTS_FUNC(mod_compress_setdefaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "compress.cache-dir", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "compress.filetype", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { "compress.max-filesize", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, + { "compress.allowed-encodings", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { "compress.max-loadavg", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + array *encodings_arr = array_init(); + + s = calloc(1, sizeof(plugin_config)); + s->compress_cache_dir = buffer_init(); + s->compress = array_init(); + s->compress_max_filesize = 0; + s->allowed_encodings = 0; + s->max_loadavg = 0.0; + + cv[0].destination = s->compress_cache_dir; + cv[1].destination = s->compress; + cv[2].destination = &(s->compress_max_filesize); + cv[3].destination = encodings_arr; /* temp array for allowed encodings list */ + cv[4].destination = srv->tmp_buf; + buffer_clear(srv->tmp_buf); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!buffer_string_is_empty(srv->tmp_buf)) { + s->max_loadavg = strtod(srv->tmp_buf->ptr, NULL); + } + + if (!array_is_vlist(s->compress)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for compress.filetype; expected list of \"mimetype\""); + return HANDLER_ERROR; + } + + if (!array_is_vlist(encodings_arr)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for compress.allowed-encodings; expected list of \"encoding\""); + return HANDLER_ERROR; + } + + if (encodings_arr->used) { + size_t j = 0; + for (j = 0; j < encodings_arr->used; j++) { +#if defined(USE_ZLIB) || defined(USE_BZ2LIB) + data_string *ds = (data_string *)encodings_arr->data[j]; +#endif +#ifdef USE_ZLIB + if (NULL != strstr(ds->value->ptr, "gzip")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_GZIP | HTTP_ACCEPT_ENCODING_X_GZIP; + if (NULL != strstr(ds->value->ptr, "x-gzip")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_X_GZIP; + if (NULL != strstr(ds->value->ptr, "deflate")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_DEFLATE; + /* + if (NULL != strstr(ds->value->ptr, "compress")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_COMPRESS; + */ +#endif +#ifdef USE_BZ2LIB + if (NULL != strstr(ds->value->ptr, "bzip2")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_BZIP2 | HTTP_ACCEPT_ENCODING_X_BZIP2; + if (NULL != strstr(ds->value->ptr, "x-bzip2")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_X_BZIP2; +#endif + } + } else { + /* default encodings */ + s->allowed_encodings = 0 +#ifdef USE_ZLIB + | HTTP_ACCEPT_ENCODING_GZIP | HTTP_ACCEPT_ENCODING_X_GZIP | HTTP_ACCEPT_ENCODING_DEFLATE +#endif +#ifdef USE_BZ2LIB + | HTTP_ACCEPT_ENCODING_BZIP2 | HTTP_ACCEPT_ENCODING_X_BZIP2 +#endif + ; + } + + array_free(encodings_arr); + + if (!buffer_string_is_empty(s->compress_cache_dir)) { + struct stat st; + mkdir_recursive(s->compress_cache_dir->ptr); + + if (0 != stat(s->compress_cache_dir->ptr, &st)) { + log_error_write(srv, __FILE__, __LINE__, "sbs", "can't stat compress.cache-dir", + s->compress_cache_dir, strerror(errno)); + + return HANDLER_ERROR; + } + } + } + + return HANDLER_GO_ON; + +} + +#ifdef USE_ZLIB +static int deflate_file_to_buffer_gzip(server *srv, connection *con, plugin_data *p, char *start, off_t st_size, time_t mtime) { + unsigned char *c; + unsigned long crc; + z_stream z; + size_t outlen; + + UNUSED(srv); + UNUSED(con); + + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = Z_NULL; + + if (Z_OK != deflateInit2(&z, + Z_DEFAULT_COMPRESSION, + Z_DEFLATED, + -MAX_WBITS, /* supress zlib-header */ + 8, + Z_DEFAULT_STRATEGY)) { + return -1; + } + + z.next_in = (unsigned char *)start; + z.avail_in = st_size; + z.total_in = 0; + + + buffer_string_prepare_copy(p->b, (z.avail_in * 1.1) + 12 + 18); + + /* write gzip header */ + + c = (unsigned char *)p->b->ptr; + c[0] = 0x1f; + c[1] = 0x8b; + c[2] = Z_DEFLATED; + c[3] = 0; /* options */ + c[4] = (mtime >> 0) & 0xff; + c[5] = (mtime >> 8) & 0xff; + c[6] = (mtime >> 16) & 0xff; + c[7] = (mtime >> 24) & 0xff; + c[8] = 0x00; /* extra flags */ + c[9] = 0x03; /* UNIX */ + + outlen = 10; + z.next_out = (unsigned char *)p->b->ptr + outlen; + z.avail_out = p->b->size - outlen - 9; + z.total_out = 0; + + if (Z_STREAM_END != deflate(&z, Z_FINISH)) { + deflateEnd(&z); + return -1; + } + + /* trailer */ + outlen += z.total_out; + + crc = generate_crc32c(start, st_size); + + c = (unsigned char *)p->b->ptr + outlen; + + c[0] = (crc >> 0) & 0xff; + c[1] = (crc >> 8) & 0xff; + c[2] = (crc >> 16) & 0xff; + c[3] = (crc >> 24) & 0xff; + c[4] = (z.total_in >> 0) & 0xff; + c[5] = (z.total_in >> 8) & 0xff; + c[6] = (z.total_in >> 16) & 0xff; + c[7] = (z.total_in >> 24) & 0xff; + outlen += 8; + buffer_commit(p->b, outlen); + + if (Z_OK != deflateEnd(&z)) { + return -1; + } + + return 0; +} + +static int deflate_file_to_buffer_deflate(server *srv, connection *con, plugin_data *p, unsigned char *start, off_t st_size) { + z_stream z; + + UNUSED(srv); + UNUSED(con); + + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = Z_NULL; + + if (Z_OK != deflateInit2(&z, + Z_DEFAULT_COMPRESSION, + Z_DEFLATED, + -MAX_WBITS, /* supress zlib-header */ + 8, + Z_DEFAULT_STRATEGY)) { + return -1; + } + + z.next_in = start; + z.avail_in = st_size; + z.total_in = 0; + + buffer_string_prepare_copy(p->b, (z.avail_in * 1.1) + 12); + + z.next_out = (unsigned char *)p->b->ptr; + z.avail_out = p->b->size - 1; + z.total_out = 0; + + if (Z_STREAM_END != deflate(&z, Z_FINISH)) { + deflateEnd(&z); + return -1; + } + + if (Z_OK != deflateEnd(&z)) { + return -1; + } + + /* trailer */ + buffer_commit(p->b, z.total_out); + + return 0; +} + +#endif + +#ifdef USE_BZ2LIB +static int deflate_file_to_buffer_bzip2(server *srv, connection *con, plugin_data *p, unsigned char *start, off_t st_size) { + bz_stream bz; + + UNUSED(srv); + UNUSED(con); + + bz.bzalloc = NULL; + bz.bzfree = NULL; + bz.opaque = NULL; + + if (BZ_OK != BZ2_bzCompressInit(&bz, + 9, /* blocksize = 900k */ + 0, /* no output */ + 0)) { /* workFactor: default */ + return -1; + } + + bz.next_in = (char *)start; + bz.avail_in = st_size; + bz.total_in_lo32 = 0; + bz.total_in_hi32 = 0; + + buffer_string_prepare_copy(p->b, (bz.avail_in * 1.1) + 12); + + bz.next_out = p->b->ptr; + bz.avail_out = p->b->size - 1; + bz.total_out_lo32 = 0; + bz.total_out_hi32 = 0; + + if (BZ_STREAM_END != BZ2_bzCompress(&bz, BZ_FINISH)) { + BZ2_bzCompressEnd(&bz); + return -1; + } + + if (BZ_OK != BZ2_bzCompressEnd(&bz)) { + return -1; + } + + /* file is too large for now */ + if (bz.total_out_hi32) return -1; + + /* trailer */ + buffer_commit(p->b, bz.total_out_lo32); + + return 0; +} +#endif + +static void mod_compress_note_ratio(server *srv, connection *con, off_t in, off_t out) { + /* store compression ratio in environment + * for possible logging by mod_accesslog + * (late in response handling, so not seen by most other modules) */ + /*(should be called only at end of successful response compression)*/ + char ratio[LI_ITOSTRING_LENGTH]; + if (0 == in) return; + li_itostrn(ratio, sizeof(ratio), out * 100 / in); + http_header_env_set(con, CONST_STR_LEN("ratio"), ratio, strlen(ratio)); + UNUSED(srv); +} + +static int deflate_file_to_file(server *srv, connection *con, plugin_data *p, buffer *fn, stat_cache_entry *sce, int type) { + int ifd, ofd; + int ret; +#ifdef USE_MMAP + volatile int mapped = 0;/* quiet warning: might be clobbered by 'longjmp' */ +#endif + void *start; + const char *filename = fn->ptr; + stat_cache_entry *sce_ofn; + ssize_t r; + + /* overflow */ + if ((off_t)(sce->st.st_size * 1.1) < sce->st.st_size) return -1; + + /* don't mmap files > 128Mb + * + * we could use a sliding window, but currently there is no need for it + */ + + if (sce->st.st_size > 128 * 1024 * 1024) return -1; + + buffer_reset(p->ofn); + buffer_copy_buffer(p->ofn, p->conf.compress_cache_dir); + buffer_append_slash(p->ofn); + + if (0 == strncmp(con->physical.path->ptr, con->physical.doc_root->ptr, buffer_string_length(con->physical.doc_root))) { + buffer_append_string(p->ofn, con->physical.path->ptr + buffer_string_length(con->physical.doc_root)); + } else { + buffer_append_string_buffer(p->ofn, con->uri.path); + } + + switch(type) { + case HTTP_ACCEPT_ENCODING_GZIP: + case HTTP_ACCEPT_ENCODING_X_GZIP: + buffer_append_string_len(p->ofn, CONST_STR_LEN("-gzip-")); + break; + case HTTP_ACCEPT_ENCODING_DEFLATE: + buffer_append_string_len(p->ofn, CONST_STR_LEN("-deflate-")); + break; + case HTTP_ACCEPT_ENCODING_BZIP2: + case HTTP_ACCEPT_ENCODING_X_BZIP2: + buffer_append_string_len(p->ofn, CONST_STR_LEN("-bzip2-")); + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sd", "unknown compression type", type); + return -1; + } + + buffer_append_string_buffer(p->ofn, sce->etag); + + if (HANDLER_ERROR != stat_cache_get_entry(srv, con, p->ofn, &sce_ofn)) { + if (0 == sce->st.st_size) return -1; /* cache file being created */ + /* cache-entry exists */ +#if 0 + log_error_write(srv, __FILE__, __LINE__, "bs", p->ofn, "compress-cache hit"); +#endif + mod_compress_note_ratio(srv, con, sce->st.st_size, sce_ofn->st.st_size); + buffer_copy_buffer(con->physical.path, p->ofn); + return 0; + } + + if (0.0 < p->conf.max_loadavg && p->conf.max_loadavg < srv->srvconf.loadavg[0]) { + return -1; + } + + if (-1 == mkdir_for_file(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "couldn't create directory for file", p->ofn); + return -1; + } + + if (-1 == (ofd = open(p->ofn->ptr, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600))) { + if (errno == EEXIST) { + return -1; /* cache file being created */ + } + + log_error_write(srv, __FILE__, __LINE__, "sbss", "creating cachefile", p->ofn, "failed", strerror(errno)); + + return -1; + } +#if 0 + log_error_write(srv, __FILE__, __LINE__, "bs", p->ofn, "compress-cache miss"); +#endif + if (-1 == (ifd = open(filename, O_RDONLY | O_BINARY))) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "opening plain-file", fn, "failed", strerror(errno)); + + close(ofd); + + /* Remove the incomplete cache file, so that later hits aren't served from it */ + if (-1 == unlink(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno)); + } + + return -1; + } + +#ifdef USE_MMAP + if (MAP_FAILED != (start = mmap(NULL, sce->st.st_size, PROT_READ, MAP_SHARED, ifd, 0)) + || (errno == EINVAL && MAP_FAILED != (start = mmap(NULL, sce->st.st_size, PROT_READ, MAP_PRIVATE, ifd, 0)))) { + mapped = 1; + signal(SIGBUS, sigbus_handler); + sigbus_jmp_valid = 1; + if (0 != sigsetjmp(sigbus_jmp, 1)) { + sigbus_jmp_valid = 0; + + log_error_write(srv, __FILE__, __LINE__, "sbd", "SIGBUS in mmap:", + fn, ifd); + + munmap(start, sce->st.st_size); + close(ofd); + close(ifd); + + /* Remove the incomplete cache file, so that later hits aren't served from it */ + if (-1 == unlink(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno)); + } + + return -1; + } + } else +#endif /* FIXME: might attempt to read very large file completely into memory; see compress.max-filesize config option */ + if (NULL == (start = malloc(sce->st.st_size)) || sce->st.st_size != read(ifd, start, sce->st.st_size)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "reading", fn, "failed", strerror(errno)); + + close(ofd); + close(ifd); + free(start); + + /* Remove the incomplete cache file, so that later hits aren't served from it */ + if (-1 == unlink(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno)); + } + + return -1; + } + + ret = -1; + switch(type) { +#ifdef USE_ZLIB + case HTTP_ACCEPT_ENCODING_GZIP: + case HTTP_ACCEPT_ENCODING_X_GZIP: + ret = deflate_file_to_buffer_gzip(srv, con, p, start, sce->st.st_size, sce->st.st_mtime); + break; + case HTTP_ACCEPT_ENCODING_DEFLATE: + ret = deflate_file_to_buffer_deflate(srv, con, p, start, sce->st.st_size); + break; +#endif +#ifdef USE_BZ2LIB + case HTTP_ACCEPT_ENCODING_BZIP2: + case HTTP_ACCEPT_ENCODING_X_BZIP2: + ret = deflate_file_to_buffer_bzip2(srv, con, p, start, sce->st.st_size); + break; +#endif + } + + if (ret == 0) { + r = write(ofd, CONST_BUF_LEN(p->b)); + if (-1 == r) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "writing cachefile", p->ofn, "failed:", strerror(errno)); + ret = -1; + } else if ((size_t)r != buffer_string_length(p->b)) { + log_error_write(srv, __FILE__, __LINE__, "sbs", "writing cachefile", p->ofn, "failed: not enough bytes written"); + ret = -1; + } + } + +#ifdef USE_MMAP + if (mapped) { + sigbus_jmp_valid = 0; + munmap(start, sce->st.st_size); + } else +#endif + free(start); + + close(ifd); + + if (0 != close(ofd) || ret != 0) { + if (0 == ret) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "writing cachefile", p->ofn, "failed:", strerror(errno)); + } + + /* Remove the incomplete cache file, so that later hits aren't served from it */ + if (-1 == unlink(p->ofn->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "unlinking incomplete cachefile", p->ofn, "failed:", strerror(errno)); + } + + return -1; + } + + buffer_copy_buffer(con->physical.path, p->ofn); + mod_compress_note_ratio(srv, con, sce->st.st_size, + (off_t)buffer_string_length(p->b)); + + return 0; +} + +static int deflate_file_to_buffer(server *srv, connection *con, plugin_data *p, buffer *fn, stat_cache_entry *sce, int type) { + int ifd; + int ret = -1; +#ifdef USE_MMAP + volatile int mapped = 0;/* quiet warning: might be clobbered by 'longjmp' */ +#endif + void *start; + + /* overflow */ + if ((off_t)(sce->st.st_size * 1.1) < sce->st.st_size) return -1; + + /* don't mmap files > 128M + * + * we could use a sliding window, but currently there is no need for it + */ + + if (sce->st.st_size > 128 * 1024 * 1024) return -1; + + if (0.0 < p->conf.max_loadavg && p->conf.max_loadavg < srv->srvconf.loadavg[0]) { + return -1; + } + + if (-1 == (ifd = open(fn->ptr, O_RDONLY | O_BINARY))) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "opening plain-file", fn, "failed", strerror(errno)); + + return -1; + } + +#ifdef USE_MMAP + if (MAP_FAILED != (start = mmap(NULL, sce->st.st_size, PROT_READ, MAP_SHARED, ifd, 0)) + || (errno == EINVAL && MAP_FAILED != (start = mmap(NULL, sce->st.st_size, PROT_READ, MAP_PRIVATE, ifd, 0)))) { + mapped = 1; + signal(SIGBUS, sigbus_handler); + sigbus_jmp_valid = 1; + if (0 != sigsetjmp(sigbus_jmp, 1)) { + sigbus_jmp_valid = 0; + + log_error_write(srv, __FILE__, __LINE__, "sbd", "SIGBUS in mmap:", + fn, ifd); + + munmap(start, sce->st.st_size); + close(ifd); + return -1; + } + } else +#endif /* FIXME: might attempt to read very large file completely into memory; see compress.max-filesize config option */ + if (NULL == (start = malloc(sce->st.st_size)) || sce->st.st_size != read(ifd, start, sce->st.st_size)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "reading", fn, "failed", strerror(errno)); + + close(ifd); + free(start); + return -1; + } + + switch(type) { +#ifdef USE_ZLIB + case HTTP_ACCEPT_ENCODING_GZIP: + case HTTP_ACCEPT_ENCODING_X_GZIP: + ret = deflate_file_to_buffer_gzip(srv, con, p, start, sce->st.st_size, sce->st.st_mtime); + break; + case HTTP_ACCEPT_ENCODING_DEFLATE: + ret = deflate_file_to_buffer_deflate(srv, con, p, start, sce->st.st_size); + break; +#endif +#ifdef USE_BZ2LIB + case HTTP_ACCEPT_ENCODING_BZIP2: + case HTTP_ACCEPT_ENCODING_X_BZIP2: + ret = deflate_file_to_buffer_bzip2(srv, con, p, start, sce->st.st_size); + break; +#endif + default: + ret = -1; + break; + } + +#ifdef USE_MMAP + if (mapped) { + sigbus_jmp_valid = 0; + munmap(start, sce->st.st_size); + } else +#endif + free(start); + + close(ifd); + + if (ret != 0) return -1; + + mod_compress_note_ratio(srv, con, sce->st.st_size, + (off_t)buffer_string_length(p->b)); + chunkqueue_reset(con->write_queue); + chunkqueue_append_buffer(con->write_queue, p->b); + + buffer_reset(con->physical.path); + + con->file_finished = 1; + con->file_started = 1; + + return 0; +} + + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_compress_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(compress_cache_dir); + PATCH(compress); + PATCH(compress_max_filesize); + PATCH(allowed_encodings); + PATCH(max_loadavg); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.cache-dir"))) { + PATCH(compress_cache_dir); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.filetype"))) { + PATCH(compress); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.max-filesize"))) { + PATCH(compress_max_filesize); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.allowed-encodings"))) { + PATCH(allowed_encodings); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.max-loadavg"))) { + PATCH(max_loadavg); + } + } + } + + return 0; +} +#undef PATCH + +static int mod_compress_contains_encoding(const char *headervalue, const char *encoding, size_t len) { + const char *m = headervalue; + do { + while (*m == ',' || *m == ' ' || *m == '\t') { + ++m; + } + if (0 == strncasecmp(m, encoding, len)) { + /*(not a full HTTP field parse: not parsing for q-values and not handling q=0)*/ + m += len; + if (*m == '\0' || *m == ',' || *m == ';' || *m == ' ' || *m == '\t') + return 1; + } else if (*m != '\0') { + ++m; + } + } while ((m = strchr(m, ','))); + return 0; +} + +PHYSICALPATH_FUNC(mod_compress_physical) { + plugin_data *p = p_d; + size_t m; + off_t max_fsize; + stat_cache_entry *sce = NULL; + buffer *mtime = NULL; + buffer *content_type; + + if (con->mode != DIRECT || con->http_status) return HANDLER_GO_ON; + + /* only GET and POST can get compressed */ + if (con->request.http_method != HTTP_METHOD_GET && + con->request.http_method != HTTP_METHOD_POST) { + return HANDLER_GO_ON; + } + + if (buffer_string_is_empty(con->physical.path)) { + return HANDLER_GO_ON; + } + + mod_compress_patch_connection(srv, con, p); + + max_fsize = p->conf.compress_max_filesize; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- handling file as static file"); + } + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + con->http_status = 403; + + log_error_write(srv, __FILE__, __LINE__, "sbsb", + "not a regular file:", con->uri.path, + "->", con->physical.path); + + return HANDLER_FINISHED; + } + + /* we only handle regular files */ +#ifdef HAVE_LSTAT + if ((sce->is_symlink == 1) && !con->conf.follow_symlink) { + return HANDLER_GO_ON; + } +#endif + if (!S_ISREG(sce->st.st_mode)) { + return HANDLER_GO_ON; + } + + /* don't compress files that are too large as we need to much time to handle them */ + if (max_fsize && (sce->st.st_size >> 10) > max_fsize) return HANDLER_GO_ON; + + /* don't try to compress files less than 128 bytes + * + * - extra overhead for compression + * - mmap() fails for st_size = 0 :) + */ + if (sce->st.st_size < 128) return HANDLER_GO_ON; + + stat_cache_etag_get(sce, con->etag_flags); + + /* check if mimetype is in compress-config */ + content_type = NULL; + stat_cache_content_type_get(srv, con, con->physical.path, sce); + if (!buffer_is_empty(sce->content_type)) { + char *c; + if ( (c = strchr(sce->content_type->ptr, ';')) != NULL) { + content_type = srv->tmp_buf; + buffer_copy_string_len(content_type, sce->content_type->ptr, c - sce->content_type->ptr); + } + } + else { + content_type = srv->tmp_buf; + buffer_copy_string_len(content_type, CONST_STR_LEN("")); + } + + for (m = 0; m < p->conf.compress->used; m++) { + data_string *compress_ds = (data_string *)p->conf.compress->data[m]; + + if (buffer_is_equal(compress_ds->value, sce->content_type) + || (content_type && buffer_is_equal(compress_ds->value, content_type))) { + /* mimetype found */ + buffer *vb; + + /* the response might change according to Accept-Encoding */ + http_header_response_append(con, HTTP_HEADER_VARY, CONST_STR_LEN("Vary"), CONST_STR_LEN("Accept-Encoding")); + + if (NULL != (vb = http_header_request_get(con, HTTP_HEADER_ACCEPT_ENCODING, CONST_STR_LEN("Accept-Encoding")))) { + int accept_encoding = 0; + char *value = vb->ptr; + int matched_encodings = 0; + int use_etag = sce->etag != NULL && sce->etag->ptr != NULL; + + /* get client side support encodings */ +#ifdef USE_ZLIB + if (mod_compress_contains_encoding(value, CONST_STR_LEN("gzip"))) accept_encoding |= HTTP_ACCEPT_ENCODING_GZIP; + if (mod_compress_contains_encoding(value, CONST_STR_LEN("x-gzip"))) accept_encoding |= HTTP_ACCEPT_ENCODING_X_GZIP; + if (mod_compress_contains_encoding(value, CONST_STR_LEN("deflate"))) accept_encoding |= HTTP_ACCEPT_ENCODING_DEFLATE; + if (mod_compress_contains_encoding(value, CONST_STR_LEN("compress"))) accept_encoding |= HTTP_ACCEPT_ENCODING_COMPRESS; +#endif +#ifdef USE_BZ2LIB + if (mod_compress_contains_encoding(value, CONST_STR_LEN("bzip2"))) accept_encoding |= HTTP_ACCEPT_ENCODING_BZIP2; + if (mod_compress_contains_encoding(value, CONST_STR_LEN("x-bzip2"))) accept_encoding |= HTTP_ACCEPT_ENCODING_X_BZIP2; +#endif + if (mod_compress_contains_encoding(value, CONST_STR_LEN("identity"))) accept_encoding |= HTTP_ACCEPT_ENCODING_IDENTITY; + + /* find matching entries */ + matched_encodings = accept_encoding & p->conf.allowed_encodings; + + if (matched_encodings) { + static const char dflt_gzip[] = "gzip"; + static const char dflt_x_gzip[] = "x-gzip"; + static const char dflt_deflate[] = "deflate"; + static const char dflt_bzip2[] = "bzip2"; + static const char dflt_x_bzip2[] = "x-bzip2"; + + const char *compression_name = NULL; + int compression_type = 0; + + mtime = strftime_cache_get(srv, sce->st.st_mtime); + + /* try matching original etag of uncompressed version */ + if (use_etag) { + etag_mutate(con->physical.etag, sce->etag); + if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + http_header_response_set(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); + http_header_response_set(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + return HANDLER_FINISHED; + } + } + + /* select best matching encoding */ + if (matched_encodings & HTTP_ACCEPT_ENCODING_BZIP2) { + compression_type = HTTP_ACCEPT_ENCODING_BZIP2; + compression_name = dflt_bzip2; + } else if (matched_encodings & HTTP_ACCEPT_ENCODING_X_BZIP2) { + compression_type = HTTP_ACCEPT_ENCODING_X_BZIP2; + compression_name = dflt_x_bzip2; + } else if (matched_encodings & HTTP_ACCEPT_ENCODING_GZIP) { + compression_type = HTTP_ACCEPT_ENCODING_GZIP; + compression_name = dflt_gzip; + } else if (matched_encodings & HTTP_ACCEPT_ENCODING_X_GZIP) { + compression_type = HTTP_ACCEPT_ENCODING_X_GZIP; + compression_name = dflt_x_gzip; + } else { + force_assert(matched_encodings & HTTP_ACCEPT_ENCODING_DEFLATE); + compression_type = HTTP_ACCEPT_ENCODING_DEFLATE; + compression_name = dflt_deflate; + } + + if (use_etag) { + /* try matching etag of compressed version */ + buffer_copy_buffer(srv->tmp_buf, sce->etag); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("-")); + buffer_append_string(srv->tmp_buf, compression_name); + etag_mutate(con->physical.etag, srv->tmp_buf); + } + + if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { + http_header_response_set(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name)); + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + http_header_response_set(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); + if (use_etag) { + http_header_response_set(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + } + return HANDLER_FINISHED; + } + + /* deflate it */ + if (use_etag && !buffer_string_is_empty(p->conf.compress_cache_dir)) { + if (0 != deflate_file_to_file(srv, con, p, con->physical.path, sce, compression_type)) + return HANDLER_GO_ON; + } else { + if (0 != deflate_file_to_buffer(srv, con, p, con->physical.path, sce, compression_type)) + return HANDLER_GO_ON; + } + http_header_response_set(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name)); + http_header_response_set(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); + if (use_etag) { + http_header_response_set(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + } + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); + /* let mod_staticfile handle the cached compressed files, physical path was modified */ + return (use_etag && !buffer_string_is_empty(p->conf.compress_cache_dir)) ? HANDLER_GO_ON : HANDLER_FINISHED; + } + } + } + } + + return HANDLER_GO_ON; +} + +int mod_compress_plugin_init(plugin *p); +int mod_compress_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("compress"); + + p->init = mod_compress_init; + p->set_defaults = mod_compress_setdefaults; + p->handle_subrequest_start = mod_compress_physical; + p->cleanup = mod_compress_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_deflate.c b/data/lighttpd/lighttpd-1.4.53/src/mod_deflate.c new file mode 100644 index 000000000..d7a7ea95d --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_deflate.c @@ -0,0 +1,1243 @@ +/* mod_deflate + * + * + * bug fix on Robert Jakabosky from alphatrade.com's lighttp 1.4.10 mod_deflate patch + * + * Bug fix and new features: + * 1) fix loop bug when content-length is bigger than work-block-size*k + * + * ------- + * + * lighttpd-1.4.26.mod_deflate.patch from + * https://redmine.lighttpd.net/projects/1/wiki/Docs_ModDeflate + * + * ------- + * + * Patch further modified in this incarnation. + * + * Note: this patch only handles completed responses (con->file_finished); + * this patch does not currently handle streaming dynamic responses, + * and therefore also does not worry about Transfer-Encoding: chunked + * (or having separate con->output_queue for chunked-encoded output) + * (or using separate buffers per connection instead of p->tmp_buf) + * (or handling interactions with block buffering and write timeouts) + * + * Bug fix: + * - fixed major bug with compressing chunks with offset > 0 + * x-ref: + * "Response breaking in mod_deflate" + * https://redmine.lighttpd.net/issues/986 + * - fix broken (in some cases) chunk accounting in deflate_compress_response() + * - fix broken bzip2 + * x-ref: + * "mod_deflate's bzip2 broken by default" + * https://redmine.lighttpd.net/issues/2035 + * - fix mismatch with current chunk interfaces + * x-ref: + * "Weird things in chunk.c (functions only handling specific cases, unexpected behaviour)" + * https://redmine.lighttpd.net/issues/1510 + * + * Behavior changes from prior patch: + * - deflate.mimetypes must now be configured to enable compression + * deflate.mimetypes = ( ) # compress nothing (disabled; default) + * deflate.mimetypes = ( "" ) # compress all mimetypes + * deflate.mimetypes = ( "text/" ) # compress text/... mimetypes + * x-ref: + * "mod_deflate enabled by default" + * https://redmine.lighttpd.net/issues/1394 + * - deflate.enabled directive removed (see new behavior of deflate.mimetypes) + * - deflate.debug removed (was developer debug trace, not end-user debug) + * - deflate.bzip2 replaced with deflate.allowed-encodings (like mod_compress) + * x-ref: + * "mod_deflate should allow limiting of compression algorithm from the configuration file" + * https://redmine.lighttpd.net/issues/996 + * "mod_compress disabling methods" + * https://redmine.lighttpd.net/issues/1773 + * - deflate.nocompress-url removed since disabling compression for a URL + * can now easily be done by setting to a blank list either directive + * deflate.accept_encodings = () or deflate.mimetypes = () in a conditional + * block, e.g. $HTTP["url"] =~ "....." { deflate.mimetypes = ( ) } + * - deflate.sync-flush removed; controlled by con->conf.stream_response_body + * (though streaming compression not currently implemented in mod_deflate) + * - inactive directives in this patch (since con->file_finished required) + * deflate.work-block-size + * deflate.output-buffer-size + * - remove weak file size check; SIGBUS is trapped, file that shrink will error + * x-ref: + * "mod_deflate: filesize check is too weak" + * https://redmine.lighttpd.net/issues/1512 + * - change default deflate.min-compress-size from 0 to now be 256 + * http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits + * Apache 2.4 mod_deflate minimum is 68 bytes + * Akamai recommends minimum 860 bytes + * Google recommends minimum be somewhere in range between 150 and 1024 bytes + * - deflate.max-compress-size new directive (in kb like compress.max_filesize) + * - deflate.mem-level removed (too many knobs for little benefit) + * - deflate.window-size removed (too many knobs for little benefit) + * + * Future: + * - config directives may be changed, renamed, or removed + * e.g. A set of reasonable defaults might be chosen + * instead of making them configurable. + * deflate.min-compress-size + * - might add deflate.mimetypes-exclude = ( ... ) for list of mimetypes + * to avoid compressing, even if a broader deflate.mimetypes matched, + * e.g. to compress all "text/" except "text/special". + * - mod_compress and mod_deflate might merge overlapping feature sets + * (mod_compress.cache-dir does not yet have an equivalent in mod_deflate) + * + * Implementation notes: + * - http_chunk_append_mem() used instead of http_chunk_append_buffer() + * so that p->tmp_buf can be large and re-used. This results in an extra copy + * of compressed data before data is sent to network, though if the compressed + * size is larger than 64k, it ends up being sent to a temporary file on + * disk without suffering an extra copy in memory, and without extra chunk + * create and destroy. If this is ever changed to give away buffers, then use + * a unique hctx->output buffer per hctx; do not reuse p->tmp_buf across + * multiple requests being handled in parallel. + */ +#include "first.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include "sys-mmap.h" + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <unistd.h> /* read() */ + +#include "base.h" +#include "fdevent.h" +#include "log.h" +#include "buffer.h" +#include "etag.h" +#include "http_chunk.h" +#include "http_header.h" +#include "response.h" + +#include "plugin.h" + +#if defined HAVE_ZLIB_H && defined HAVE_LIBZ +# define USE_ZLIB +# include <zlib.h> +#endif +#ifndef Z_DEFAULT_COMPRESSION +#define Z_DEFAULT_COMPRESSION -1 +#endif +#ifndef MAX_WBITS +#define MAX_WBITS 15 +#endif + +#if defined HAVE_BZLIB_H && defined HAVE_LIBBZ2 +# define USE_BZ2LIB +/* we don't need stdio interface */ +# define BZ_NO_STDIO +# include <bzlib.h> +#endif + +#if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP +#define USE_MMAP + +#include "sys-mmap.h" +#include <setjmp.h> +#include <signal.h> + +static volatile int sigbus_jmp_valid; +static sigjmp_buf sigbus_jmp; + +static void sigbus_handler(int sig) { + UNUSED(sig); + if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1); + log_failed_assert(__FILE__, __LINE__, "SIGBUS"); +} +#endif + +/* request: accept-encoding */ +#define HTTP_ACCEPT_ENCODING_IDENTITY BV(0) +#define HTTP_ACCEPT_ENCODING_GZIP BV(1) +#define HTTP_ACCEPT_ENCODING_DEFLATE BV(2) +#define HTTP_ACCEPT_ENCODING_COMPRESS BV(3) +#define HTTP_ACCEPT_ENCODING_BZIP2 BV(4) +#define HTTP_ACCEPT_ENCODING_X_GZIP BV(5) +#define HTTP_ACCEPT_ENCODING_X_BZIP2 BV(6) + +#define KByte * 1024 +#define MByte * 1024 KByte +#define GByte * 1024 MByte + +typedef struct { + array *mimetypes; + int allowed_encodings; + unsigned int max_compress_size; + unsigned short min_compress_size; + unsigned short output_buffer_size; + unsigned short work_block_size; + unsigned short sync_flush; + short compression_level; + double max_loadavg; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + buffer *tmp_buf; + array *encodings; + + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +typedef struct { + union { + #ifdef USE_ZLIB + z_stream z; + #endif + #ifdef USE_BZ2LIB + bz_stream bz; + #endif + int dummy; + } u; + off_t bytes_in; + off_t bytes_out; + chunkqueue *in_queue; + buffer *output; + plugin_data *plugin_data; + int compression_type; +} handler_ctx; + +static handler_ctx *handler_ctx_init() { + handler_ctx *hctx; + + hctx = calloc(1, sizeof(*hctx)); + hctx->in_queue = chunkqueue_init(); + + return hctx; +} + +static void handler_ctx_free(handler_ctx *hctx) { + #if 0 + if (hctx->output != p->tmp_buf) { + buffer_free(hctx->output); + } + #endif + chunkqueue_free(hctx->in_queue); + free(hctx); +} + +INIT_FUNC(mod_deflate_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->encodings = array_init(); + p->tmp_buf = buffer_init(); + buffer_string_prepare_copy(p->tmp_buf, 64 KByte); + + return p; +} + +FREE_FUNC(mod_deflate_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (!s) continue; + + array_free(s->mimetypes); + free(s); + } + free(p->config_storage); + } + + buffer_free(p->tmp_buf); + array_free(p->encodings); + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_deflate_setdefaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "deflate.mimetypes", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { "deflate.allowed-encodings", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { "deflate.max-compress-size", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, + { "deflate.min-compress-size", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, + { "deflate.compression-level", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, + { "deflate.output-buffer-size", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, + { "deflate.work-block-size", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, + { "deflate.max-loadavg", NULL, T_CONFIG_STRING,T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->mimetypes = array_init(); + s->allowed_encodings = 0; + s->max_compress_size = 128*1024; /*(128 MB measured as num KB)*/ + s->min_compress_size = 256; + s->output_buffer_size = 0; + s->work_block_size = 2048; + s->sync_flush = 0; + s->compression_level = -1; + s->max_loadavg = 0.0; + + array_reset_data_strings(p->encodings); /* temp array for allowed encodings list */ + + cv[0].destination = s->mimetypes; + cv[1].destination = p->encodings; + cv[2].destination = &(s->max_compress_size); + cv[3].destination = &(s->min_compress_size); + cv[4].destination = &(s->compression_level); + cv[5].destination = &(s->output_buffer_size); + cv[6].destination = &(s->work_block_size); + cv[7].destination = p->tmp_buf; + buffer_clear(p->tmp_buf); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if ((s->compression_level < 1 || s->compression_level > 9) && + s->compression_level != -1) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "compression-level must be between 1 and 9:", s->compression_level); + return HANDLER_ERROR; + } + + if (!buffer_string_is_empty(p->tmp_buf)) { + s->max_loadavg = strtod(p->tmp_buf->ptr, NULL); + } + + if (!array_is_vlist(s->mimetypes)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for deflate.mimetypes; expected list of \"mimetype\""); + return HANDLER_ERROR; + } + + if (!array_is_vlist(p->encodings)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for deflate.allowed-encodings; expected list of \"encoding\""); + return HANDLER_ERROR; + } + + if (p->encodings->used) { + size_t j = 0; + for (j = 0; j < p->encodings->used; j++) { +#if defined(USE_ZLIB) || defined(USE_BZ2LIB) + data_string *ds = (data_string *)p->encodings->data[j]; +#endif +#ifdef USE_ZLIB + if (NULL != strstr(ds->value->ptr, "gzip")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_GZIP | HTTP_ACCEPT_ENCODING_X_GZIP; + if (NULL != strstr(ds->value->ptr, "x-gzip")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_X_GZIP; + if (NULL != strstr(ds->value->ptr, "deflate")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_DEFLATE; + /* + if (NULL != strstr(ds->value->ptr, "compress")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_COMPRESS; + */ +#endif +#ifdef USE_BZ2LIB + if (NULL != strstr(ds->value->ptr, "bzip2")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_BZIP2 | HTTP_ACCEPT_ENCODING_X_BZIP2; + if (NULL != strstr(ds->value->ptr, "x-bzip2")) + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_X_BZIP2; +#endif + } + } else { + /* default encodings */ +#ifdef USE_ZLIB + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_GZIP + | HTTP_ACCEPT_ENCODING_X_GZIP + | HTTP_ACCEPT_ENCODING_DEFLATE; +#endif +#ifdef USE_BZ2LIB + s->allowed_encodings |= HTTP_ACCEPT_ENCODING_BZIP2 + | HTTP_ACCEPT_ENCODING_X_BZIP2; +#endif + } + + /* mod_deflate matches mimetype as prefix of Content-Type + * so ignore '*' at end of mimetype for end-user flexibility + * in specifying trailing wildcard to grouping of mimetypes */ + for (size_t m = 0; m < s->mimetypes->used; ++m) { + buffer *mimetype = ((data_string *)s->mimetypes->data[m])->value; + size_t len = buffer_string_length(mimetype); + if (len > 2 && mimetype->ptr[len-1] == '*') { + buffer_string_set_length(mimetype, len-1); + } + } + } + + return HANDLER_GO_ON; + +} + + +#if defined(USE_ZLIB) || defined(USE_BZ2LIB) +static int stream_http_chunk_append_mem(server *srv, connection *con, handler_ctx *hctx, size_t len) { + /* future: might also write stream to hctx temporary file in compressed file cache */ + return http_chunk_append_mem(srv, con, hctx->output->ptr, len); +} +#endif + + +#ifdef USE_ZLIB + +static int stream_deflate_init(handler_ctx *hctx) { + z_stream * const z = &hctx->u.z; + const plugin_data * const p = hctx->plugin_data; + z->zalloc = Z_NULL; + z->zfree = Z_NULL; + z->opaque = Z_NULL; + z->total_in = 0; + z->total_out = 0; + z->next_out = (unsigned char *)hctx->output->ptr; + z->avail_out = hctx->output->size; + + if (Z_OK != deflateInit2(z, + p->conf.compression_level > 0 + ? p->conf.compression_level + : Z_DEFAULT_COMPRESSION, + Z_DEFLATED, + (hctx->compression_type == HTTP_ACCEPT_ENCODING_GZIP) + ? (MAX_WBITS | 16) /*(0x10 flags gzip header, trailer)*/ + : -MAX_WBITS, /*(negate to suppress zlib header)*/ + 8, /* default memLevel */ + Z_DEFAULT_STRATEGY)) { + return -1; + } + + return 0; +} + +static int stream_deflate_compress(server *srv, connection *con, handler_ctx *hctx, unsigned char *start, off_t st_size) { + z_stream * const z = &(hctx->u.z); + size_t len; + + z->next_in = start; + z->avail_in = st_size; + hctx->bytes_in += st_size; + + /* compress data */ + do { + if (Z_OK != deflate(z, Z_NO_FLUSH)) return -1; + + if (z->avail_out == 0 || z->avail_in > 0) { + len = hctx->output->size - z->avail_out; + hctx->bytes_out += len; + stream_http_chunk_append_mem(srv, con, hctx, len); + z->next_out = (unsigned char *)hctx->output->ptr; + z->avail_out = hctx->output->size; + } + } while (z->avail_in > 0); + + return 0; +} + +static int stream_deflate_flush(server *srv, connection *con, handler_ctx *hctx, int end) { + z_stream * const z = &(hctx->u.z); + const plugin_data *p = hctx->plugin_data; + size_t len; + int rc = 0; + int done; + + /* compress data */ + do { + done = 1; + if (end) { + rc = deflate(z, Z_FINISH); + if (rc == Z_OK) { + done = 0; + } else if (rc != Z_STREAM_END) { + return -1; + } + } else { + if (p->conf.sync_flush) { + rc = deflate(z, Z_SYNC_FLUSH); + if (rc != Z_OK) return -1; + } else if (z->avail_in > 0) { + rc = deflate(z, Z_NO_FLUSH); + if (rc != Z_OK) return -1; + } + } + + len = hctx->output->size - z->avail_out; + if (z->avail_out == 0 || (len > 0 && (end || p->conf.sync_flush))) { + hctx->bytes_out += len; + stream_http_chunk_append_mem(srv, con, hctx, len); + z->next_out = (unsigned char *)hctx->output->ptr; + z->avail_out = hctx->output->size; + } + } while (z->avail_in != 0 || !done); + + return 0; +} + +static int stream_deflate_end(server *srv, handler_ctx *hctx) { + z_stream * const z = &(hctx->u.z); + int rc = deflateEnd(z); + if (Z_OK == rc || Z_DATA_ERROR == rc) return 0; + + if (z->msg != NULL) { + log_error_write(srv, __FILE__, __LINE__, "sdss", + "deflateEnd error ret=", rc, ", msg=", z->msg); + } else { + log_error_write(srv, __FILE__, __LINE__, "sd", + "deflateEnd error ret=", rc); + } + return -1; +} + +#endif + + +#ifdef USE_BZ2LIB + +static int stream_bzip2_init(handler_ctx *hctx) { + bz_stream * const bz = &hctx->u.bz; + const plugin_data * const p = hctx->plugin_data; + bz->bzalloc = NULL; + bz->bzfree = NULL; + bz->opaque = NULL; + bz->total_in_lo32 = 0; + bz->total_in_hi32 = 0; + bz->total_out_lo32 = 0; + bz->total_out_hi32 = 0; + bz->next_out = hctx->output->ptr; + bz->avail_out = hctx->output->size; + + if (BZ_OK != BZ2_bzCompressInit(bz, + p->conf.compression_level > 0 + ? p->conf.compression_level + : 9, /* blocksize = 900k */ + 0, /* verbosity */ + 0)) { /* workFactor: default */ + return -1; + } + + return 0; +} + +static int stream_bzip2_compress(server *srv, connection *con, handler_ctx *hctx, unsigned char *start, off_t st_size) { + bz_stream * const bz = &(hctx->u.bz); + size_t len; + + bz->next_in = (char *)start; + bz->avail_in = st_size; + hctx->bytes_in += st_size; + + /* compress data */ + do { + if (BZ_RUN_OK != BZ2_bzCompress(bz, BZ_RUN)) return -1; + + if (bz->avail_out == 0 || bz->avail_in > 0) { + len = hctx->output->size - bz->avail_out; + hctx->bytes_out += len; + stream_http_chunk_append_mem(srv, con, hctx, len); + bz->next_out = hctx->output->ptr; + bz->avail_out = hctx->output->size; + } + } while (bz->avail_in > 0); + + return 0; +} + +static int stream_bzip2_flush(server *srv, connection *con, handler_ctx *hctx, int end) { + bz_stream * const bz = &(hctx->u.bz); + const plugin_data *p = hctx->plugin_data; + size_t len; + int rc; + int done; + + /* compress data */ + do { + done = 1; + if (end) { + rc = BZ2_bzCompress(bz, BZ_FINISH); + if (rc == BZ_FINISH_OK) { + done = 0; + } else if (rc != BZ_STREAM_END) { + return -1; + } + } else if (bz->avail_in > 0) { + /* p->conf.sync_flush not implemented here, + * which would loop on BZ_FLUSH while BZ_FLUSH_OK + * until BZ_RUN_OK returned */ + rc = BZ2_bzCompress(bz, BZ_RUN); + if (rc != BZ_RUN_OK) { + return -1; + } + } + + len = hctx->output->size - bz->avail_out; + if (bz->avail_out == 0 || (len > 0 && (end || p->conf.sync_flush))) { + hctx->bytes_out += len; + stream_http_chunk_append_mem(srv, con, hctx, len); + bz->next_out = hctx->output->ptr; + bz->avail_out = hctx->output->size; + } + } while (bz->avail_in != 0 || !done); + + return 0; +} + +static int stream_bzip2_end(server *srv, handler_ctx *hctx) { + bz_stream * const bz = &(hctx->u.bz); + int rc = BZ2_bzCompressEnd(bz); + if (BZ_OK == rc || BZ_DATA_ERROR == rc) return 0; + + log_error_write(srv, __FILE__, __LINE__, "sd", + "BZ2_bzCompressEnd error ret=", rc); + return -1; +} + +#endif + + +static int mod_deflate_stream_init(handler_ctx *hctx) { + switch(hctx->compression_type) { +#ifdef USE_ZLIB + case HTTP_ACCEPT_ENCODING_GZIP: + case HTTP_ACCEPT_ENCODING_DEFLATE: + return stream_deflate_init(hctx); +#endif +#ifdef USE_BZ2LIB + case HTTP_ACCEPT_ENCODING_BZIP2: + return stream_bzip2_init(hctx); +#endif + default: + return -1; + } +} + +static int mod_deflate_compress(server *srv, connection *con, handler_ctx *hctx, unsigned char *start, off_t st_size) { + if (0 == st_size) return 0; + switch(hctx->compression_type) { +#ifdef USE_ZLIB + case HTTP_ACCEPT_ENCODING_GZIP: + case HTTP_ACCEPT_ENCODING_DEFLATE: + return stream_deflate_compress(srv, con, hctx, start, st_size); +#endif +#ifdef USE_BZ2LIB + case HTTP_ACCEPT_ENCODING_BZIP2: + return stream_bzip2_compress(srv, con, hctx, start, st_size); +#endif + default: + UNUSED(srv); + UNUSED(con); + UNUSED(start); + return -1; + } +} + +static int mod_deflate_stream_flush(server *srv, connection *con, handler_ctx *hctx, int end) { + if (0 == hctx->bytes_in) return 0; + switch(hctx->compression_type) { +#ifdef USE_ZLIB + case HTTP_ACCEPT_ENCODING_GZIP: + case HTTP_ACCEPT_ENCODING_DEFLATE: + return stream_deflate_flush(srv, con, hctx, end); +#endif +#ifdef USE_BZ2LIB + case HTTP_ACCEPT_ENCODING_BZIP2: + return stream_bzip2_flush(srv, con, hctx, end); +#endif + default: + UNUSED(srv); + UNUSED(con); + UNUSED(end); + return -1; + } +} + +static void mod_deflate_note_ratio(server *srv, connection *con, handler_ctx *hctx) { + /* store compression ratio in environment + * for possible logging by mod_accesslog + * (late in response handling, so not seen by most other modules) */ + /*(should be called only at end of successful response compression)*/ + char ratio[LI_ITOSTRING_LENGTH]; + if (0 == hctx->bytes_in) return; + li_itostrn(ratio, sizeof(ratio), hctx->bytes_out * 100 / hctx->bytes_in); + http_header_env_set(con, CONST_STR_LEN("ratio"), ratio, strlen(ratio)); + UNUSED(srv); +} + +static int mod_deflate_stream_end(server *srv, handler_ctx *hctx) { + switch(hctx->compression_type) { +#ifdef USE_ZLIB + case HTTP_ACCEPT_ENCODING_GZIP: + case HTTP_ACCEPT_ENCODING_DEFLATE: + return stream_deflate_end(srv, hctx); +#endif +#ifdef USE_BZ2LIB + case HTTP_ACCEPT_ENCODING_BZIP2: + return stream_bzip2_end(srv, hctx); +#endif + default: + UNUSED(srv); + return -1; + } +} + +static void deflate_compress_cleanup(server *srv, connection *con, handler_ctx *hctx) { + const plugin_data *p = hctx->plugin_data; + con->plugin_ctx[p->id] = NULL; + + if (0 != mod_deflate_stream_end(srv, hctx)) { + log_error_write(srv, __FILE__, __LINE__, "s", "error closing stream"); + } + + #if 1 /* unnecessary if deflate.min-compress-size is set to a reasonable value */ + if (hctx->bytes_in < hctx->bytes_out) { + log_error_write(srv, __FILE__, __LINE__, "sbsdsd", + "uri ", con->uri.path_raw, " in=", hctx->bytes_in, " smaller than out=", hctx->bytes_out); + } + #endif + + handler_ctx_free(hctx); +} + + +static int mod_deflate_file_chunk(server *srv, connection *con, handler_ctx *hctx, chunk *c, off_t st_size) { + off_t abs_offset; + off_t toSend = -1; + char *start; +#ifdef USE_MMAP + off_t we_want_to_mmap = 2 MByte; + off_t we_want_to_send = st_size; + volatile int mapped = 0;/* quiet warning: might be clobbered by 'longjmp' */ +#else + start = NULL; +#endif + + if (-1 == c->file.fd) { /* open the file if not already open */ + if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, O_RDONLY, 0))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", "open failed for:", c->mem, strerror(errno)); + + return -1; + } + } + + abs_offset = c->file.start + c->offset; + +#ifdef USE_MMAP + /* mmap the buffer + * - first mmap + * - new mmap as the we are at the end of the last one */ + if (c->file.mmap.start == MAP_FAILED || + abs_offset == (off_t)(c->file.mmap.offset + c->file.mmap.length)) { + + /* Optimizations for the future: + * + * adaptive mem-mapping + * the problem: + * we mmap() the whole file. If someone has alot large files and 32bit + * machine the virtual address area will be unrun and we will have a failing + * mmap() call. + * solution: + * only mmap 16M in one chunk and move the window as soon as we have finished + * the first 8M + * + * read-ahead buffering + * the problem: + * sending out several large files in parallel trashes the read-ahead of the + * kernel leading to long wait-for-seek times. + * solutions: (increasing complexity) + * 1. use madvise + * 2. use a internal read-ahead buffer in the chunk-structure + * 3. use non-blocking IO for file-transfers + * */ + + /* all mmap()ed areas are 512kb expect the last which might be smaller */ + off_t to_mmap; + + /* this is a remap, move the mmap-offset */ + if (c->file.mmap.start != MAP_FAILED) { + munmap(c->file.mmap.start, c->file.mmap.length); + c->file.mmap.offset += we_want_to_mmap; + } else { + /* in case the range-offset is after the first mmap()ed area we skip the area */ + c->file.mmap.offset = 0; + + while (c->file.mmap.offset + we_want_to_mmap < c->file.start) { + c->file.mmap.offset += we_want_to_mmap; + } + } + + /* length is rel, c->offset too, assume there is no limit at the mmap-boundaries */ + to_mmap = (c->file.start + c->file.length) - c->file.mmap.offset; + if (to_mmap > we_want_to_mmap) to_mmap = we_want_to_mmap; + /* we have more to send than we can mmap() at once */ + if (we_want_to_send > to_mmap) we_want_to_send = to_mmap; + + if (MAP_FAILED == (c->file.mmap.start = mmap(0, (size_t)to_mmap, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset)) + && (errno != EINVAL || MAP_FAILED == (c->file.mmap.start = mmap(0, (size_t)to_mmap, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset)))) { + log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed:", + strerror(errno), c->mem, c->file.fd); + + return -1; + } + + c->file.mmap.length = to_mmap; +#ifdef HAVE_MADVISE + /* don't advise files < 64Kb */ + if (c->file.mmap.length > (64 KByte) && + 0 != madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED)) { + log_error_write(srv, __FILE__, __LINE__, "ssbd", "madvise failed:", + strerror(errno), c->mem, c->file.fd); + } +#endif + + /* chunk_reset() or chunk_free() will cleanup for us */ + } + + /* to_send = abs_mmap_end - abs_offset */ + toSend = (c->file.mmap.offset + c->file.mmap.length) - abs_offset; + if (toSend > we_want_to_send) toSend = we_want_to_send; + + if (toSend < 0) { + log_error_write(srv, __FILE__, __LINE__, "soooo", + "toSend is negative:", + toSend, + c->file.mmap.length, + abs_offset, + c->file.mmap.offset); + force_assert(toSend < 0); + } + + start = c->file.mmap.start; + mapped = 1; +#endif + + if (MAP_FAILED == c->file.mmap.start) { + toSend = st_size; + if (toSend > 2 MByte) toSend = 2 MByte; + if (NULL == (start = malloc((size_t)toSend)) || -1 == lseek(c->file.fd, abs_offset, SEEK_SET) || toSend != read(c->file.fd, start, (size_t)toSend)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "reading", c->mem, "failed:", strerror(errno)); + + free(start); + return -1; + } + abs_offset = 0; + } + +#ifdef USE_MMAP + if (mapped) { + signal(SIGBUS, sigbus_handler); + sigbus_jmp_valid = 1; + if (0 != sigsetjmp(sigbus_jmp, 1)) { + sigbus_jmp_valid = 0; + + log_error_write(srv, __FILE__, __LINE__, "sbd", "SIGBUS in mmap:", + c->mem, c->file.fd); + return -1; + } + } +#endif + + if (mod_deflate_compress(srv, con, hctx, + (unsigned char *)start + (abs_offset - c->file.mmap.offset), toSend) < 0) { + log_error_write(srv, __FILE__, __LINE__, "s", + "compress failed."); + toSend = -1; + } + +#ifdef USE_MMAP + if (mapped) + sigbus_jmp_valid = 0; + else +#endif + free(start); + + return toSend; +} + + +static handler_t deflate_compress_response(server *srv, connection *con, handler_ctx *hctx) { + off_t len, max; + int close_stream; + + /* move all chunk from write_queue into our in_queue, then adjust + * counters since con->write_queue is reused for compressed output */ + len = chunkqueue_length(con->write_queue); + chunkqueue_remove_finished_chunks(con->write_queue); + chunkqueue_append_chunkqueue(hctx->in_queue, con->write_queue); + con->write_queue->bytes_in -= len; + con->write_queue->bytes_out -= len; + + max = chunkqueue_length(hctx->in_queue); + #if 0 + /* calculate max bytes to compress for this call */ + if (p->conf.sync_flush && max > (len = p->conf.work_block_size << 10)) { + max = len; + } + #endif + + /* Compress chunks from in_queue into chunks for write_queue */ + while (max) { + chunk *c = hctx->in_queue->first; + + switch(c->type) { + case MEM_CHUNK: + len = buffer_string_length(c->mem) - c->offset; + if (len > max) len = max; + if (mod_deflate_compress(srv, con, hctx, (unsigned char *)c->mem->ptr+c->offset, len) < 0) { + log_error_write(srv, __FILE__, __LINE__, "s", + "compress failed."); + return HANDLER_ERROR; + } + break; + case FILE_CHUNK: + len = c->file.length - c->offset; + if (len > max) len = max; + if ((len = mod_deflate_file_chunk(srv, con, hctx, c, len)) < 0) { + log_error_write(srv, __FILE__, __LINE__, "s", + "compress file chunk failed."); + return HANDLER_ERROR; + } + break; + default: + log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); + return HANDLER_ERROR; + } + + max -= len; + chunkqueue_mark_written(hctx->in_queue, len); + } + + /*(currently should always be true)*/ + /*(current implementation requires response be complete)*/ + close_stream = (con->file_finished && chunkqueue_is_empty(hctx->in_queue)); + if (mod_deflate_stream_flush(srv, con, hctx, close_stream) < 0) { + log_error_write(srv, __FILE__, __LINE__, "s", "flush error"); + return HANDLER_ERROR; + } + + return close_stream ? HANDLER_FINISHED : HANDLER_GO_ON; +} + + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_deflate_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(mimetypes); + PATCH(allowed_encodings); + PATCH(max_compress_size); + PATCH(min_compress_size); + PATCH(compression_level); + PATCH(output_buffer_size); + PATCH(work_block_size); + PATCH(max_loadavg); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.mimetypes"))) { + PATCH(mimetypes); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.allowed-encodings"))) { + PATCH(allowed_encodings); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.max-compress-size"))) { + PATCH(max_compress_size); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.min-compress-size"))) { + PATCH(min_compress_size); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.compression-level"))) { + PATCH(compression_level); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.output-buffer-size"))) { + PATCH(output_buffer_size); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.work-block-size"))) { + PATCH(work_block_size); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("deflate.max-loadavg"))) { + PATCH(max_loadavg); + } + } + } + + return 0; +} +#undef PATCH + +static int mod_deflate_choose_encoding (const char *value, plugin_data *p, const char **label) { + /* get client side support encodings */ + int accept_encoding = 0; +#if !defined(USE_ZLIB) && !defined(USE_BZ2LIB) + UNUSED(value); +#endif +#ifdef USE_ZLIB + if (NULL != strstr(value, "gzip")) accept_encoding |= HTTP_ACCEPT_ENCODING_GZIP; + else if (NULL != strstr(value, "x-gzip")) accept_encoding |= HTTP_ACCEPT_ENCODING_X_GZIP; + if (NULL != strstr(value, "deflate")) accept_encoding |= HTTP_ACCEPT_ENCODING_DEFLATE; +#endif + /* if (NULL != strstr(value, "compress")) accept_encoding |= HTTP_ACCEPT_ENCODING_COMPRESS; */ +#ifdef USE_BZ2LIB + if (p->conf.allowed_encodings & (HTTP_ACCEPT_ENCODING_BZIP2 | HTTP_ACCEPT_ENCODING_X_BZIP2)) { + if (NULL != strstr(value, "bzip2")) accept_encoding |= HTTP_ACCEPT_ENCODING_BZIP2; + else if (NULL != strstr(value, "x-bzip2")) accept_encoding |= HTTP_ACCEPT_ENCODING_X_BZIP2; + } +#endif + /* if (NULL != strstr(value, "identity")) accept_encoding |= HTTP_ACCEPT_ENCODING_IDENTITY; */ + + /* mask to limit to allowed_encodings */ + accept_encoding &= p->conf.allowed_encodings; + + /* select best matching encoding */ +#ifdef USE_BZ2LIB + if (accept_encoding & HTTP_ACCEPT_ENCODING_BZIP2) { + *label = "bzip2"; + return HTTP_ACCEPT_ENCODING_BZIP2; + } else if (accept_encoding & HTTP_ACCEPT_ENCODING_X_BZIP2) { + *label = "x-bzip2"; + return HTTP_ACCEPT_ENCODING_BZIP2; + } else +#endif + if (accept_encoding & HTTP_ACCEPT_ENCODING_GZIP) { + *label = "gzip"; + return HTTP_ACCEPT_ENCODING_GZIP; + } else if (accept_encoding & HTTP_ACCEPT_ENCODING_X_GZIP) { + *label = "x-gzip"; + return HTTP_ACCEPT_ENCODING_GZIP; + } else if (accept_encoding & HTTP_ACCEPT_ENCODING_DEFLATE) { + *label = "deflate"; + return HTTP_ACCEPT_ENCODING_DEFLATE; + } else { + return 0; + } +} + +CONNECTION_FUNC(mod_deflate_handle_response_start) { + plugin_data *p = p_d; + buffer *vb; + handler_ctx *hctx; + const char *label; + off_t len; + size_t etaglen = 0; + int compression_type; + handler_t rc; + + /*(current implementation requires response be complete)*/ + if (!con->file_finished) return HANDLER_GO_ON; + if (con->request.http_method == HTTP_METHOD_HEAD) return HANDLER_GO_ON; + if (con->response.htags & HTTP_HEADER_TRANSFER_ENCODING) return HANDLER_GO_ON; + + /* disable compression for some http status types. */ + switch(con->http_status) { + case 100: + case 101: + case 204: + case 205: + case 304: + /* disable compression as we have no response entity */ + return HANDLER_GO_ON; + default: + break; + } + + mod_deflate_patch_connection(srv, con, p); + + /* check if deflate configured for any mimetypes */ + if (!p->conf.mimetypes->used) return HANDLER_GO_ON; + + /* check if size of response is below min-compress-size or exceeds max*/ + /* (con->file_finished checked at top of routine) */ + len = chunkqueue_length(con->write_queue); + if (len <= (off_t)p->conf.min_compress_size) return HANDLER_GO_ON; + if (p->conf.max_compress_size /*(max_compress_size in KB)*/ + && len > ((off_t)p->conf.max_compress_size << 10)) { + return HANDLER_GO_ON; + } + + /* Check if response has a Content-Encoding. */ + vb = http_header_response_get(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding")); + if (NULL != vb) return HANDLER_GO_ON; + + /* Check Accept-Encoding for supported encoding. */ + vb = http_header_request_get(con, HTTP_HEADER_ACCEPT_ENCODING, CONST_STR_LEN("Accept-Encoding")); + if (NULL == vb) return HANDLER_GO_ON; + + /* find matching encodings */ + compression_type = mod_deflate_choose_encoding(vb->ptr, p, &label); + if (!compression_type) return HANDLER_GO_ON; + + /* Check mimetype in response header "Content-Type" */ + if (NULL != (vb = http_header_response_get(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type")))) { + if (NULL == array_match_value_prefix(p->conf.mimetypes, vb)) return HANDLER_GO_ON; + } else { + /* If no Content-Type set, compress only if first p->conf.mimetypes value is "" */ + data_string *mimetype = (data_string *)p->conf.mimetypes->data[0]; + if (!buffer_string_is_empty(mimetype->value)) return HANDLER_GO_ON; + } + + /* Vary: Accept-Encoding (response might change according to request Accept-Encoding) */ + if (NULL != (vb = http_header_response_get(con, HTTP_HEADER_VARY, CONST_STR_LEN("Vary")))) { + if (NULL == strstr(vb->ptr, "Accept-Encoding")) { + buffer_append_string_len(vb, CONST_STR_LEN(",Accept-Encoding")); + } + } else { + http_header_response_append(con, HTTP_HEADER_VARY, + CONST_STR_LEN("Vary"), + CONST_STR_LEN("Accept-Encoding")); + } + + /* check ETag as is done in http_response_handle_cachable() + * (slightly imperfect (close enough?) match of ETag "000000" to "000000-gzip") */ + vb = http_header_response_get(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag")); + if (NULL != vb && (con->request.htags & HTTP_HEADER_IF_NONE_MATCH)) { + buffer *if_none_match = http_header_response_get(con, HTTP_HEADER_IF_NONE_MATCH, CONST_STR_LEN("If-None-Match")); + etaglen = buffer_string_length(vb); + if (etaglen + && con->http_status < 300 /*(want 2xx only)*/ + && NULL != if_none_match + && 0 == strncmp(if_none_match->ptr, vb->ptr, etaglen-1) + && if_none_match->ptr[etaglen-1] == '-' + && 0 == strncmp(if_none_match->ptr+etaglen, label, strlen(label))) { + + if ( HTTP_METHOD_GET == con->request.http_method + || HTTP_METHOD_HEAD == con->request.http_method) { + /* modify ETag response header in-place to remove '"' and append '-label"' */ + vb->ptr[etaglen-1] = '-'; /*(overwrite end '"')*/ + buffer_append_string(vb, label); + buffer_append_string_len(vb, CONST_STR_LEN("\"")); + /*buffer_copy_buffer(con->physical.etag, vb);*//*(keep in sync?)*/ + con->http_status = 304; + } else { + con->http_status = 412; + } + + /* response_start hook occurs after error docs have been handled. + * For now, send back empty response body. + * In the future, might extract the error doc code so that it + * might be run again if response_start hooks return with + * changed http_status and con->mode = DIRECT */ + /* clear content length even if 304 since compressed length unknown */ + http_response_body_clear(con, 0); + + con->file_finished = 1; + con->mode = DIRECT; + return HANDLER_GO_ON; + } + } + + if (0.0 < p->conf.max_loadavg && p->conf.max_loadavg < srv->srvconf.loadavg[0]) { + return HANDLER_GO_ON; + } + + /* update ETag, if ETag response header is set */ + if (etaglen) { + /* modify ETag response header in-place to remove '"' and append '-label"' */ + vb->ptr[etaglen-1] = '-'; /*(overwrite end '"')*/ + buffer_append_string(vb, label); + buffer_append_string_len(vb, CONST_STR_LEN("\"")); + /*buffer_copy_buffer(con->physical.etag, vb);*//*(keep in sync?)*/ + } + + /* set Content-Encoding to show selected compression type */ + http_header_response_set(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding"), label, strlen(label)); + + /* clear Content-Length and con->write_queue if HTTP HEAD request + * (alternatively, could return original Content-Length with HEAD + * request if ETag not modified and Content-Encoding not added) */ + if (HTTP_METHOD_HEAD == con->request.http_method) { + /* ensure that uncompressed Content-Length is not sent in HEAD response */ + http_response_body_clear(con, 0); + return HANDLER_GO_ON; + } + + /* future: might use ETag to check if compressed content is in compressed file cache */ + /*if (etaglen) { ... } *//* return if in file cache after updating con->write_queue */ + + /* enable compression */ + p->conf.sync_flush = + (con->conf.stream_response_body && 0 == p->conf.output_buffer_size); + hctx = handler_ctx_init(); + hctx->plugin_data = p; + hctx->compression_type = compression_type; + /* setup output buffer */ + buffer_clear(p->tmp_buf); + hctx->output = p->tmp_buf; + if (0 != mod_deflate_stream_init(hctx)) { + /*(should not happen unless ENOMEM)*/ + handler_ctx_free(hctx); + log_error_write(srv, __FILE__, __LINE__, "ss", + "Failed to initialize compression", label); + /* restore prior Etag and unset Content-Encoding */ + if (etaglen) { + vb->ptr[etaglen-1] = '"'; /*(overwrite '-')*/ + buffer_string_set_length(vb, etaglen); + } + http_header_response_unset(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding")); + return HANDLER_GO_ON; + } + + if (con->response.htags & HTTP_HEADER_CONTENT_LENGTH) { + http_header_response_unset(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); + } + con->plugin_ctx[p->id] = hctx; + + rc = deflate_compress_response(srv, con, hctx); + if (HANDLER_GO_ON != rc) { + if (HANDLER_FINISHED == rc) { + mod_deflate_note_ratio(srv, con, hctx); + } + deflate_compress_cleanup(srv, con, hctx); + if (HANDLER_ERROR == rc) return HANDLER_ERROR; + } + + return HANDLER_GO_ON; +} + +static handler_t mod_deflate_cleanup(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + + if (NULL != hctx) deflate_compress_cleanup(srv, con, hctx); + + return HANDLER_GO_ON; +} + +int mod_deflate_plugin_init(plugin *p); +int mod_deflate_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("deflate"); + + p->init = mod_deflate_init; + p->cleanup = mod_deflate_free; + p->set_defaults = mod_deflate_setdefaults; + p->connection_reset = mod_deflate_cleanup; + p->handle_response_start = mod_deflate_handle_response_start; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_dirlisting.c b/data/lighttpd/lighttpd-1.4.53/src/mod_dirlisting.c new file mode 100644 index 000000000..2f746740f --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_dirlisting.c @@ -0,0 +1,1216 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "http_header.h" + +#include "plugin.h" + +#include "stat_cache.h" + +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <time.h> + +#ifdef HAVE_PCRE_H +#include <pcre.h> +#endif + +/** + * this is a dirlisting for a lighttpd plugin + */ + +#ifdef HAVE_ATTR_ATTRIBUTES_H +#include <attr/attributes.h> +#endif + +#ifdef HAVE_SYS_EXTATTR_H +#include <sys/extattr.h> +#endif + +/* plugin config for all request/connections */ + +typedef struct { +#ifdef HAVE_PCRE_H + pcre *regex; +#endif + buffer *string; +} excludes; + +typedef struct { + excludes **ptr; + + size_t used; + size_t size; +} excludes_buffer; + +typedef struct { + unsigned short dir_listing; + unsigned short hide_dot_files; + unsigned short hide_readme_file; + unsigned short encode_readme; + unsigned short hide_header_file; + unsigned short encode_header; + unsigned short auto_layout; + + excludes_buffer *excludes; + + buffer *show_readme; + buffer *show_header; + buffer *external_css; + buffer *external_js; + buffer *encoding; + buffer *set_footer; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + buffer *tmp_buf; + buffer *content_charset; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +static excludes_buffer *excludes_buffer_init(void) { + excludes_buffer *exb; + + exb = calloc(1, sizeof(*exb)); + + return exb; +} + +#ifdef HAVE_PCRE_H +static int excludes_buffer_append(excludes_buffer *exb, buffer *string) { + size_t i; + const char *errptr; + int erroff; + + if (!string) return -1; + + if (exb->size == 0) { + exb->size = 4; + exb->used = 0; + + exb->ptr = malloc(exb->size * sizeof(*exb->ptr)); + + for(i = 0; i < exb->size ; i++) { + exb->ptr[i] = calloc(1, sizeof(**exb->ptr)); + } + } else if (exb->used == exb->size) { + exb->size += 4; + + exb->ptr = realloc(exb->ptr, exb->size * sizeof(*exb->ptr)); + + for(i = exb->used; i < exb->size; i++) { + exb->ptr[i] = calloc(1, sizeof(**exb->ptr)); + } + } + + + if (NULL == (exb->ptr[exb->used]->regex = pcre_compile(string->ptr, 0, + &errptr, &erroff, NULL))) { + return -1; + } + + exb->ptr[exb->used]->string = buffer_init(); + buffer_copy_buffer(exb->ptr[exb->used]->string, string); + + exb->used++; + + return 0; +} +#endif + +static void excludes_buffer_free(excludes_buffer *exb) { +#ifdef HAVE_PCRE_H + size_t i; + + for (i = 0; i < exb->size; i++) { + if (exb->ptr[i]->regex) pcre_free(exb->ptr[i]->regex); + if (exb->ptr[i]->string) buffer_free(exb->ptr[i]->string); + free(exb->ptr[i]); + } + + if (exb->ptr) free(exb->ptr); +#endif + + free(exb); +} + +/* init the plugin data */ +INIT_FUNC(mod_dirlisting_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->tmp_buf = buffer_init(); + p->content_charset = buffer_init(); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_dirlisting_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (!s) continue; + + excludes_buffer_free(s->excludes); + buffer_free(s->show_readme); + buffer_free(s->show_header); + buffer_free(s->external_css); + buffer_free(s->external_js); + buffer_free(s->encoding); + buffer_free(s->set_footer); + + free(s); + } + free(p->config_storage); + } + + buffer_free(p->tmp_buf); + buffer_free(p->content_charset); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +#define CONFIG_EXCLUDE "dir-listing.exclude" +#define CONFIG_ACTIVATE "dir-listing.activate" +#define CONFIG_HIDE_DOTFILES "dir-listing.hide-dotfiles" +#define CONFIG_EXTERNAL_CSS "dir-listing.external-css" +#define CONFIG_EXTERNAL_JS "dir-listing.external-js" +#define CONFIG_ENCODING "dir-listing.encoding" +#define CONFIG_SHOW_README "dir-listing.show-readme" +#define CONFIG_HIDE_README_FILE "dir-listing.hide-readme-file" +#define CONFIG_SHOW_HEADER "dir-listing.show-header" +#define CONFIG_HIDE_HEADER_FILE "dir-listing.hide-header-file" +#define CONFIG_DIR_LISTING "server.dir-listing" +#define CONFIG_SET_FOOTER "dir-listing.set-footer" +#define CONFIG_ENCODE_README "dir-listing.encode-readme" +#define CONFIG_ENCODE_HEADER "dir-listing.encode-header" +#define CONFIG_AUTO_LAYOUT "dir-listing.auto-layout" + + +SETDEFAULTS_FUNC(mod_dirlisting_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { CONFIG_EXCLUDE, NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { CONFIG_ACTIVATE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { CONFIG_HIDE_DOTFILES, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { CONFIG_EXTERNAL_CSS, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { CONFIG_ENCODING, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { CONFIG_SHOW_README, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { CONFIG_HIDE_README_FILE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ + { CONFIG_SHOW_HEADER, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ + { CONFIG_HIDE_HEADER_FILE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ + { CONFIG_DIR_LISTING, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ + { CONFIG_SET_FOOTER, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ + { CONFIG_ENCODE_README, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ + { CONFIG_ENCODE_HEADER, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ + { CONFIG_AUTO_LAYOUT, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ + { CONFIG_EXTERNAL_JS, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ + + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + data_unset *du_excludes; + + s = calloc(1, sizeof(plugin_config)); + s->excludes = excludes_buffer_init(); + s->dir_listing = 0; + s->show_readme = buffer_init(); + s->show_header = buffer_init(); + s->external_css = buffer_init(); + s->external_js = buffer_init(); + s->hide_dot_files = 1; + s->hide_readme_file = 0; + s->hide_header_file = 0; + s->encode_readme = 1; + s->encode_header = 1; + s->auto_layout = 1; + + s->encoding = buffer_init(); + s->set_footer = buffer_init(); + + cv[0].destination = s->excludes; + cv[1].destination = &(s->dir_listing); + cv[2].destination = &(s->hide_dot_files); + cv[3].destination = s->external_css; + cv[4].destination = s->encoding; + cv[5].destination = s->show_readme; + cv[6].destination = &(s->hide_readme_file); + cv[7].destination = s->show_header; + cv[8].destination = &(s->hide_header_file); + cv[9].destination = &(s->dir_listing); /* old name */ + cv[10].destination = s->set_footer; + cv[11].destination = &(s->encode_readme); + cv[12].destination = &(s->encode_header); + cv[13].destination = &(s->auto_layout); + cv[14].destination = s->external_js; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (NULL != (du_excludes = array_get_element(config->value, CONFIG_EXCLUDE))) { + array *excludes_list; + + excludes_list = ((data_array*)du_excludes)->value; + + if (du_excludes->type != TYPE_ARRAY || !array_is_vlist(excludes_list)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected type for " CONFIG_EXCLUDE "; expected list of \"regex\""); + return HANDLER_ERROR; + } + +#ifndef HAVE_PCRE_H + if (excludes_list->used > 0) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "pcre support is missing for: ", CONFIG_EXCLUDE, ", please install libpcre and the headers"); + return HANDLER_ERROR; + } +#else + for (size_t j = 0; j < excludes_list->used; ++j) { + data_unset *du_exclude = excludes_list->data[j]; + + if (du_exclude->type != TYPE_STRING) { + log_error_write(srv, __FILE__, __LINE__, "sssbs", + "unexpected type for key: ", CONFIG_EXCLUDE, "[", + du_exclude->key, "](string)"); + return HANDLER_ERROR; + } + + if (0 != excludes_buffer_append(s->excludes, ((data_string*)(du_exclude))->value)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "pcre-compile failed for", ((data_string*)(du_exclude))->value); + return HANDLER_ERROR; + } + } +#endif + } + + if (!buffer_string_is_empty(s->show_readme)) { + if (buffer_is_equal_string(s->show_readme, CONST_STR_LEN("enable"))) { + buffer_copy_string_len(s->show_readme, CONST_STR_LEN("README.txt")); + } + else if (buffer_is_equal_string(s->show_readme, CONST_STR_LEN("disable"))) { + buffer_clear(s->show_readme); + } + } + + if (!buffer_string_is_empty(s->show_header)) { + if (buffer_is_equal_string(s->show_header, CONST_STR_LEN("enable"))) { + buffer_copy_string_len(s->show_header, CONST_STR_LEN("HEADER.txt")); + } + else if (buffer_is_equal_string(s->show_header, CONST_STR_LEN("disable"))) { + buffer_clear(s->show_header); + } + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_dirlisting_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(dir_listing); + PATCH(external_css); + PATCH(external_js); + PATCH(hide_dot_files); + PATCH(encoding); + PATCH(show_readme); + PATCH(hide_readme_file); + PATCH(show_header); + PATCH(hide_header_file); + PATCH(excludes); + PATCH(set_footer); + PATCH(encode_readme); + PATCH(encode_header); + PATCH(auto_layout); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ACTIVATE)) || + buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DIR_LISTING))) { + PATCH(dir_listing); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_DOTFILES))) { + PATCH(hide_dot_files); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXTERNAL_CSS))) { + PATCH(external_css); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXTERNAL_JS))) { + PATCH(external_js); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODING))) { + PATCH(encoding); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SHOW_README))) { + PATCH(show_readme); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_README_FILE))) { + PATCH(hide_readme_file); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SHOW_HEADER))) { + PATCH(show_header); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_HEADER_FILE))) { + PATCH(hide_header_file); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SET_FOOTER))) { + PATCH(set_footer); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXCLUDE))) { + PATCH(excludes); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODE_README))) { + PATCH(encode_readme); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODE_HEADER))) { + PATCH(encode_header); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_AUTO_LAYOUT))) { + PATCH(auto_layout); + } + } + } + + return 0; +} +#undef PATCH + +typedef struct { + size_t namelen; + time_t mtime; + off_t size; +} dirls_entry_t; + +typedef struct { + dirls_entry_t **ent; + size_t used; + size_t size; +} dirls_list_t; + +#define DIRLIST_ENT_NAME(ent) ((char*)(ent) + sizeof(dirls_entry_t)) +#define DIRLIST_BLOB_SIZE 16 + +/* simple combsort algorithm */ +static void http_dirls_sort(dirls_entry_t **ent, int num) { + int gap = num; + int i, j; + int swapped; + dirls_entry_t *tmp; + + do { + gap = (gap * 10) / 13; + if (gap == 9 || gap == 10) + gap = 11; + if (gap < 1) + gap = 1; + swapped = 0; + + for (i = 0; i < num - gap; i++) { + j = i + gap; + if (strcmp(DIRLIST_ENT_NAME(ent[i]), DIRLIST_ENT_NAME(ent[j])) > 0) { + tmp = ent[i]; + ent[i] = ent[j]; + ent[j] = tmp; + swapped = 1; + } + } + + } while (gap > 1 || swapped); +} + +/* buffer must be able to hold "999.9K" + * conversion is simple but not perfect + */ +static int http_list_directory_sizefmt(char *buf, size_t bufsz, off_t size) { + const char unit[] = " KMGTPE"; /* Kilo, Mega, Giga, Tera, Peta, Exa */ + const char *u = unit; /* u will always increment at least once */ + int remain; + size_t buflen; + + if (size < 100) + size += 99; + if (size < 100) + size = 0; + + while (1) { + remain = (int) size & 1023; + size >>= 10; + u++; + if ((size & (~0 ^ 1023)) == 0) + break; + } + + remain /= 100; + if (remain > 9) + remain = 9; + if (size > 999) { + size = 0; + remain = 9; + u++; + } + + li_itostrn(buf, bufsz, size); + buflen = strlen(buf); + if (buflen + 3 >= bufsz) return buflen; + buf[buflen+0] = '.'; + buf[buflen+1] = remain + '0'; + buf[buflen+2] = *u; + buf[buflen+3] = '\0'; + + return buflen + 3; +} + +/* don't want to block when open()ing a fifo */ +#if defined(O_NONBLOCK) +# define FIFO_NONBLOCK O_NONBLOCK +#else +# define FIFO_NONBLOCK 0 +#endif + +static void http_list_directory_include_file(buffer *out, buffer *path, const char *classname, int encode) { + int fd = open(path->ptr, O_RDONLY | FIFO_NONBLOCK); + ssize_t rd; + char buf[8192]; + + if (-1 == fd) return; + + if (encode) { + buffer_append_string_len(out, CONST_STR_LEN("<pre class=\"")); + buffer_append_string(out, classname); + buffer_append_string_len(out, CONST_STR_LEN("\">")); + } + + while ((rd = read(fd, buf, sizeof(buf))) > 0) { + if (encode) { + buffer_append_string_encoded(out, buf, (size_t)rd, ENCODING_MINIMAL_XML); + } else { + buffer_append_string_len(out, buf, (size_t)rd); + } + } + close(fd); + + if (encode) { + buffer_append_string_len(out, CONST_STR_LEN("</pre>")); + } +} + +/* portions copied from mod_status + * modified and specialized for stable dirlist sorting by name */ +static const char js_simple_table_resort[] = \ +"var click_column;\n" \ +"var name_column = 0;\n" \ +"var date_column = 1;\n" \ +"var size_column = 2;\n" \ +"var type_column = 3;\n" \ +"var prev_span = null;\n" \ +"\n" \ +"if (typeof(String.prototype.localeCompare) === 'undefined') {\n" \ +" String.prototype.localeCompare = function(str, locale, options) {\n" \ +" return ((this == str) ? 0 : ((this > str) ? 1 : -1));\n" \ +" };\n" \ +"}\n" \ +"\n" \ +"if (typeof(String.prototype.toLocaleUpperCase) === 'undefined') {\n" \ +" String.prototype.toLocaleUpperCase = function() {\n" \ +" return this.toUpperCase();\n" \ +" };\n" \ +"}\n" \ +"\n" \ +"function get_inner_text(el) {\n" \ +" if((typeof el == 'string')||(typeof el == 'undefined'))\n" \ +" return el;\n" \ +" if(el.innerText)\n" \ +" return el.innerText;\n" \ +" else {\n" \ +" var str = \"\";\n" \ +" var cs = el.childNodes;\n" \ +" var l = cs.length;\n" \ +" for (i=0;i<l;i++) {\n" \ +" if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n" \ +" else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n" \ +" }\n" \ +" }\n" \ +" return str;\n" \ +"}\n" \ +"\n" \ +"function isdigit(c) {\n" \ +" return (c >= '0' && c <= '9');\n" \ +"}\n" \ +"\n" \ +"function unit_multiplier(unit) {\n" \ +" return (unit=='K') ? 1000\n" \ +" : (unit=='M') ? 1000000\n" \ +" : (unit=='G') ? 1000000000\n" \ +" : (unit=='T') ? 1000000000000\n" \ +" : (unit=='P') ? 1000000000000000\n" \ +" : (unit=='E') ? 1000000000000000000 : 1;\n" \ +"}\n" \ +"\n" \ +"var li_date_regex=/(\\d{4})-(\\w{3})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})/;\n" \ +"\n" \ +"var li_mon = ['Jan','Feb','Mar','Apr','May','Jun',\n" \ +" 'Jul','Aug','Sep','Oct','Nov','Dec'];\n" \ +"\n" \ +"function li_mon_num(mon) {\n" \ +" var i; for (i = 0; i < 12 && mon != li_mon[i]; ++i); return i;\n" \ +"}\n" \ +"\n" \ +"function li_date_cmp(s1, s2) {\n" \ +" var dp1 = li_date_regex.exec(s1)\n" \ +" var dp2 = li_date_regex.exec(s2)\n" \ +" for (var i = 1; i < 7; ++i) {\n" \ +" var cmp = (2 != i)\n" \ +" ? parseInt(dp1[i]) - parseInt(dp2[i])\n" \ +" : li_mon_num(dp1[2]) - li_mon_num(dp2[2]);\n" \ +" if (0 != cmp) return cmp;\n" \ +" }\n" \ +" return 0;\n" \ +"}\n" \ +"\n" \ +"function sortfn_then_by_name(a,b,sort_column) {\n" \ +" if (sort_column == name_column || sort_column == type_column) {\n" \ +" var ad = (a.cells[type_column].innerHTML === 'Directory');\n" \ +" var bd = (b.cells[type_column].innerHTML === 'Directory');\n" \ +" if (ad != bd) return (ad ? -1 : 1);\n" \ +" }\n" \ +" var at = get_inner_text(a.cells[sort_column]);\n" \ +" var bt = get_inner_text(b.cells[sort_column]);\n" \ +" var cmp;\n" \ +" if (sort_column == name_column) {\n" \ +" if (at == '..') return -1;\n" \ +" if (bt == '..') return 1;\n" \ +" }\n" \ +" if (a.cells[sort_column].className == 'int') {\n" \ +" cmp = parseInt(at)-parseInt(bt);\n" \ +" } else if (sort_column == date_column) {\n" \ +" var ad = isdigit(at.substr(0,1));\n" \ +" var bd = isdigit(bt.substr(0,1));\n" \ +" if (ad != bd) return (!ad ? -1 : 1);\n" \ +" cmp = li_date_cmp(at,bt);\n" \ +" } else if (sort_column == size_column) {\n" \ +" var ai = parseInt(at, 10) * unit_multiplier(at.substr(-1,1));\n" \ +" var bi = parseInt(bt, 10) * unit_multiplier(bt.substr(-1,1));\n" \ +" if (at.substr(0,1) == '-') ai = -1;\n" \ +" if (bt.substr(0,1) == '-') bi = -1;\n" \ +" cmp = ai - bi;\n" \ +" } else {\n" \ +" cmp = at.toLocaleUpperCase().localeCompare(bt.toLocaleUpperCase());\n" \ +" if (0 != cmp) return cmp;\n" \ +" cmp = at.localeCompare(bt);\n" \ +" }\n" \ +" if (0 != cmp || sort_column == name_column) return cmp;\n" \ +" return sortfn_then_by_name(a,b,name_column);\n" \ +"}\n" \ +"\n" \ +"function sortfn(a,b) {\n" \ +" return sortfn_then_by_name(a,b,click_column);\n" \ +"}\n" \ +"\n" \ +"function resort(lnk) {\n" \ +" var span = lnk.childNodes[1];\n" \ +" var table = lnk.parentNode.parentNode.parentNode.parentNode;\n" \ +" var rows = new Array();\n" \ +" for (j=1;j<table.rows.length;j++)\n" \ +" rows[j-1] = table.rows[j];\n" \ +" click_column = lnk.parentNode.cellIndex;\n" \ +" rows.sort(sortfn);\n" \ +"\n" \ +" if (prev_span != null) prev_span.innerHTML = '';\n" \ +" if (span.getAttribute('sortdir')=='down') {\n" \ +" span.innerHTML = '↑';\n" \ +" span.setAttribute('sortdir','up');\n" \ +" rows.reverse();\n" \ +" } else {\n" \ +" span.innerHTML = '↓';\n" \ +" span.setAttribute('sortdir','down');\n" \ +" }\n" \ +" for (i=0;i<rows.length;i++)\n" \ +" table.tBodies[0].appendChild(rows[i]);\n" \ +" prev_span = span;\n" \ +"}\n"; + +/* portions copied from mod_dirlist (lighttpd2) */ +static const char js_simple_table_init_sort[] = \ +"\n" \ +"function init_sort(init_sort_column, ascending) {\n" \ +" var tables = document.getElementsByTagName(\"table\");\n" \ +" for (var i = 0; i < tables.length; i++) {\n" \ +" var table = tables[i];\n" \ +" //var c = table.getAttribute(\"class\")\n" \ +" //if (-1 != c.split(\" \").indexOf(\"sort\")) {\n" \ +" var row = table.rows[0].cells;\n" \ +" for (var j = 0; j < row.length; j++) {\n" \ +" var n = row[j];\n" \ +" if (n.childNodes.length == 1 && n.childNodes[0].nodeType == 3) {\n" \ +" var link = document.createElement(\"a\");\n" \ +" var title = n.childNodes[0].nodeValue.replace(/:$/, \"\");\n" \ +" link.appendChild(document.createTextNode(title));\n" \ +" link.setAttribute(\"href\", \"#\");\n" \ +" link.setAttribute(\"class\", \"sortheader\");\n" \ +" link.setAttribute(\"onclick\", \"resort(this);return false;\");\n" \ +" var arrow = document.createElement(\"span\");\n" \ +" arrow.setAttribute(\"class\", \"sortarrow\");\n" \ +" arrow.appendChild(document.createTextNode(\":\"));\n" \ +" link.appendChild(arrow)\n" \ +" n.replaceChild(link, n.firstChild);\n" \ +" }\n" \ +" }\n" \ +" var lnk = row[init_sort_column].firstChild;\n" \ +" if (ascending) {\n" \ +" var span = lnk.childNodes[1];\n" \ +" span.setAttribute('sortdir','down');\n" \ +" }\n" \ +" resort(lnk);\n" \ +" //}\n" \ +" }\n" \ +"}\n"; + +static void http_dirlist_append_js_table_resort (buffer *b, connection *con) { + char col = '0'; + char ascending = '0'; + if (!buffer_string_is_empty(con->uri.query)) { + const char *qs = con->uri.query->ptr; + do { + if (qs[0] == 'C' && qs[1] == '=') { + switch (qs[2]) { + case 'N': col = '0'; break; + case 'M': col = '1'; break; + case 'S': col = '2'; break; + case 'T': + case 'D': col = '3'; break; + default: break; + } + } + else if (qs[0] == 'O' && qs[1] == '=') { + switch (qs[2]) { + case 'A': ascending = '1'; break; + case 'D': ascending = '0'; break; + default: break; + } + } + } while ((qs = strchr(qs, '&')) && *++qs); + } + + buffer_append_string_len(b, CONST_STR_LEN("\n<script type=\"text/javascript\">\n// <!--\n\n")); + buffer_append_string_len(b, js_simple_table_resort, sizeof(js_simple_table_resort)-1); + buffer_append_string_len(b, js_simple_table_init_sort, sizeof(js_simple_table_init_sort)-1); + buffer_append_string_len(b, CONST_STR_LEN("\ninit_sort(")); + buffer_append_string_len(b, &col, 1); + buffer_append_string_len(b, CONST_STR_LEN(", ")); + buffer_append_string_len(b, &ascending, 1); + buffer_append_string_len(b, CONST_STR_LEN(");\n\n// -->\n</script>\n\n")); +} + +static void http_list_directory_header(server *srv, connection *con, plugin_data *p, buffer *out) { + UNUSED(srv); + + if (p->conf.auto_layout) { + buffer_append_string_len(out, CONST_STR_LEN( + "<!DOCTYPE html>\n" + "<html>\n" + "<head>\n" + )); + if (!buffer_string_is_empty(p->conf.encoding)) { + buffer_append_string_len(out, CONST_STR_LEN("<meta charset=\"")); + buffer_append_string_buffer(out, p->conf.encoding); + buffer_append_string_len(out, CONST_STR_LEN("\">\n")); + } + buffer_append_string_len(out, CONST_STR_LEN("<title>Index of ")); + buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); + buffer_append_string_len(out, CONST_STR_LEN("</title>\n")); + + if (!buffer_string_is_empty(p->conf.external_css)) { + buffer_append_string_len(out, CONST_STR_LEN("<meta name=\"viewport\" content=\"initial-scale=1\">")); + buffer_append_string_len(out, CONST_STR_LEN("<link rel=\"stylesheet\" type=\"text/css\" href=\"")); + buffer_append_string_buffer(out, p->conf.external_css); + buffer_append_string_len(out, CONST_STR_LEN("\">\n")); + } else { + buffer_append_string_len(out, CONST_STR_LEN( + "<style type=\"text/css\">\n" + "a, a:active {text-decoration: none; color: blue;}\n" + "a:visited {color: #48468F;}\n" + "a:hover, a:focus {text-decoration: underline; color: red;}\n" + "body {background-color: #F5F5F5;}\n" + "h2 {margin-bottom: 12px;}\n" + "table {margin-left: 12px;}\n" + "th, td {" + " font: 90% monospace;" + " text-align: left;" + "}\n" + "th {" + " font-weight: bold;" + " padding-right: 14px;" + " padding-bottom: 3px;" + "}\n" + "td {padding-right: 14px;}\n" + "td.s, th.s {text-align: right;}\n" + "div.list {" + " background-color: white;" + " border-top: 1px solid #646464;" + " border-bottom: 1px solid #646464;" + " padding-top: 10px;" + " padding-bottom: 14px;" + "}\n" + "div.foot {" + " font: 90% monospace;" + " color: #787878;" + " padding-top: 4px;" + "}\n" + "</style>\n" + )); + } + + buffer_append_string_len(out, CONST_STR_LEN("</head>\n<body>\n")); + } + + if (!buffer_string_is_empty(p->conf.show_header)) { + /* if we have a HEADER file, display it in <pre class="header"></pre> */ + + buffer *hb = p->conf.show_header; + if (hb->ptr[0] != '/') { + buffer_copy_buffer(p->tmp_buf, con->physical.path); + buffer_append_path_len(p->tmp_buf, CONST_BUF_LEN(p->conf.show_header)); + hb = p->tmp_buf; + } + + http_list_directory_include_file(out, hb, "header", p->conf.encode_header); + } + + buffer_append_string_len(out, CONST_STR_LEN("<h2>Index of ")); + buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); + buffer_append_string_len(out, CONST_STR_LEN( + "</h2>\n" + "<div class=\"list\">\n" + "<table summary=\"Directory Listing\" cellpadding=\"0\" cellspacing=\"0\">\n" + "<thead>" + "<tr>" + "<th class=\"n\">Name</th>" + "<th class=\"m\">Last Modified</th>" + "<th class=\"s\">Size</th>" + "<th class=\"t\">Type</th>" + "</tr>" + "</thead>\n" + "<tbody>\n" + )); + if (!buffer_is_equal_string(con->uri.path, CONST_STR_LEN("/"))) { + buffer_append_string_len(out, CONST_STR_LEN( + "<tr class=\"d\">" + "<td class=\"n\"><a href=\"../\">..</a>/</td>" + "<td class=\"m\"> </td>" + "<td class=\"s\">- </td>" + "<td class=\"t\">Directory</td>" + "</tr>\n" + )); + } +} + +static void http_list_directory_footer(server *srv, connection *con, plugin_data *p, buffer *out) { + UNUSED(srv); + + buffer_append_string_len(out, CONST_STR_LEN( + "</tbody>\n" + "</table>\n" + "</div>\n" + )); + + if (!buffer_string_is_empty(p->conf.show_readme)) { + /* if we have a README file, display it in <pre class="readme"></pre> */ + + buffer *rb = p->conf.show_readme; + if (rb->ptr[0] != '/') { + buffer_copy_buffer(p->tmp_buf, con->physical.path); + buffer_append_path_len(p->tmp_buf, CONST_BUF_LEN(p->conf.show_readme)); + rb = p->tmp_buf; + } + + http_list_directory_include_file(out, rb, "readme", p->conf.encode_readme); + } + + if(p->conf.auto_layout) { + + buffer_append_string_len(out, CONST_STR_LEN( + "<div class=\"foot\">" + )); + + if (!buffer_string_is_empty(p->conf.set_footer)) { + buffer_append_string_buffer(out, p->conf.set_footer); + } else { + buffer_append_string_buffer(out, con->conf.server_tag); + } + + buffer_append_string_len(out, CONST_STR_LEN( + "</div>\n" + )); + + if (!buffer_string_is_empty(p->conf.external_js)) { + buffer_append_string_len(out, CONST_STR_LEN("<script type=\"text/javascript\" src=\"")); + buffer_append_string_buffer(out, p->conf.external_js); + buffer_append_string_len(out, CONST_STR_LEN("\"></script>\n")); + } else if (buffer_is_empty(p->conf.external_js)) { + http_dirlist_append_js_table_resort(out, con); + } + + buffer_append_string_len(out, CONST_STR_LEN( + "</body>\n" + "</html>\n" + )); + } +} + +static int http_list_directory(server *srv, connection *con, plugin_data *p, buffer *dir) { + DIR *dp; + buffer *out; + struct dirent *dent; + struct stat st; + char *path, *path_file; + size_t i; + int hide_dotfiles = p->conf.hide_dot_files; + dirls_list_t dirs, files, *list; + dirls_entry_t *tmp; + char sizebuf[sizeof("999.9K")]; + char datebuf[sizeof("2005-Jan-01 22:23:24")]; + const char *content_type; + long name_max; +#if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) + char attrval[128]; + int attrlen; +#endif +#ifdef HAVE_LOCALTIME_R + struct tm tm; +#endif + + if (buffer_string_is_empty(dir)) return -1; + + i = buffer_string_length(dir); + +#ifdef HAVE_PATHCONF + if (0 >= (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) { + /* some broken fs (fuse) return 0 instead of -1 */ +#ifdef NAME_MAX + name_max = NAME_MAX; +#else + name_max = 255; /* stupid default */ +#endif + } +#elif defined __WIN32 + name_max = FILENAME_MAX; +#else + name_max = NAME_MAX; +#endif + + path = malloc(i + name_max + 1); + force_assert(NULL != path); + memcpy(path, dir->ptr, i+1); + path_file = path + i; + + if (NULL == (dp = opendir(path))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "opendir failed:", dir, strerror(errno)); + + free(path); + return -1; + } + + dirs.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); + force_assert(dirs.ent); + dirs.size = DIRLIST_BLOB_SIZE; + dirs.used = 0; + files.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); + force_assert(files.ent); + files.size = DIRLIST_BLOB_SIZE; + files.used = 0; + + while ((dent = readdir(dp)) != NULL) { +#ifdef HAVE_PCRE_H + unsigned short exclude_match = 0; +#endif + + if (dent->d_name[0] == '.') { + if (hide_dotfiles) + continue; + if (dent->d_name[1] == '\0') + continue; + if (dent->d_name[1] == '.' && dent->d_name[2] == '\0') + continue; + } + + if (p->conf.hide_readme_file && !buffer_string_is_empty(p->conf.show_readme)) { + if (strcmp(dent->d_name, p->conf.show_readme->ptr) == 0) + continue; + } + if (p->conf.hide_header_file && !buffer_string_is_empty(p->conf.show_header)) { + if (strcmp(dent->d_name, p->conf.show_header->ptr) == 0) + continue; + } + + /* compare d_name against excludes array + * elements, skipping any that match. + */ +#ifdef HAVE_PCRE_H + for(i = 0; i < p->conf.excludes->used; i++) { + int n; +#define N 10 + int ovec[N * 3]; + pcre *regex = p->conf.excludes->ptr[i]->regex; + + if ((n = pcre_exec(regex, NULL, dent->d_name, + strlen(dent->d_name), 0, 0, ovec, 3 * N)) < 0) { + if (n != PCRE_ERROR_NOMATCH) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "execution error while matching:", n); + + /* aborting would require a lot of manual cleanup here. + * skip instead (to not leak names that break pcre matching) + */ + exclude_match = 1; + break; + } + } + else { + exclude_match = 1; + break; + } + } + + if (exclude_match) { + continue; + } +#endif + + i = strlen(dent->d_name); + + /* NOTE: the manual says, d_name is never more than NAME_MAX + * so this should actually not be a buffer-overflow-risk + */ + if (i > (size_t)name_max) continue; + + memcpy(path_file, dent->d_name, i + 1); + if (stat(path, &st) != 0) + continue; + + list = &files; + if (S_ISDIR(st.st_mode)) + list = &dirs; + + if (list->used == list->size) { + list->size += DIRLIST_BLOB_SIZE; + list->ent = (dirls_entry_t**) realloc(list->ent, sizeof(dirls_entry_t*) * list->size); + force_assert(list->ent); + } + + tmp = (dirls_entry_t*) malloc(sizeof(dirls_entry_t) + 1 + i); + tmp->mtime = st.st_mtime; + tmp->size = st.st_size; + tmp->namelen = i; + memcpy(DIRLIST_ENT_NAME(tmp), dent->d_name, i + 1); + + list->ent[list->used++] = tmp; + } + closedir(dp); + + if (dirs.used) http_dirls_sort(dirs.ent, dirs.used); + + if (files.used) http_dirls_sort(files.ent, files.used); + + out = chunkqueue_append_buffer_open(con->write_queue); + http_list_directory_header(srv, con, p, out); + + /* directories */ + for (i = 0; i < dirs.used; i++) { + tmp = dirs.ent[i]; + +#ifdef HAVE_LOCALTIME_R + localtime_r(&(tmp->mtime), &tm); + strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); +#else + strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime))); +#endif + + buffer_append_string_len(out, CONST_STR_LEN("<tr class=\"d\"><td class=\"n\"><a href=\"")); + buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); + buffer_append_string_len(out, CONST_STR_LEN("/\">")); + buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); + buffer_append_string_len(out, CONST_STR_LEN("</a>/</td><td class=\"m\">")); + buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); + buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">- </td><td class=\"t\">Directory</td></tr>\n")); + + free(tmp); + } + + /* files */ + for (i = 0; i < files.used; i++) { + tmp = files.ent[i]; + + content_type = NULL; +#if defined(HAVE_XATTR) + if (con->conf.use_xattr) { + memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); + attrlen = sizeof(attrval) - 1; + if (attr_get(path, srv->srvconf.xattr_name->ptr, attrval, &attrlen, 0) == 0) { + attrval[attrlen] = '\0'; + content_type = attrval; + } + } +#elif defined(HAVE_EXTATTR) + if (con->conf.use_xattr) { + memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); + if(-1 != (attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, srv->srvconf.xattr_name->ptr, attrval, sizeof(attrval)-1))) { + attrval[attrlen] = '\0'; + content_type = attrval; + } + } +#endif + + if (content_type == NULL) { + const buffer *type = stat_cache_mimetype_by_ext(con, DIRLIST_ENT_NAME(tmp), tmp->namelen); + content_type = NULL != type ? type->ptr : "application/octet-stream"; + } + +#ifdef HAVE_LOCALTIME_R + localtime_r(&(tmp->mtime), &tm); + strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); +#else + strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime))); +#endif + http_list_directory_sizefmt(sizebuf, sizeof(sizebuf), tmp->size); + + buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\"")); + buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); + buffer_append_string_len(out, CONST_STR_LEN("\">")); + buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); + buffer_append_string_len(out, CONST_STR_LEN("</a></td><td class=\"m\">")); + buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); + buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">")); + buffer_append_string(out, sizebuf); + buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"t\">")); + buffer_append_string(out, content_type); + buffer_append_string_len(out, CONST_STR_LEN("</td></tr>\n")); + + free(tmp); + } + + free(files.ent); + free(dirs.ent); + free(path); + + http_list_directory_footer(srv, con, p, out); + + /* Insert possible charset to Content-Type */ + if (buffer_string_is_empty(p->conf.encoding)) { + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); + } else { + buffer_copy_string_len(p->content_charset, CONST_STR_LEN("text/html; charset=")); + buffer_append_string_buffer(p->content_charset, p->conf.encoding); + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->content_charset)); + } + + chunkqueue_append_buffer_commit(con->write_queue); + con->file_finished = 1; + + return 0; +} + + + +URIHANDLER_FUNC(mod_dirlisting_subrequest) { + plugin_data *p = p_d; + stat_cache_entry *sce = NULL; + + UNUSED(srv); + + /* we only handle GET and HEAD */ + switch(con->request.http_method) { + case HTTP_METHOD_GET: + case HTTP_METHOD_HEAD: + break; + default: + return HANDLER_GO_ON; + } + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; + if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; + if (con->uri.path->ptr[buffer_string_length(con->uri.path) - 1] != '/') return HANDLER_GO_ON; + + mod_dirlisting_patch_connection(srv, con, p); + + if (!p->conf.dir_listing) return HANDLER_GO_ON; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- handling the request as Dir-Listing"); + log_error_write(srv, __FILE__, __LINE__, "sb", "URI :", con->uri.path); + } + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + log_error_write(srv, __FILE__, __LINE__, "SB", "stat_cache_get_entry failed: ", con->physical.path); + SEGFAULT(); + } + + if (!S_ISDIR(sce->st.st_mode)) return HANDLER_GO_ON; + + if (http_list_directory(srv, con, p, con->physical.path)) { + /* dirlisting failed */ + con->http_status = 403; + } + + buffer_reset(con->physical.path); + + /* not found */ + return HANDLER_FINISHED; +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_dirlisting_plugin_init(plugin *p); +int mod_dirlisting_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("dirlisting"); + + p->init = mod_dirlisting_init; + p->handle_subrequest_start = mod_dirlisting_subrequest; + p->set_defaults = mod_dirlisting_set_defaults; + p->cleanup = mod_dirlisting_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_evasive.c b/data/lighttpd/lighttpd-1.4.53/src/mod_evasive.c new file mode 100644 index 000000000..9171d5c44 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_evasive.c @@ -0,0 +1,208 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "http_header.h" +#include "sock_addr.h" + +#include "plugin.h" + +#include <stdlib.h> +#include <string.h> + +/** + * mod_evasive + * + * we indent to implement all features the mod_evasive from apache has + * + * - limit of connections per IP + * - provide a list of block-listed ip/networks (no access) + * - provide a white-list of ips/network which is not affected by the limit + * (hmm, conditionals might be enough) + * - provide a bandwidth limiter per IP + * + * started by: + * - w1zzard@techpowerup.com + */ + +typedef struct { + unsigned short max_conns; + unsigned short silent; + buffer *location; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +INIT_FUNC(mod_evasive_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + return p; +} + +FREE_FUNC(mod_evasive_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->location); + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_evasive_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "evasive.max-conns-per-ip", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "evasive.silent", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "evasive.location", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->max_conns = 0; + s->silent = 0; + s->location = buffer_init(); + + cv[0].destination = &(s->max_conns); + cv[1].destination = &(s->silent); + cv[2].destination = s->location; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_evasive_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(max_conns); + PATCH(silent); + PATCH(location); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.max-conns-per-ip"))) { + PATCH(max_conns); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.silent"))) { + PATCH(silent); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("evasive.location"))) { + PATCH(location); + } + } + } + + return 0; +} +#undef PATCH + +URIHANDLER_FUNC(mod_evasive_uri_handler) { + plugin_data *p = p_d; + size_t conns_by_ip = 0; + size_t j; + + if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; + + mod_evasive_patch_connection(srv, con, p); + + /* no limit set, nothing to block */ + if (p->conf.max_conns == 0) return HANDLER_GO_ON; + + for (j = 0; j < srv->conns->used; j++) { + connection *c = srv->conns->ptr[j]; + + /* check if other connections are already actively serving data for the same IP + * we can only ban connections which are already behind the 'read request' state + * */ + if (c->state <= CON_STATE_REQUEST_END) continue; + + if (!sock_addr_is_addr_eq(&c->dst_addr, &con->dst_addr)) continue; + conns_by_ip++; + + if (conns_by_ip > p->conf.max_conns) { + if (!p->conf.silent) { + log_error_write(srv, __FILE__, __LINE__, "bs", + con->dst_addr_buf, + "turned away. Too many connections."); + } + + if (!buffer_is_empty(p->conf.location)) { + http_header_response_set(con, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.location)); + con->http_status = 302; + con->file_finished = 1; + } else { + con->http_status = 403; + } + con->mode = DIRECT; + return HANDLER_FINISHED; + } + } + + return HANDLER_GO_ON; +} + + +int mod_evasive_plugin_init(plugin *p); +int mod_evasive_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("evasive"); + + p->init = mod_evasive_init; + p->set_defaults = mod_evasive_set_defaults; + p->handle_uri_clean = mod_evasive_uri_handler; + p->cleanup = mod_evasive_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_evhost.c b/data/lighttpd/lighttpd-1.4.53/src/mod_evhost.c new file mode 100644 index 000000000..ae7076970 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_evhost.c @@ -0,0 +1,363 @@ +#include "first.h" + +#include "base.h" +#include "plugin.h" +#include "log.h" +#include "stat_cache.h" + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +typedef struct { + /* unparsed pieces */ + buffer *path_pieces_raw; + + /* pieces for path creation */ + size_t len; + buffer **path_pieces; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + buffer *tmp_buf; + + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +INIT_FUNC(mod_evhost_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->tmp_buf = buffer_init(); + + return p; +} + +FREE_FUNC(mod_evhost_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + if(s->path_pieces) { + size_t j; + for (j = 0; j < s->len; j++) { + buffer_free(s->path_pieces[j]); + } + + free(s->path_pieces); + } + + buffer_free(s->path_pieces_raw); + + free(s); + } + free(p->config_storage); + } + + buffer_free(p->tmp_buf); + + free(p); + + return HANDLER_GO_ON; +} + +static int mod_evhost_parse_pattern(plugin_config *s) { + char *ptr = s->path_pieces_raw->ptr,*pos; + + s->path_pieces = NULL; + + for(pos=ptr;*ptr;ptr++) { + if(*ptr == '%') { + size_t len; + s->path_pieces = realloc(s->path_pieces,(s->len+2) * sizeof(*s->path_pieces)); + s->path_pieces[s->len] = buffer_init(); + s->path_pieces[s->len+1] = buffer_init(); + + /* "%%" "%_" "%x" "%{x.y}" where x and y are *single digit* 0 - 9 */ + if (ptr[1] == '%' || ptr[1] == '_' || light_isdigit(ptr[1])) { + len = 2; + } else if (ptr[1] == '{') { + if (!light_isdigit(ptr[2])) return -1; + if (ptr[3] == '.') { + if (!light_isdigit(ptr[4])) return -1; + if (ptr[5] != '}') return -1; + len = 6; + } else if (ptr[3] == '}') { + len = 4; + } else { + return -1; + } + } else { + return -1; + } + + buffer_copy_string_len(s->path_pieces[s->len],pos,ptr-pos); + pos = ptr + len; + + buffer_copy_string_len(s->path_pieces[s->len+1],ptr,len); + ptr += len - 1; /*(ptr++ in for() loop)*/ + + s->len += 2; + } + } + + if(*pos != '\0') { + s->path_pieces = realloc(s->path_pieces,(s->len+1) * sizeof(*s->path_pieces)); + s->path_pieces[s->len] = buffer_init(); + + buffer_copy_string_len(s->path_pieces[s->len],pos,ptr-pos); + + s->len += 1; + } + + return 0; +} + +SETDEFAULTS_FUNC(mod_evhost_set_defaults) { + plugin_data *p = p_d; + size_t i; + + /** + * + * # + * # define a pattern for the host url finding + * # %% => % sign + * # %0 => domain name + tld + * # %1 => tld + * # %2 => domain name without tld + * # %3 => subdomain 1 name + * # %4 => subdomain 2 name + * # %_ => fqdn (without port info) + * # + * evhost.path-pattern = "/home/ckruse/dev/www/%3/htdocs/" + * + */ + + config_values_t cv[] = { + { "evhost.path-pattern", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->path_pieces_raw = buffer_init(); + s->path_pieces = NULL; + s->len = 0; + + cv[0].destination = s->path_pieces_raw; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!buffer_string_is_empty(s->path_pieces_raw)) { + if (0 != mod_evhost_parse_pattern(s)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "invalid evhost.path-pattern:", s->path_pieces_raw); + return HANDLER_ERROR; + } + } + } + + return HANDLER_GO_ON; +} + +/** + * assign the different parts of the domain to array-indezes (sub2.sub1.domain.tld) + * - %0 - domain.tld + * - %1 - tld + * - %2 - domain + * - %3 - sub1 + * - ... + */ + +static void mod_evhost_parse_host(buffer *key, array *host, buffer *authority) { + char *ptr = authority->ptr + buffer_string_length(authority); + char *colon = ptr; /* needed to filter out the colon (if exists) */ + int first = 1; + int i; + + /* first, find the domain + tld */ + for(; ptr > authority->ptr; --ptr) { + if(*ptr == '.') { + if(first) first = 0; + else break; + } else if(*ptr == ':') { + colon = ptr; + first = 1; + } + } + + /* if we stopped at a dot, skip the dot */ + if (*ptr == '.') ptr++; + array_insert_key_value(host, CONST_STR_LEN("%0"), ptr, colon-ptr); + + /* if the : is not the start of the authority, go on parsing the hostname */ + + if (colon != authority->ptr) { + for(ptr = colon - 1, i = 1; ptr > authority->ptr; --ptr) { + if(*ptr == '.') { + if (ptr != colon - 1) { + /* is something between the dots */ + buffer_copy_string_len(key, CONST_STR_LEN("%")); + buffer_append_int(key, i++); + array_insert_key_value(host, CONST_BUF_LEN(key), ptr+1, colon-ptr-1); + } + colon = ptr; + } + } + + /* if the . is not the first charactor of the hostname */ + if (colon != ptr) { + buffer_copy_string_len(key, CONST_STR_LEN("%")); + buffer_append_int(key, i /* ++ */); + array_insert_key_value(host, CONST_BUF_LEN(key), ptr, colon-ptr); + } + } +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_evhost_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(path_pieces); + PATCH(len); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("evhost.path-pattern"))) { + PATCH(path_pieces); + PATCH(len); + } + } + } + + return 0; +} +#undef PATCH + + +static void mod_evhost_build_doc_root_path(buffer *b, array *parsed_host, buffer *authority, buffer **path_pieces, const size_t npieces) { + array_reset_data_strings(parsed_host); + mod_evhost_parse_host(b, parsed_host, authority); + buffer_clear(b); + + for (size_t i = 0; i < npieces; ++i) { + const char *ptr = path_pieces[i]->ptr; + if (*ptr == '%') { + data_string *ds; + + if (*(ptr+1) == '%') { + /* %% */ + buffer_append_string_len(b, CONST_STR_LEN("%")); + } else if (*(ptr+1) == '_' ) { + /* %_ == full hostname */ + char *colon = strchr(authority->ptr, ':'); + + if(colon == NULL) { + buffer_append_string_buffer(b, authority); /* adds fqdn */ + } else { + /* strip the port out of the authority-part of the URI scheme */ + buffer_append_string_len(b, authority->ptr, colon - authority->ptr); /* adds fqdn */ + } + } else if (ptr[1] == '{' ) { + char s[3] = "% "; + s[1] = ptr[2]; /*(assumes single digit before '.', and, optionally, '.' and single digit after '.')*/ + if (NULL != (ds = (data_string *)array_get_element_klen(parsed_host, s, 2))) { + if (ptr[3] != '.' || ptr[4] == '0') { + buffer_append_string_buffer(b, ds->value); + } else { + if ((size_t)(ptr[4]-'0') <= buffer_string_length(ds->value)) { + buffer_append_string_len(b, ds->value->ptr+(ptr[4]-'0')-1, 1); + } + } + } else { + /* unhandled %-sequence */ + } + } else if (NULL != (ds = (data_string *)array_get_element_klen(parsed_host, CONST_BUF_LEN(path_pieces[i])))) { + buffer_append_string_buffer(b, ds->value); + } else { + /* unhandled %-sequence */ + } + } else { + buffer_append_string_buffer(b, path_pieces[i]); + } + } + + buffer_append_slash(b); +} + +static handler_t mod_evhost_uri_handler(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + stat_cache_entry *sce = NULL; + + /* not authority set */ + if (buffer_string_is_empty(con->uri.authority)) return HANDLER_GO_ON; + + mod_evhost_patch_connection(srv, con, p); + + /* missing even default(global) conf */ + if (0 == p->conf.len) { + return HANDLER_GO_ON; + } + + mod_evhost_build_doc_root_path(p->tmp_buf, srv->split_vals, con->uri.authority, p->conf.path_pieces, p->conf.len); + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, p->tmp_buf, &sce)) { + log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), p->tmp_buf); + } else if(!S_ISDIR(sce->st.st_mode)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "not a directory:", p->tmp_buf); + } else { + buffer_copy_buffer(con->physical.doc_root, p->tmp_buf); + } + + return HANDLER_GO_ON; +} + +int mod_evhost_plugin_init(plugin *p); +int mod_evhost_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("evhost"); + p->init = mod_evhost_init; + p->set_defaults = mod_evhost_set_defaults; + p->handle_docroot = mod_evhost_uri_handler; + p->cleanup = mod_evhost_free; + + p->data = NULL; + + return 0; +} + +/* eof */ diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_expire.c b/data/lighttpd/lighttpd-1.4.53/src/mod_expire.c new file mode 100644 index 000000000..d0f771bf8 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_expire.c @@ -0,0 +1,424 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "http_header.h" + +#include "plugin.h" +#include "stat_cache.h" + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +/** + * this is a expire module for a lighttpd + * + * set 'Expires:' HTTP Headers on demand + */ + + + +/* plugin config for all request/connections */ + +typedef struct { + array *expire_url; + array *expire_mimetypes; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + buffer *expire_tstmp; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_expire_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->expire_tstmp = buffer_init(); + + buffer_string_prepare_copy(p->expire_tstmp, 255); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_expire_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + buffer_free(p->expire_tstmp); + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->expire_url); + array_free(s->expire_mimetypes); + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +static int mod_expire_get_offset(server *srv, plugin_data *p, buffer *expire, time_t *offset) { + char *ts; + int type = -1; + time_t retts = 0; + + UNUSED(p); + + /* + * parse + * + * '(access|now|modification) [plus] {<num> <type>}*' + * + * e.g. 'access 1 years' + */ + + if (buffer_string_is_empty(expire)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "empty:"); + return -1; + } + + ts = expire->ptr; + + if (0 == strncmp(ts, "access ", 7)) { + type = 0; + ts += 7; + } else if (0 == strncmp(ts, "now ", 4)) { + type = 0; + ts += 4; + } else if (0 == strncmp(ts, "modification ", 13)) { + type = 1; + ts += 13; + } else { + /* invalid type-prefix */ + log_error_write(srv, __FILE__, __LINE__, "ss", + "invalid <base>:", ts); + return -1; + } + + if (0 == strncmp(ts, "plus ", 5)) { + /* skip the optional plus */ + ts += 5; + } + + /* the rest is just <number> (years|months|weeks|days|hours|minutes|seconds) */ + while (1) { + char *space, *err; + int num; + + if (NULL == (space = strchr(ts, ' '))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "missing space after <num>:", ts); + return -1; + } + + num = strtol(ts, &err, 10); + if (*err != ' ') { + log_error_write(srv, __FILE__, __LINE__, "ss", + "missing <type> after <num>:", ts); + return -1; + } + + ts = space + 1; + + if (NULL != (space = strchr(ts, ' '))) { + int slen; + /* */ + + slen = space - ts; + + if (slen == 5 && + 0 == strncmp(ts, "years", slen)) { + num *= 60 * 60 * 24 * 30 * 12; + } else if (slen == 6 && + 0 == strncmp(ts, "months", slen)) { + num *= 60 * 60 * 24 * 30; + } else if (slen == 5 && + 0 == strncmp(ts, "weeks", slen)) { + num *= 60 * 60 * 24 * 7; + } else if (slen == 4 && + 0 == strncmp(ts, "days", slen)) { + num *= 60 * 60 * 24; + } else if (slen == 5 && + 0 == strncmp(ts, "hours", slen)) { + num *= 60 * 60; + } else if (slen == 7 && + 0 == strncmp(ts, "minutes", slen)) { + num *= 60; + } else if (slen == 7 && + 0 == strncmp(ts, "seconds", slen)) { + num *= 1; + } else { + log_error_write(srv, __FILE__, __LINE__, "ss", + "unknown type:", ts); + return -1; + } + + retts += num; + + ts = space + 1; + } else { + if (0 == strcmp(ts, "years")) { + num *= 60 * 60 * 24 * 30 * 12; + } else if (0 == strcmp(ts, "months")) { + num *= 60 * 60 * 24 * 30; + } else if (0 == strcmp(ts, "weeks")) { + num *= 60 * 60 * 24 * 7; + } else if (0 == strcmp(ts, "days")) { + num *= 60 * 60 * 24; + } else if (0 == strcmp(ts, "hours")) { + num *= 60 * 60; + } else if (0 == strcmp(ts, "minutes")) { + num *= 60; + } else if (0 == strcmp(ts, "seconds")) { + num *= 1; + } else { + log_error_write(srv, __FILE__, __LINE__, "ss", + "unknown type:", ts); + return -1; + } + + retts += num; + + break; + } + } + + if (offset != NULL) *offset = retts; + + return type; +} + + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_expire_set_defaults) { + plugin_data *p = p_d; + size_t i = 0, k; + + config_values_t cv[] = { + { "expire.url", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "expire.mimetypes", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->expire_url = array_init(); + s->expire_mimetypes = array_init(); + + cv[0].destination = s->expire_url; + cv[1].destination = s->expire_mimetypes; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_kvstring(s->expire_url)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for expire.url; expected list of \"urlpath\" => \"expiration\""); + return HANDLER_ERROR; + } + + for (k = 0; k < s->expire_url->used; k++) { + data_string *ds = (data_string *)s->expire_url->data[k]; + + /* parse lines */ + if (-1 == mod_expire_get_offset(srv, p, ds->value, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "parsing expire.url failed:", ds->value); + return HANDLER_ERROR; + } + } + + if (!array_is_kvstring(s->expire_mimetypes)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for expire.mimetypes; expected list of \"mimetype\" => \"expiration\""); + return HANDLER_ERROR; + } + + for (k = 0; k < s->expire_mimetypes->used; k++) { + data_string *ds = (data_string *)s->expire_mimetypes->data[k]; + size_t klen = buffer_string_length(ds->key); + + /*(omit trailing '*', if present, from prefix match)*/ + /*(not usually a good idea to modify array keys + * since doing so might break array_get_element_klen() search, + * but array use in this module only walks array)*/ + if (klen && ds->key->ptr[klen-1] == '*') buffer_string_set_length(ds->key, klen-1); + + /* parse lines */ + if (-1 == mod_expire_get_offset(srv, p, ds->value, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "parsing expire.mimetypes failed:", ds->value); + return HANDLER_ERROR; + } + } + } + + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_expire_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(expire_url); + PATCH(expire_mimetypes); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("expire.url"))) { + PATCH(expire_url); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("expire.mimetypes"))) { + PATCH(expire_mimetypes); + } + } + } + + return 0; +} +#undef PATCH + +CONNECTION_FUNC(mod_expire_handler) { + plugin_data *p = p_d; + buffer *vb; + data_string *ds; + + /* Add caching headers only to http_status 200 OK or 206 Partial Content */ + if (con->http_status != 200 && con->http_status != 206) return HANDLER_GO_ON; + /* Add caching headers only to GET or HEAD requests */ + if ( con->request.http_method != HTTP_METHOD_GET + && con->request.http_method != HTTP_METHOD_HEAD) return HANDLER_GO_ON; + /* Add caching headers only if not already present */ + vb = http_header_response_get(con, HTTP_HEADER_CACHE_CONTROL, CONST_STR_LEN("Cache-Control")); + if (NULL != vb) return HANDLER_GO_ON; + + if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; + + mod_expire_patch_connection(srv, con, p); + + /* check expire.url */ + ds = (data_string *)array_match_key_prefix(p->conf.expire_url, con->uri.path); + if (NULL != ds) { + vb = ds->value; + } + else { + /* check expire.mimetypes (if no match with expire.url) */ + vb = http_header_response_get(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type")); + ds = (NULL != vb) + ? (data_string *)array_match_key_prefix(p->conf.expire_mimetypes, vb) + : (data_string *)array_get_element_klen(p->conf.expire_mimetypes, CONST_STR_LEN("")); + if (NULL == ds) return HANDLER_GO_ON; + vb = ds->value; + } + + if (NULL != vb) { + time_t ts, expires; + stat_cache_entry *sce = NULL; + + /* if stat fails => sce == NULL, ignore return value */ + (void) stat_cache_get_entry(srv, con, con->physical.path, &sce); + + switch(mod_expire_get_offset(srv, p, vb, &ts)) { + case 0: + /* access */ + expires = (ts + srv->cur_ts); + break; + case 1: + /* modification */ + + /* can't set modification based expire header if + * mtime is not available + */ + if (NULL == sce) return HANDLER_GO_ON; + + expires = (ts + sce->st.st_mtime); + break; + default: + /* -1 is handled at parse-time */ + return HANDLER_ERROR; + } + + /* expires should be at least srv->cur_ts */ + if (expires < srv->cur_ts) expires = srv->cur_ts; + + buffer_clear(p->expire_tstmp); + buffer_append_strftime(p->expire_tstmp, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(expires))); + + /* HTTP/1.0 */ + http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Expires"), CONST_BUF_LEN(p->expire_tstmp)); + + /* HTTP/1.1 */ + buffer_copy_string_len(p->expire_tstmp, CONST_STR_LEN("max-age=")); + buffer_append_int(p->expire_tstmp, expires - srv->cur_ts); /* as expires >= srv->cur_ts the difference is >= 0 */ + + http_header_response_set(con, HTTP_HEADER_CACHE_CONTROL, CONST_STR_LEN("Cache-Control"), CONST_BUF_LEN(p->expire_tstmp)); + + return HANDLER_GO_ON; + } + + /* not found */ + return HANDLER_GO_ON; +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_expire_plugin_init(plugin *p); +int mod_expire_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("expire"); + + p->init = mod_expire_init; + p->handle_response_start = mod_expire_handler; + p->set_defaults = mod_expire_set_defaults; + p->cleanup = mod_expire_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_extforward.c b/data/lighttpd/lighttpd-1.4.53/src/mod_extforward.c new file mode 100644 index 000000000..152353141 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_extforward.c @@ -0,0 +1,1645 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "http_header.h" +#include "request.h" +#include "sock_addr.h" + +#include "plugin.h" + +#include "configfile.h" + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "sys-socket.h" + +/** + * mod_extforward.c for lighttpd, by comman.kang <at> gmail <dot> com + * extended, modified by Lionel Elie Mamane (LEM), lionel <at> mamane <dot> lu + * support chained proxies by glen@delfi.ee, #1528 + * + * Config example: + * + * Trust proxy 10.0.0.232 and 10.0.0.232 + * extforward.forwarder = ( "10.0.0.232" => "trust", + * "10.0.0.233" => "trust" ) + * + * Trust all proxies (NOT RECOMMENDED!) + * extforward.forwarder = ( "all" => "trust") + * + * Note that "all" has precedence over specific entries, + * so "all except" setups will not work. + * + * In case you have chained proxies, you can add all their IP's to the + * config. However "all" has effect only on connecting IP, as the + * X-Forwarded-For header can not be trusted. + * + * Note: The effect of this module is variable on $HTTP["remotip"] directives and + * other module's remote ip dependent actions. + * Things done by modules before we change the remoteip or after we reset it will match on the proxy's IP. + * Things done in between these two moments will match on the real client's IP. + * The moment things are done by a module depends on in which hook it does things and within the same hook + * on whether they are before/after us in the module loading order + * (order in the server.modules directive in the config file). + * + * Tested behaviours: + * + * mod_access: Will match on the real client. + * + * mod_accesslog: + * In order to see the "real" ip address in access log , + * you'll have to load mod_extforward after mod_accesslog. + * like this: + * + * server.modules = ( + * ..... + * mod_accesslog, + * mod_extforward + * ) + */ + + +/* plugin config for all request/connections */ + +typedef enum { + PROXY_FORWARDED_NONE = 0x00, + PROXY_FORWARDED_FOR = 0x01, + PROXY_FORWARDED_PROTO = 0x02, + PROXY_FORWARDED_HOST = 0x04, + PROXY_FORWARDED_BY = 0x08, + PROXY_FORWARDED_REMOTE_USER = 0x10 +} proxy_forwarded_t; + +struct sock_addr_mask { + sock_addr addr; + int bits; +}; + +struct sock_addr_masks { + struct sock_addr_mask *addrs; + size_t used; + size_t sz; +}; + +typedef struct { + array *forwarder; + struct sock_addr_masks *forward_masks; + array *headers; + array *opts_params; + unsigned int opts; + unsigned short int hap_PROXY; + unsigned short int hap_PROXY_ssl_client_verify; + short int forward_all; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +static plugin_data *mod_extforward_plugin_data_singleton; +static int extforward_check_proxy; + + +/* context , used for restore remote ip */ + +typedef struct { + /* per-request state */ + sock_addr saved_remote_addr; + buffer *saved_remote_addr_buf; + + /* hap-PROXY protocol prior to receiving first request */ + int(*saved_network_read)(server *, connection *, chunkqueue *, off_t); + + /* connection-level state applied to requests in handle_request_env */ + array *env; + int ssl_client_verify; +} handler_ctx; + + +static handler_ctx * handler_ctx_init(void) { + handler_ctx * hctx; + hctx = calloc(1, sizeof(*hctx)); + return hctx; +} + +static void handler_ctx_free(handler_ctx *hctx) { + free(hctx); +} + +/* init the plugin data */ +INIT_FUNC(mod_extforward_init) { + plugin_data *p; + p = calloc(1, sizeof(*p)); + mod_extforward_plugin_data_singleton = p; + return p; +} + +/* destroy the plugin data */ +FREE_FUNC(mod_extforward_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->forwarder); + array_free(s->headers); + array_free(s->opts_params); + + if (s->forward_masks) { + free(s->forward_masks->addrs); + free(s->forward_masks); + } + + free(s); + } + free(p->config_storage); + } + + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_extforward_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "extforward.forwarder", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "extforward.headers", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "extforward.params", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "extforward.hap-PROXY", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "extforward.hap-PROXY-ssl-client-verify", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->forwarder = array_init(); + s->headers = array_init(); + s->opts_params = array_init(); + s->opts = PROXY_FORWARDED_NONE; + + cv[0].destination = s->forwarder; + cv[1].destination = s->headers; + cv[2].destination = s->opts_params; + cv[3].destination = &s->hap_PROXY; + cv[4].destination = &s->hap_PROXY_ssl_client_verify; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_kvstring(s->forwarder)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for extforward.forwarder; expected list of \"IPaddr\" => \"trust\""); + return HANDLER_ERROR; + } + + if (array_get_element(config->value, "extforward.forwarder")) { + const data_string * const allds = (data_string *)array_get_element(s->forwarder, "all"); + s->forward_all = (NULL == allds) ? 0 : (0 == strcasecmp(allds->value->ptr, "trust")) ? 1 : -1; + for (size_t j = 0; j < s->forwarder->used; ++j) { + data_string * const ds = (data_string *)s->forwarder->data[j]; + char * const nm_slash = strchr(ds->key->ptr, '/'); + if (0 != strcasecmp(ds->value->ptr, "trust")) { + if (0 != strcasecmp(ds->value->ptr, "untrusted")) { + log_error_write(srv, __FILE__, __LINE__, "sbsbs", "ERROR: expect \"trust\", not \"", ds->key, "\" => \"", ds->value, "\"; treating as untrusted"); + } + if (NULL != nm_slash) { + log_error_write(srv, __FILE__, __LINE__, "sbsbs", "ERROR: untrusted CIDR masks are ignored (\"", ds->key, "\" => \"", ds->value, "\")"); + } + buffer_clear(ds->value); /* empty is untrusted */ + continue; + } + if (NULL != nm_slash) { + struct sock_addr_mask *sm; + char *err; + const int nm_bits = strtol(nm_slash + 1, &err, 10); + int rc; + if (*err || nm_bits <= 0) { + log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: invalid netmask:", ds->key, err); + return HANDLER_ERROR; + } + if (NULL == s->forward_masks) { + s->forward_masks = calloc(1, sizeof(struct sock_addr_masks)); + force_assert(s->forward_masks); + } + if (s->forward_masks->used == s->forward_masks->sz) { + s->forward_masks->sz += 2; + s->forward_masks->addrs = realloc(s->forward_masks->addrs, s->forward_masks->sz * sizeof(struct sock_addr_mask)); + force_assert(s->forward_masks->addrs); + } + sm = s->forward_masks->addrs + s->forward_masks->used++; + sm->bits = nm_bits; + *nm_slash = '\0'; + rc = sock_addr_from_str_numeric(srv, &sm->addr, ds->key->ptr); + *nm_slash = '/'; + if (1 != rc) return HANDLER_ERROR; + buffer_clear(ds->value); /* empty is untrusted, e.g. if subnet (incorrectly) appears in X-Forwarded-For */ + } + } + } + + if (!array_is_vlist(s->headers)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for extforward.headers; expected list of \"headername\""); + return HANDLER_ERROR; + } + + /* default to "X-Forwarded-For" or "Forwarded-For" if extforward.headers not specified or empty */ + if (!s->hap_PROXY && 0 == s->headers->used && (0 == i || NULL != array_get_element(config->value, "extforward.headers"))) { + array_insert_value(s->headers, CONST_STR_LEN("X-Forwarded-For")); + array_insert_value(s->headers, CONST_STR_LEN("Forwarded-For")); + } + + if (!array_is_kvany(s->opts_params)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for extforward.params; expected ( \"param\" => \"value\" )"); + return HANDLER_ERROR; + } + for (size_t j = 0, used = s->opts_params->used; j < used; ++j) { + proxy_forwarded_t param; + data_unset *du = s->opts_params->data[j]; + #if 0 /*("for" and "proto" historical behavior: always enabled)*/ + if (buffer_is_equal_string(du->key, CONST_STR_LEN("by"))) { + param = PROXY_FORWARDED_BY; + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("for"))) { + param = PROXY_FORWARDED_FOR; + } else + #endif + if (buffer_is_equal_string(du->key, CONST_STR_LEN("host"))) { + param = PROXY_FORWARDED_HOST; + #if 0 + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proto"))) { + param = PROXY_FORWARDED_PROTO; + #endif + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("remote_user"))) { + param = PROXY_FORWARDED_REMOTE_USER; + } else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "extforward.params keys must be one of: host, remote_user, but not:", du->key); + return HANDLER_ERROR; + } + if (du->type == TYPE_STRING) { + data_string *ds = (data_string *)du; + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("enable"))) { + s->opts |= param; + } else if (!buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "extforward.params values must be one of: 0, 1, enable, disable; error for key:", du->key); + return HANDLER_ERROR; + } + } else if (du->type == TYPE_INTEGER) { + data_integer *di = (data_integer *)du; + if (di->value) s->opts |= param; + } else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "extforward.params values must be one of: 0, 1, enable, disable; error for key:", du->key); + return HANDLER_ERROR; + } + } + } + + /* attempt to warn if mod_extforward is not last module loaded to hook + * handle_connection_accept. (Nice to have, but remove this check if + * it reaches too far into internals and prevents other code changes.) + * While it would be nice to check connection_handle_accept plugin slot + * to make sure mod_extforward is last, that info is private to plugin.c + * so merely warn if mod_openssl is loaded after mod_extforward, though + * future modules which hook connection_handle_accept might be missed.*/ + for (i = 0; i < srv->config_context->used; ++i) { + plugin_config *s = p->config_storage[i]; + if (s->hap_PROXY) { + size_t j; + for (j = 0; j < srv->srvconf.modules->used; ++j) { + data_string *ds = (data_string *)srv->srvconf.modules->data[j]; + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_extforward"))) { + break; + } + } + for (; j < srv->srvconf.modules->used; ++j) { + data_string *ds = (data_string *)srv->srvconf.modules->data[j]; + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_openssl"))) { + log_error_write(srv, __FILE__, __LINE__, "s", + "mod_extforward must be loaded after mod_openssl in server.modules when extforward.hap-PROXY = \"enable\""); + break; + } + } + break; + } + } + + for (i = 0; i < srv->srvconf.modules->used; i++) { + data_string *ds = (data_string *)srv->srvconf.modules->data[i]; + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_proxy"))) { + extforward_check_proxy = 1; + break; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_extforward_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(forwarder); + PATCH(forward_masks); + PATCH(headers); + PATCH(opts); + PATCH(hap_PROXY); + PATCH(hap_PROXY_ssl_client_verify); + PATCH(forward_all); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.forwarder"))) { + PATCH(forwarder); + PATCH(forward_masks); + PATCH(forward_all); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.headers"))) { + PATCH(headers); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.params"))) { + PATCH(opts); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.hap-PROXY"))) { + PATCH(hap_PROXY); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("extforward.hap-PROXY-ssl-client-verify"))) { + PATCH(hap_PROXY_ssl_client_verify); + } + } + } + + return 0; +} +#undef PATCH + + +/* + extract a forward array from the environment +*/ +static array *extract_forward_array(buffer *pbuffer) +{ + array *result = array_init(); + if (!buffer_string_is_empty(pbuffer)) { + char *base, *curr; + /* state variable, 0 means not in string, 1 means in string */ + int in_str = 0; + for (base = pbuffer->ptr, curr = pbuffer->ptr; *curr; curr++) { + if (in_str) { + if ((*curr > '9' || *curr < '0') && *curr != '.' && *curr != ':' && (*curr < 'a' || *curr > 'f') && (*curr < 'A' || *curr > 'F')) { + /* found an separator , insert value into result array */ + array_insert_value(result, base, curr - base); + /* change state to not in string */ + in_str = 0; + } + } else { + if ((*curr >= '0' && *curr <= '9') || *curr == ':' || (*curr >= 'a' && *curr <= 'f') || (*curr >= 'A' && *curr <= 'F')) { + /* found leading char of an IP address, move base pointer and change state */ + base = curr; + in_str = 1; + } + } + } + /* if breaking out while in str, we got to the end of string, so add it */ + if (in_str) { + array_insert_value(result, base, curr - base); + } + } + return result; +} + +/* + * check whether ip is trusted, return 1 for trusted , 0 for untrusted + */ +static int is_proxy_trusted(plugin_data *p, const char * const ip, size_t iplen) +{ + data_string *ds = + (data_string *)array_get_element_klen(p->conf.forwarder, ip, iplen); + if (NULL != ds) return !buffer_string_is_empty(ds->value); + + if (p->conf.forward_masks) { + const struct sock_addr_mask * const addrs =p->conf.forward_masks->addrs; + const size_t aused = p->conf.forward_masks->used; + sock_addr addr; + /* C funcs inet_aton(), inet_pton() require '\0'-terminated IP str */ + char addrstr[64]; /*(larger than INET_ADDRSTRLEN and INET6_ADDRSTRLEN)*/ + if (iplen >= sizeof(addrstr)) return 0; + memcpy(addrstr, ip, iplen); + addrstr[iplen] = '\0'; + + if (1 != sock_addr_inet_pton(&addr, addrstr, AF_INET, 0) + && 1 != sock_addr_inet_pton(&addr, addrstr, AF_INET6, 0)) return 0; + + for (size_t i = 0; i < aused; ++i) { + if (sock_addr_is_addr_eq_bits(&addr, &addrs[i].addr, addrs[i].bits)) + return 1; + } + } + + return 0; +} + +static int is_connection_trusted(connection * const con, plugin_data *p) +{ + if (p->conf.forward_all) return (1 == p->conf.forward_all); + return is_proxy_trusted(p, CONST_BUF_LEN(con->dst_addr_buf)); +} + +/* + * Return last address of proxy that is not trusted. + * Do not accept "all" keyword here. + */ +static const char *last_not_in_array(array *a, plugin_data *p) +{ + int i; + + for (i = a->used - 1; i >= 0; i--) { + data_string *ds = (data_string *)a->data[i]; + if (!is_proxy_trusted(p, CONST_BUF_LEN(ds->value))) { + return ds->value->ptr; + } + } + return NULL; +} + +static int mod_extforward_set_addr(server *srv, connection *con, plugin_data *p, const char *addr) { + sock_addr sock; + handler_ctx *hctx = con->plugin_ctx[p->id]; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "ss", "using address:", addr); + } + + sock.plain.sa_family = AF_UNSPEC; + if (1 != sock_addr_from_str_numeric(srv, &sock, addr)) return 0; + if (sock.plain.sa_family == AF_UNSPEC) return 0; + + /* we found the remote address, modify current connection and save the old address */ + if (hctx) { + if (hctx->saved_remote_addr_buf) { + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", + "-- mod_extforward_uri_handler already patched this connection, resetting state"); + } + con->dst_addr = hctx->saved_remote_addr; + buffer_free(con->dst_addr_buf); + con->dst_addr_buf = hctx->saved_remote_addr_buf; + hctx->saved_remote_addr_buf = NULL; + } + } else { + con->plugin_ctx[p->id] = hctx = handler_ctx_init(); + } + /* save old address */ + if (extforward_check_proxy) { + http_header_env_set(con, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR"), CONST_BUF_LEN(con->dst_addr_buf)); + } + hctx->saved_remote_addr = con->dst_addr; + hctx->saved_remote_addr_buf = con->dst_addr_buf; + /* patch connection address */ + con->dst_addr = sock; + con->dst_addr_buf = buffer_init_string(addr); + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "patching con->dst_addr_buf for the accesslog:", addr); + } + + /* Now, clean the conf_cond cache, because we may have changed the results of tests */ + config_cond_cache_reset_item(srv, con, COMP_HTTP_REMOTE_IP); + + return 1; +} + +static void mod_extforward_set_proto(server *srv, connection *con, const char *proto, size_t protolen) { + if (0 != protolen && !buffer_is_equal_caseless_string(con->uri.scheme, proto, protolen)) { + /* update scheme if X-Forwarded-Proto is set + * Limitations: + * - Only "http" or "https" are currently accepted since the request to lighttpd currently has to + * be HTTP/1.0 or HTTP/1.1 using http or https. If this is changed, then the scheme from this + * untrusted header must be checked to contain only alphanumeric characters, and to be a + * reasonable length, e.g. < 256 chars. + * - con->uri.scheme is not reset in mod_extforward_restore() but is currently not an issues since + * con->uri.scheme will be reset by next request. If a new module uses con->uri.scheme in the + * handle_request_done hook, then should evaluate if that module should use the forwarded value + * (probably) or the original value. + */ + if (extforward_check_proxy) { + http_header_env_set(con, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO"), CONST_BUF_LEN(con->uri.scheme)); + } + if (0 == buffer_caseless_compare(proto, protolen, CONST_STR_LEN("https"))) { + buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("https")); + config_cond_cache_reset_item(srv, con, COMP_HTTP_SCHEME); + } else if (0 == buffer_caseless_compare(proto, protolen, CONST_STR_LEN("http"))) { + buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("http")); + config_cond_cache_reset_item(srv, con, COMP_HTTP_SCHEME); + } + } +} + +static handler_t mod_extforward_X_Forwarded_For(server *srv, connection *con, plugin_data *p, buffer *x_forwarded_for) { + /* build forward_array from forwarded data_string */ + array *forward_array = extract_forward_array(x_forwarded_for); + const char *real_remote_addr = last_not_in_array(forward_array, p); + if (real_remote_addr != NULL) { /* parsed */ + /* get scheme if X-Forwarded-Proto is set + * Limitations: + * - X-Forwarded-Proto may or may not be set by proxies, even if X-Forwarded-For is set + * - X-Forwarded-Proto may be a comma-separated list if there are multiple proxies, + * but the historical behavior of the code below only honored it if there was exactly one value + * (not done: walking backwards in X-Forwarded-Proto the same num of steps + * as in X-Forwarded-For to find proto set by last trusted proxy) + */ + buffer *x_forwarded_proto = http_header_request_get(con, HTTP_HEADER_X_FORWARDED_PROTO, CONST_STR_LEN("X-Forwarded-Proto")); + if (mod_extforward_set_addr(srv, con, p, real_remote_addr) && NULL != x_forwarded_proto) { + mod_extforward_set_proto(srv, con, CONST_BUF_LEN(x_forwarded_proto)); + } + } + array_free(forward_array); + return HANDLER_GO_ON; +} + +static int find_end_quoted_string (const char * const s, int i) { + do { + ++i; + } while (s[i] != '"' && s[i] != '\0' && (s[i] != '\\' || s[++i] != '\0')); + return i; +} + +static int find_next_semicolon_or_comma_or_eq (const char * const s, int i) { + for (; s[i] != '=' && s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) { + if (s[i] == '"') { + i = find_end_quoted_string(s, i); + if (s[i] == '\0') return -1; + } + } + return i; +} + +static int find_next_semicolon_or_comma (const char * const s, int i) { + for (; s[i] != ';' && s[i] != ',' && s[i] != '\0'; ++i) { + if (s[i] == '"') { + i = find_end_quoted_string(s, i); + if (s[i] == '\0') return -1; + } + } + return i; +} + +static int buffer_backslash_unescape (buffer * const b) { + /* (future: might move to buffer.c) */ + size_t j = 0; + size_t len = buffer_string_length(b); + char *p = memchr(b->ptr, '\\', len); + + if (NULL == p) return 1; /*(nothing to do)*/ + + len -= (size_t)(p - b->ptr); + for (size_t i = 0; i < len; ++i) { + if (p[i] == '\\') { + if (++i == len) return 0; /*(invalid trailing backslash)*/ + } + p[j++] = p[i]; + } + buffer_string_set_length(b, (size_t)(p+j - b->ptr)); + return 1; +} + +static handler_t mod_extforward_Forwarded (server *srv, connection *con, plugin_data *p, buffer *forwarded) { + /* HTTP list need not consist of param=value tokens, + * but this routine expect such for HTTP Forwarded header + * Since info in each set of params is only used if from + * admin-specified trusted proxy: + * - invalid param=value tokens are ignored and skipped + * - not checking "for" exists in each set of params + * - not checking for duplicated params in each set of params + * - not checking canonical form of addr (also might be obfuscated) + * - obfuscated tokens permitted in chain, though end of trust is expected + * to be non-obfuscated IP for mod_extforward to masquerade as remote IP + * future: since (potentially) trusted proxies begin at end of string, + * it might be better to parse from end of string rather than parsing from + * beginning. Doing so would also allow reducing arbitrary param limit + * to number of params permitted per proxy. + */ + char * const s = forwarded->ptr; + int i = 0, j = -1, v, vlen, k, klen; + int used = (int)buffer_string_length(forwarded); + int ofor = -1, oproto, ohost, oby, oremote_user; + int offsets[256];/*(~50 params is more than reasonably expected to handle)*/ + while (i < used) { + while (s[i] == ' ' || s[i] == '\t') ++i; + if (s[i] == ';') { ++i; continue; } + if (s[i] == ',') { + if (j >= (int)(sizeof(offsets)/sizeof(int))) break; + offsets[++j] = -1; /*("offset" separating params from next proxy)*/ + ++i; + continue; + } + if (s[i] == '\0') break; + + k = i; + i = find_next_semicolon_or_comma_or_eq(s, i); + if (i < 0) { + /*(reject IP spoofing if attacker sets improper quoted-string)*/ + log_error_write(srv, __FILE__, __LINE__, "s", + "invalid quoted-string in Forwarded header"); + con->http_status = 400; /* Bad Request */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + if (s[i] != '=') continue; + klen = i - k; + v = ++i; + i = find_next_semicolon_or_comma(s, i); + if (i < 0) { + /*(reject IP spoofing if attacker sets improper quoted-string)*/ + log_error_write(srv, __FILE__, __LINE__, "s", + "invalid quoted-string in Forwarded header"); + con->http_status = 400; /* Bad Request */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + vlen = i - v; /* might be 0 */ + + /* have k, klen, v, vlen + * (might contain quoted string) (contents not validated or decoded) + * (might be repeated k) + */ + if (0 == klen) continue; /* invalid k */ + if (j >= (int)(sizeof(offsets)/sizeof(int))-4) break; + offsets[j+1] = k; + offsets[j+2] = klen; + offsets[j+3] = v; + offsets[j+4] = vlen; + j += 4; + } + + if (j >= (int)(sizeof(offsets)/sizeof(int))-4) { + /* error processing Forwarded; too many params; fail closed */ + log_error_write(srv, __FILE__, __LINE__, "s", + "Too many params in Forwarded header"); + con->http_status = 400; /* Bad Request */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + + if (-1 == j) return HANDLER_GO_ON; /* make no changes */ + used = j+1; + offsets[used] = -1; /* mark end of last set of params */ + + while (j >= 4) { /*(param=value pairs)*/ + if (-1 == offsets[j]) { --j; continue; } + do { + j -= 3; /*(k, klen, v, vlen come in sets of 4)*/ + } while ((3 != offsets[j+1] /* 3 == sizeof("for")-1 */ + || 0 != buffer_caseless_compare(s+offsets[j], 3, "for", 3)) + && 0 != j-- && -1 != offsets[j]); + if (j < 0) break; + if (-1 == offsets[j]) { --j; continue; } + + /* remove trailing spaces/tabs and double-quotes from string + * (note: not unescaping backslash escapes in quoted string) */ + v = offsets[j+2]; + vlen = v + offsets[j+3]; + while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; + if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { + offsets[j+2] = ++v; + --vlen; + if (s[v] == '[') { + /* remove "[]" surrounding IPv6, as well as (optional) port + * (assumes properly formatted IPv6 addr from trusted proxy) */ + ++v; + do { --vlen; } while (vlen > v && s[vlen] != ']'); + if (v == vlen) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Invalid IPv6 addr in Forwarded header"); + con->http_status = 400; /* Bad Request */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + } + else if (s[v] != '_' && s[v] != '/' && s[v] != 'u') { + /* remove (optional) port from non-obfuscated IPv4 */ + for (klen=vlen, vlen=v; vlen < klen && s[vlen] != ':'; ++vlen) ; + } + offsets[j+2] = v; + } + offsets[j+3] = vlen - v; + + /* obfuscated ipstr and obfuscated port are also accepted here, as + * is path to unix domain socket, but note that backslash escapes + * in quoted-string were not unescaped above. Also, if obfuscated + * identifiers are rotated by proxies as recommended by RFC, then + * maintaining list of trusted identifiers is non-trivial and is not + * attempted by this module. */ + + if (v != vlen) { + int trusted = is_proxy_trusted(p, s+v, vlen-v); + + if (s[v] != '_' && s[v] != '/' + && (7 != (vlen - v) || 0 != memcmp(s+v, "unknown", 7))) { + ofor = j; /* save most recent non-obfuscated ipstr */ + } + + if (!trusted) break; + } + + do { --j; } while (j > 0 && -1 != offsets[j]); + if (j <= 0) break; + --j; + } + + if (-1 != ofor) { + /* C funcs getaddrinfo(), inet_addr() require '\0'-terminated IP str */ + char *ipend = s+offsets[ofor+2]+offsets[ofor+3]; + char c = *ipend; + int rc; + *ipend = '\0'; + rc = mod_extforward_set_addr(srv, con, p, s+offsets[ofor+2]); + *ipend = c; + if (!rc) return HANDLER_GO_ON; /* invalid addr; make no changes */ + } + else { + return HANDLER_GO_ON; /* make no changes */ + } + + /* parse out params associated with for=<ip> addr set above */ + oproto = ohost = oby = oremote_user = -1; + j = ofor; + if (j > 0) { do { --j; } while (j > 0 && -1 != offsets[j]); } + if (-1 == offsets[j]) ++j; + if (j == ofor) j += 4; + for (; -1 != offsets[j]; j+=4) { /*(k, klen, v, vlen come in sets of 4)*/ + switch (offsets[j+1]) { + #if 0 + case 2: + if (0 == buffer_caseless_compare(s+offsets[j],2,"by",2)) + oby = j; + break; + #endif + #if 0 + /*(already handled above to find IP prior to earliest trusted proxy)*/ + case 3: + if (0 == buffer_caseless_compare(s+offsets[j],3,"for",3)) + ofor = j; + break; + #endif + case 4: + if (0 == buffer_caseless_compare(s+offsets[j],4,"host",4)) + ohost = j; + break; + case 5: + if (0 == buffer_caseless_compare(s+offsets[j],5,"proto",5)) + oproto = j; + break; + case 11: + if (0 == buffer_caseless_compare(s+offsets[j],11,"remote_user",11)) + oremote_user = j; + break; + default: + break; + } + } + i = ++j; + + if (-1 != oproto) { + /* remove trailing spaces/tabs, and double-quotes from proto + * (note: not unescaping backslash escapes in quoted string) */ + v = offsets[oproto+2]; + vlen = v + offsets[oproto+3]; + while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; + if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { ++v; --vlen; } + mod_extforward_set_proto(srv, con, s+v, vlen-v); + } + + if (p->conf.opts & PROXY_FORWARDED_HOST) { + /* Limitations: + * - con->request.http_host is not reset in mod_extforward_restore() + * but is currently not an issues since con->request.http_host will be + * reset by next request. If a new module uses con->request.http_host + * in the handle_request_done hook, then should evaluate if that + * module should use the forwarded value (probably) or original value. + * - due to need to decode and unescape host=..., some extra work is + * done in the case where host matches current Host header. + * future: might add code to check if Host has actually changed or not + * + * note: change host after mod_extforward_set_proto() since that may + * affect scheme port used in http_request_host_policy() host + * normalization + */ + + /* find host param set by earliest trusted proxy in proxy chain + * (host might be changed anywhere along the chain) */ + for (j = i; j < used && -1 == ohost; ) { + if (-1 == offsets[j]) { ++j; continue; } + if (4 == offsets[j+1] + && 0 == buffer_caseless_compare(s+offsets[j], 4, "host", 4)) + ohost = j; + j += 4; /*(k, klen, v, vlen come in sets of 4)*/ + } + if (-1 != ohost) { + if (extforward_check_proxy + && !buffer_string_is_empty(con->request.http_host)) { + http_header_env_set(con, + CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_HOST"), + CONST_BUF_LEN(con->request.http_host)); + } + /* remove trailing spaces/tabs, and double-quotes from host */ + v = offsets[ohost+2]; + vlen = v + offsets[ohost+3]; + while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; + if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { + ++v; --vlen; + buffer_copy_string_len(con->request.http_host, s+v, vlen-v); + if (!buffer_backslash_unescape(con->request.http_host)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "invalid host= value in Forwarded header"); + con->http_status = 400; /* Bad Request */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + } + else { + buffer_copy_string_len(con->request.http_host, s+v, vlen-v); + } + + if (0 != http_request_host_policy(con, con->request.http_host, + con->uri.scheme)) { + /*(reject invalid chars in Host)*/ + log_error_write(srv, __FILE__, __LINE__, "s", + "invalid host= value in Forwarded header"); + con->http_status = 400; /* Bad Request */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + + config_cond_cache_reset_item(srv, con, COMP_HTTP_HOST); + } + } + + if (p->conf.opts & PROXY_FORWARDED_REMOTE_USER) { + /* find remote_user param set by closest proxy + * (auth may have been handled by any trusted proxy in proxy chain) */ + for (j = i; j < used; ) { + if (-1 == offsets[j]) { ++j; continue; } + if (11 == offsets[j+1] + && 0==buffer_caseless_compare(s+offsets[j],11,"remote_user",11)) + oremote_user = j; + j += 4; /*(k, klen, v, vlen come in sets of 4)*/ + } + if (-1 != oremote_user) { + /* ???: should we also support param for auth_type ??? */ + /* remove trailing spaces/tabs, and double-quotes from remote_user*/ + v = offsets[oremote_user+2]; + vlen = v + offsets[oremote_user+3]; + while (vlen > v && (s[vlen-1] == ' ' || s[vlen-1] == '\t')) --vlen; + if (vlen > v+1 && s[v] == '"' && s[vlen-1] == '"') { + buffer *euser; + ++v; --vlen; + http_header_env_set(con, + CONST_STR_LEN("REMOTE_USER"), s+v, vlen-v); + euser = http_header_env_get(con, CONST_STR_LEN("REMOTE_USER")); + force_assert(NULL != euser); + if (!buffer_backslash_unescape(euser)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "invalid remote_user= value in Forwarded header"); + con->http_status = 400; /* Bad Request */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + } + else { + http_header_env_set(con, + CONST_STR_LEN("REMOTE_USER"), s+v, vlen-v); + } + } + } + + #if 0 + if ((p->conf.opts & PROXY_FORWARDED_CREATE_XFF) + && NULL == http_header_request_get(con, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For"))) { + /* create X-Forwarded-For if not present + * (and at least original connecting IP is a trusted proxy) */ + buffer *xff = srv->tmp_buf; + buffer_clear(xff); + for (j = 0; j < used; ) { + if (-1 == offsets[j]) { ++j; continue; } + if (3 == offsets[j+1] + && 0 == buffer_caseless_compare(s+offsets[j], 3, "for", 3)) { + if (!buffer_string_is_empty(xff)) + buffer_append_string_len(xff, CONST_STR_LEN(", ")); + /* quoted-string, IPv6 brackets, and :port already removed */ + v = offsets[j+2]; + vlen = offsets[j+3]; + buffer_append_string_len(xff, s+v, vlen); + if (s[v-1] != '=') { /*(must have been quoted-string)*/ + char *x = + memchr(xff->ptr+buffer_string_length(xff)-vlen,'\\',vlen); + if (NULL != x) { /* backslash unescape in-place */ + for (v = 0; x[v]; ++x) { + if (x[v] == '\\' && x[++v] == '\0') + break; /*(invalid trailing backslash)*/ + *x = x[v]; + } + buffer_string_set_length(xff, x - xff->ptr); + } + } + /* skip to next group; take first "for=..." in group + * (should be 0 or 1 "for=..." per group, but not trusted) */ + do { j += 4; } while (-1 != offsets[j]); + ++j; + continue; + } + j += 4; /*(k, klen, v, vlen come in sets of 4)*/ + } + http_header_request_set(con, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For"), CONST_BUF_LEN(xff)); + } + #endif + + return HANDLER_GO_ON; +} + +URIHANDLER_FUNC(mod_extforward_uri_handler) { + plugin_data *p = p_d; + buffer *forwarded = NULL; + handler_ctx *hctx = con->plugin_ctx[p->id]; + int is_forwarded_header = 0; + + mod_extforward_patch_connection(srv, con, p); + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", + "-- mod_extforward_uri_handler called"); + } + + if (p->conf.hap_PROXY_ssl_client_verify) { + data_string *ds; + if (NULL != hctx && hctx->ssl_client_verify && NULL != hctx->env + && NULL != (ds = (data_string *)array_get_element(hctx->env, "SSL_CLIENT_S_DN_CN"))) { + http_header_env_set(con, + CONST_STR_LEN("SSL_CLIENT_VERIFY"), + CONST_STR_LEN("SUCCESS")); + http_header_env_set(con, + CONST_STR_LEN("REMOTE_USER"), + CONST_BUF_LEN(ds->value)); + http_header_env_set(con, + CONST_STR_LEN("AUTH_TYPE"), + CONST_STR_LEN("SSL_CLIENT_VERIFY")); + } else { + http_header_env_set(con, + CONST_STR_LEN("SSL_CLIENT_VERIFY"), + CONST_STR_LEN("NONE")); + } + } + + for (size_t k = 0; k < p->conf.headers->used && NULL == forwarded; ++k) { + buffer *hdr = ((data_string *)p->conf.headers->data[k])->value; + forwarded = http_header_request_get(con, HTTP_HEADER_UNSPECIFIED, CONST_BUF_LEN(hdr)); + if (forwarded) { + is_forwarded_header = buffer_is_equal_caseless_string(hdr, CONST_STR_LEN("Forwarded")); + break; + } + } + if (NULL == forwarded) { + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "no forward header found, skipping"); + } + + return HANDLER_GO_ON; + } + + /* if the remote ip itself is not trusted, then do nothing */ + if (!is_connection_trusted(con, p)) { + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "remote address", con->dst_addr_buf, "is NOT a trusted proxy, skipping"); + } + + return HANDLER_GO_ON; + } + + if (is_forwarded_header) { + return mod_extforward_Forwarded(srv, con, p, forwarded); + } + + return mod_extforward_X_Forwarded_For(srv, con, p, forwarded); +} + + +CONNECTION_FUNC(mod_extforward_handle_request_env) { + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + UNUSED(srv); + if (NULL == hctx || NULL == hctx->env) return HANDLER_GO_ON; + for (size_t i=0; i < hctx->env->used; ++i) { + /* note: replaces values which may have been set by mod_openssl + * (when mod_extforward is listed after mod_openssl in server.modules)*/ + data_string *ds = (data_string *)hctx->env->data[i]; + http_header_env_set(con, + CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); + } + return HANDLER_GO_ON; +} + + +CONNECTION_FUNC(mod_extforward_restore) { + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + + if (!hctx) return HANDLER_GO_ON; + + if (NULL != hctx->saved_network_read) { + con->network_read = hctx->saved_network_read; + hctx->saved_network_read = NULL; + } + + if (NULL != hctx->saved_remote_addr_buf) { + con->dst_addr = hctx->saved_remote_addr; + buffer_free(con->dst_addr_buf); + con->dst_addr_buf = hctx->saved_remote_addr_buf; + hctx->saved_remote_addr_buf = NULL; + /* Now, clean the conf_cond cache, because we may have changed the results of tests */ + config_cond_cache_reset_item(srv, con, COMP_HTTP_REMOTE_IP); + } + + if (NULL == hctx->env) { + handler_ctx_free(hctx); + con->plugin_ctx[p->id] = NULL; + } + + return HANDLER_GO_ON; +} + + +CONNECTION_FUNC(mod_extforward_handle_con_close) +{ + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + UNUSED(srv); + if (NULL != hctx) { + if (NULL != hctx->saved_network_read) { + con->network_read = hctx->saved_network_read; + } + if (NULL != hctx->saved_remote_addr_buf) { + con->dst_addr = hctx->saved_remote_addr; + buffer_free(con->dst_addr_buf); + con->dst_addr_buf = hctx->saved_remote_addr_buf; + } + if (NULL != hctx->env) { + array_free(hctx->env); + } + handler_ctx_free(hctx); + con->plugin_ctx[p->id] = NULL; + } + + return HANDLER_GO_ON; +} + + +static int mod_extforward_network_read (server *srv, connection *con, chunkqueue *cq, off_t max_bytes); + +CONNECTION_FUNC(mod_extforward_handle_con_accept) +{ + plugin_data *p = p_d; + mod_extforward_patch_connection(srv, con, p); + if (!p->conf.hap_PROXY) return HANDLER_GO_ON; + if (is_connection_trusted(con, p)) { + handler_ctx *hctx = handler_ctx_init(); + con->plugin_ctx[p->id] = hctx; + hctx->saved_network_read = con->network_read; + con->network_read = mod_extforward_network_read; + } + else { + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "remote address", con->dst_addr_buf, + "is NOT a trusted proxy, skipping"); + } + } + return HANDLER_GO_ON; +} + + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_extforward_plugin_init(plugin *p); +int mod_extforward_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("extforward"); + + p->init = mod_extforward_init; + p->handle_connection_accept = mod_extforward_handle_con_accept; + p->handle_uri_raw = mod_extforward_uri_handler; + p->handle_request_env = mod_extforward_handle_request_env; + p->handle_request_done = mod_extforward_restore; + p->connection_reset = mod_extforward_restore; + p->handle_connection_close = mod_extforward_handle_con_close; + p->set_defaults = mod_extforward_set_defaults; + p->cleanup = mod_extforward_free; + + p->data = NULL; + + return 0; +} + + + + +/* Modified from: + * http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt + * +9. Sample code + +The code below is an example of how a receiver may deal with both versions of +the protocol header for TCP over IPv4 or IPv6. The function is supposed to be +called upon a read event. Addresses may be directly copied into their final +memory location since they're transported in network byte order. The sending +side is even simpler and can easily be deduced from this sample code. + * + */ + +union hap_PROXY_hdr { + struct { + char line[108]; + } v1; + struct { + uint8_t sig[12]; + uint8_t ver_cmd; + uint8_t fam; + uint16_t len; + union { + struct { /* for TCP/UDP over IPv4, len = 12 */ + uint32_t src_addr; + uint32_t dst_addr; + uint16_t src_port; + uint16_t dst_port; + } ip4; + struct { /* for TCP/UDP over IPv6, len = 36 */ + uint8_t src_addr[16]; + uint8_t dst_addr[16]; + uint16_t src_port; + uint16_t dst_port; + } ip6; + struct { /* for AF_UNIX sockets, len = 216 */ + uint8_t src_addr[108]; + uint8_t dst_addr[108]; + } unx; + } addr; + } v2; +}; + +/* +If the length specified in the PROXY protocol header indicates that additional +bytes are part of the header beyond the address information, a receiver may +choose to skip over and ignore those bytes, or attempt to interpret those +bytes. + +The information in those bytes will be arranged in Type-Length-Value (TLV +vectors) in the following format. The first byte is the Type of the vector. +The second two bytes represent the length in bytes of the value (not included +the Type and Length bytes), and following the length field is the number of +bytes specified by the length. + */ +struct pp2_tlv { + uint8_t type; + uint8_t length_hi; + uint8_t length_lo; + /*uint8_t value[0];*//* C99 zero-length array */ +}; + +/* +The following types have already been registered for the <type> field : + */ + +#define PP2_TYPE_ALPN 0x01 +#define PP2_TYPE_AUTHORITY 0x02 +#define PP2_TYPE_CRC32C 0x03 +#define PP2_TYPE_NOOP 0x04 +#define PP2_TYPE_SSL 0x20 +#define PP2_SUBTYPE_SSL_VERSION 0x21 +#define PP2_SUBTYPE_SSL_CN 0x22 +#define PP2_SUBTYPE_SSL_CIPHER 0x23 +#define PP2_SUBTYPE_SSL_SIG_ALG 0x24 +#define PP2_SUBTYPE_SSL_KEY_ALG 0x25 +#define PP2_TYPE_NETNS 0x30 + +/* +For the type PP2_TYPE_SSL, the value is itselv a defined like this : + */ + +struct pp2_tlv_ssl { + uint8_t client; + uint32_t verify; + /*struct pp2_tlv sub_tlv[0];*//* C99 zero-length array */ +}; + +/* +And the <client> field is made of a bit field from the following values, +indicating which element is present : + */ + +#define PP2_CLIENT_SSL 0x01 +#define PP2_CLIENT_CERT_CONN 0x02 +#define PP2_CLIENT_CERT_SESS 0x04 + + + + +#ifndef MSG_DONTWAIT +#define MSG_DONTWAIT 0 +#endif +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +/* returns 0 if needs to poll, <0 upon error or >0 is protocol vers (success) */ +static int hap_PROXY_recv (const int fd, union hap_PROXY_hdr * const hdr, const int family, const int so_type) +{ + static const char v2sig[12] = + "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"; + + ssize_t ret; + size_t sz; + int ver; + + do { + ret = recv(fd, hdr, sizeof(*hdr), MSG_PEEK|MSG_DONTWAIT|MSG_NOSIGNAL); + } while (-1 == ret && errno == EINTR); + + if (-1 == ret) + return (errno == EAGAIN + #ifdef EWOULDBLOCK + #if EAGAIN != EWOULDBLOCK + || errno == EWOULDBLOCK + #endif + #endif + ) ? 0 : -1; + + if (ret >= 16 && 0 == memcmp(&hdr->v2, v2sig, 12) + && (hdr->v2.ver_cmd & 0xF0) == 0x20) { + ver = 2; + sz = 16 + (size_t)ntohs(hdr->v2.len); + if ((size_t)ret < sz) + return -2; /* truncated or too large header */ + + switch (hdr->v2.ver_cmd & 0xF) { + case 0x01: break; /* PROXY command */ + case 0x00: break; /* LOCAL command */ + default: return -2; /* not a supported command */ + } + } + else if (ret >= 8 && 0 == memcmp(hdr->v1.line, "PROXY", 5)) { + const char *end = memchr(hdr->v1.line, '\r', ret - 1); + if (!end || end[1] != '\n') + return -2; /* partial or invalid header */ + ver = 1; + sz = (size_t)(end + 2 - hdr->v1.line); /* skip header + CRLF */ + } + else { + /* Wrong protocol */ + return -2; + } + + /* we need to consume the appropriate amount of data from the socket + * (overwrites existing contents of hdr with same data) */ + UNUSED(family); + UNUSED(so_type); + do { + #if defined(MSG_TRUNC) && defined(__linux__) + if ((family==AF_INET || family==AF_INET6) && so_type == SOCK_STREAM) { + ret = recv(fd, hdr, sz, MSG_TRUNC|MSG_DONTWAIT|MSG_NOSIGNAL); + if (ret >= 0 || errno != EINVAL) continue; + } + #endif + ret = recv(fd, hdr, sz, MSG_DONTWAIT|MSG_NOSIGNAL); + } while (-1 == ret && errno == EINTR); + if (ret < 0) return -1; + if (ret != (ssize_t)sz) { + errno = EIO; /*(partial read; valid but unexpected; not handled)*/ + return -1; + } + if (1 == ver) hdr->v1.line[sz-2] = '\0'; /*terminate str to ease parsing*/ + return ver; +} + + +static int mod_extforward_hap_PROXY_v1 (connection * const con, + union hap_PROXY_hdr * const hdr) +{ + #ifdef __COVERITY__ + __coverity_tainted_data_sink__(hdr); + #endif + + /* samples + * "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n" + * "PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n" + * "PROXY UNKNOWN\r\n" + * "PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n" + */ + char *s = hdr->v1.line + sizeof("PROXY")-1; /*checked in hap_PROXY_recv()*/ + char *src_addr, *dst_addr, *src_port, *dst_port, *e; + int family; + long src_lport, dst_lport; + if (*s != ' ') return -1; + ++s; + if (s[0] == 'T' && s[1] == 'C' && s[2] == 'P' && s[4] == ' ') { + if (s[3] == '4') { + family = AF_INET; + } else if (s[3] == '6') { + family = AF_INET6; + } + else { + return -1; + } + s += 5; + } + else if (0 == memcmp(s, "UNKNOWN", sizeof("UNKNOWN")-1) + && (s[7] == '\0' || s[7] == ' ')) { + return 0; /* keep local connection address */ + } + else { + return -1; + } + + /*(strsep() should be fairly portable, but is not standard)*/ + src_addr = s; + dst_addr = strchr(src_addr, ' '); + if (NULL == dst_addr) return -1; + *dst_addr++ = '\0'; + src_port = strchr(dst_addr, ' '); + if (NULL == src_port) return -1; + *src_port++ = '\0'; + dst_port = strchr(src_port, ' '); + if (NULL == dst_port) return -1; + *dst_port++ = '\0'; + + src_lport = strtol(src_port, &e, 10); + if (src_lport <= 0 || src_lport > USHRT_MAX || *e != '\0') return -1; + dst_lport = strtol(dst_port, &e, 10); + if (dst_lport <= 0 || dst_lport > USHRT_MAX || *e != '\0') return -1; + + if (1 != sock_addr_inet_pton(&con->dst_addr, + src_addr, family, (unsigned short)src_lport)) + return -1; + /* Forwarded by=... could be saved here. + * (see additional comments in mod_extforward_hap_PROXY_v2()) */ + + /* re-parse addr to string to normalize + * (instead of trusting PROXY to provide canonicalized src_addr string) + * (should prefer PROXY v2 protocol if concerned about performance) */ + sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr); + + return 0; +} + + +static int mod_extforward_hap_PROXY_v2 (connection * const con, + union hap_PROXY_hdr * const hdr) +{ + #ifdef __COVERITY__ + __coverity_tainted_data_sink__(hdr); + #endif + + /* If HAProxy-PROXY protocol used, then lighttpd acts as transparent proxy, + * masquerading as servicing the client IP provided in by HAProxy-PROXY hdr. + * The connecting con->dst_addr and con->dst_addr_buf are not saved here, + * so that info is lost unless getsockname() and getpeername() are used. + * One result is that mod_proxy will use the masqueraded IP instead of the + * actual IP when updated Forwarded and X-Forwarded-For (but if actual + * connection IPs needed, better to save the info here rather than use + * syscalls to retrieve the info later). + * (Exception: con->dst_addr can be further changed if mod_extforward parses + * Forwaded or X-Forwarded-For request headers later, after request headers + * have been received.) + */ + + /* Forwarded by=... could be saved here. The by param is for backends to be + * able to construct URIs for that interface (interface on server which + * received request and made PROXY connection here), though that server + * should provide that information in updated Forwarded or X-Forwarded-For + * HTTP headers */ + /*struct sockaddr_storage by;*/ + + /* Addresses provided by HAProxy-PROXY protocol are in network byte order. + * Note: addr info is not validated, so do not accept HAProxy-PROXY + * protocol from untrusted servers. For example, untrusted servers from + * which HAProxy-PROXY protocol is accepted (don't do that) could pretend + * to be from the internal network and might thereby bypass security policy. + */ + + /* (Clear con->dst_addr with memset() in case actual and proxies IPs + * are different domains, e.g. one is IPv4 and the other is IPv6) */ + + struct pp2_tlv *tlv; + uint32_t sz = ntohs(hdr->v2.len); + uint32_t len = 0; + + switch (hdr->v2.ver_cmd & 0xF) { + case 0x01: break; /* PROXY command */ + case 0x00: return 0;/* LOCAL command; keep local connection address */ + default: return -1;/* should not happen; validated in hap_PROXY_recv()*/ + } + + /* PROXY command */ + + switch (hdr->v2.fam) { + case 0x11: /* TCPv4 */ + sock_addr_assign(&con->dst_addr, AF_INET, hdr->v2.addr.ip4.src_port, + &hdr->v2.addr.ip4.src_addr); + sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr); + #if 0 + ((struct sockaddr_in *)&by)->sin_family = AF_INET; + ((struct sockaddr_in *)&by)->sin_addr.s_addr = + hdr->v2.addr.ip4.dst_addr; + ((struct sockaddr_in *)&by)->sin_port = + hdr->v2.addr.ip4.dst_port; + #endif + len = (uint32_t)sizeof(hdr->v2.addr.ip4); + break; + #ifdef HAVE_IPV6 + case 0x21: /* TCPv6 */ + sock_addr_assign(&con->dst_addr, AF_INET6, hdr->v2.addr.ip6.src_port, + &hdr->v2.addr.ip6.src_addr); + sock_addr_inet_ntop_copy_buffer(con->dst_addr_buf, &con->dst_addr); + #if 0 + ((struct sockaddr_in6 *)&by)->sin6_family = AF_INET6; + memcpy(&((struct sockaddr_in6 *)&by)->sin6_addr, + hdr->v2.addr.ip6.dst_addr, 16); + ((struct sockaddr_in6 *)&by)->sin6_port = + hdr->v2.addr.ip6.dst_port; + #endif + len = (uint32_t)sizeof(hdr->v2.addr.ip6); + break; + #endif + #ifdef HAVE_SYS_UN_H + case 0x31: /* UNIX domain socket */ + { + char *src_addr = (char *)hdr->v2.addr.unx.src_addr; + char *z = memchr(src_addr, '\0', UNIX_PATH_MAX); + if (NULL == z) return -1; /* invalid addr; too long */ + len = (uint32_t)(z - src_addr + 1); /*(+1 for '\0')*/ + sock_addr_assign(&con->dst_addr, AF_UNIX, 0, src_addr); + buffer_copy_string_len(con->dst_addr_buf, src_addr, len); + } + #if 0 /*(dst_addr should be identical to src_addr for AF_UNIX)*/ + ((struct sockaddr_un *)&by)->sun_family = AF_UNIX; + memcpy(&((struct sockaddr_un *)&by)->sun_path, + hdr->v2.addr.unx.dst_addr, 108); + #endif + len = (uint32_t)sizeof(hdr->v2.addr.unx); + break; + #endif + default: /* keep local connection address; unsupported protocol */ + return 0; + } + + /* (optional) Type-Length-Value (TLV vectors) follow addresses */ + + tlv = (struct pp2_tlv *)((char *)hdr + 16); + for (sz -= len, len -= 3; sz >= 3; sz -= 3 + len) { + tlv = (struct pp2_tlv *)((char *)tlv + 3 + len); + len = ((uint32_t)tlv->length_hi << 8) | tlv->length_lo; + if (3 + len > sz) break; /*(invalid TLV)*/ + switch (tlv->type) { + #if 0 /*(not implemented here)*/ + case PP2_TYPE_ALPN: + case PP2_TYPE_AUTHORITY: + case PP2_TYPE_CRC32C: + #endif + case PP2_TYPE_SSL: { + static const uint32_t zero = 0; + handler_ctx *hctx = + con->plugin_ctx[mod_extforward_plugin_data_singleton->id]; + struct pp2_tlv_ssl *tlv_ssl = + (struct pp2_tlv_ssl *)(void *)((char *)tlv+3); + struct pp2_tlv *subtlv = tlv; + if (tlv_ssl->client & PP2_CLIENT_SSL) { + buffer_copy_string_len(con->proto, CONST_STR_LEN("https")); + } + if ((tlv_ssl->client & (PP2_CLIENT_CERT_CONN|PP2_CLIENT_CERT_SESS)) + && 0 == memcmp(&tlv_ssl->verify, &zero, 4)) { /* misaligned */ + hctx->ssl_client_verify = 1; + } + for (uint32_t subsz = len-5, n = 5; subsz >= 3; subsz -= 3 + n) { + subtlv = (struct pp2_tlv *)((char *)subtlv + 3 + n); + n = ((uint32_t)subtlv->length_hi << 8) | subtlv->length_lo; + if (3 + n > subsz) break; /*(invalid TLV)*/ + if (NULL == hctx->env) hctx->env = array_init(); + switch (subtlv->type) { + case PP2_SUBTYPE_SSL_VERSION: + array_set_key_value(hctx->env, + CONST_STR_LEN("SSL_PROTOCOL"), + (char *)subtlv+3, n); + break; + case PP2_SUBTYPE_SSL_CN: + /* (tlv_ssl->client & PP2_CLIENT_CERT_CONN) + * or + * (tlv_ssl->client & PP2_CLIENT_CERT_SESS) */ + array_set_key_value(hctx->env, + CONST_STR_LEN("SSL_CLIENT_S_DN_CN"), + (char *)subtlv+3, n); + break; + case PP2_SUBTYPE_SSL_CIPHER: + array_set_key_value(hctx->env, + CONST_STR_LEN("SSL_CIPHER"), + (char *)subtlv+3, n); + break; + case PP2_SUBTYPE_SSL_SIG_ALG: + array_set_key_value(hctx->env, + CONST_STR_LEN("SSL_SERVER_A_SIG"), + (char *)subtlv+3, n); + break; + case PP2_SUBTYPE_SSL_KEY_ALG: + array_set_key_value(hctx->env, + CONST_STR_LEN("SSL_SERVER_A_KEY"), + (char *)subtlv+3, n); + break; + default: + break; + } + } + break; + } + #if 0 /*(not implemented here)*/ + case PP2_TYPE_NETNS: + #endif + /*case PP2_TYPE_NOOP:*//* no-op */ + default: + break; + } + } + + return 0; +} + + +static int mod_extforward_network_read (server *srv, connection *con, + chunkqueue *cq, off_t max_bytes) +{ + /* XXX: when using hap-PROXY protocol, currently avoid overhead of setting + * _L_ environment variables for mod_proxy to accurately set Forwarded hdr + * In the future, might add config switch to enable doing this extra work */ + + union hap_PROXY_hdr hdr; + int rc = hap_PROXY_recv(con->fd, &hdr, + con->dst_addr.plain.sa_family, SOCK_STREAM); + switch (rc) { + case 2: rc = mod_extforward_hap_PROXY_v2(con, &hdr); break; + case 1: rc = mod_extforward_hap_PROXY_v1(con, &hdr); break; + case 0: return 0; /*(errno == EAGAIN || errno == EWOULDBLOCK)*/ + case -1: log_error_write(srv, __FILE__, __LINE__, "ss", + "hap-PROXY recv()", strerror(errno)); + rc = -1; break; + case -2: log_error_write(srv, __FILE__, __LINE__, "s", + "hap-PROXY proto received " + "invalid/unsupported request"); + /* fall through */ + default: rc = -1; break; + } + + mod_extforward_restore(srv, con, mod_extforward_plugin_data_singleton); + return (0 == rc) ? con->network_read(srv, con, cq, max_bytes) : rc; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_fastcgi.c b/data/lighttpd/lighttpd-1.4.53/src/mod_fastcgi.c new file mode 100644 index 000000000..901b0b2c1 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_fastcgi.c @@ -0,0 +1,542 @@ +#include "first.h" + +#include <sys/types.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "gw_backend.h" +typedef gw_plugin_config plugin_config; +typedef gw_plugin_data plugin_data; +typedef gw_handler_ctx handler_ctx; + +#include "base.h" +#include "buffer.h" +#include "fdevent.h" +#include "http_chunk.h" +#include "log.h" +#include "status_counter.h" + +#ifdef HAVE_FASTCGI_FASTCGI_H +# include <fastcgi/fastcgi.h> +#else +# ifdef HAVE_FASTCGI_H +# include <fastcgi.h> +# else +# include "fastcgi.h" +# endif +#endif /* HAVE_FASTCGI_FASTCGI_H */ + +#if GW_RESPONDER != FCGI_RESPONDER +#error "mismatched defines: (GW_RESPONDER != FCGI_RESPONDER)" +#endif +#if GW_AUTHORIZER != FCGI_AUTHORIZER +#error "mismatched defines: (GW_AUTHORIZER != FCGI_AUTHORIZER)" +#endif +#if GW_FILTER != FCGI_FILTER +#error "mismatched defines: (GW_FILTER != FCGI_FILTER)" +#endif + +SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) { + plugin_data *p = p_d; + data_unset *du; + size_t i = 0; + + config_values_t cv[] = { + { "fastcgi.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "fastcgi.debug", NULL, T_CONFIG_INT , T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "fastcgi.map-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "fastcgi.balance", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + force_assert(p->config_storage); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + force_assert(s); + s->exts = NULL; + s->exts_auth = NULL; + s->exts_resp = NULL; + s->debug = 0; + s->ext_mapping = array_init(); + + cv[0].destination = s->exts; /* not used; T_CONFIG_LOCAL */ + cv[1].destination = &(s->debug); + cv[2].destination = s->ext_mapping; + cv[3].destination = NULL; /* not used; T_CONFIG_LOCAL */ + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + du = array_get_element(config->value, "fastcgi.server"); + if (!gw_set_defaults_backend(srv, p, du, i, 0)) { + return HANDLER_ERROR; + } + + du = array_get_element(config->value, "fastcgi.balance"); + if (!gw_set_defaults_balance(srv, s, du)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +static int fcgi_env_add(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { + buffer *env = venv; + size_t len; + char len_enc[8]; + size_t len_enc_len = 0; + char *dst; + + if (!key || !val) return -1; + + len = key_len + val_len; + + len += key_len > 127 ? 4 : 1; + len += val_len > 127 ? 4 : 1; + + if (buffer_string_length(env) + len >= FCGI_MAX_LENGTH + sizeof(FCGI_BeginRequestRecord) + sizeof(FCGI_Header)) { + /** + * we can't append more headers, ignore it + */ + return -1; + } + + /** + * field length can be 31bit max + * + * HINT: this can't happen as FCGI_MAX_LENGTH is only 16bit + */ + force_assert(key_len < 0x7fffffffu); + force_assert(val_len < 0x7fffffffu); + + if (buffer_string_space(env) < len) { + size_t extend = env->size * 2 - buffer_string_length(env); + extend = extend > len ? extend : len + 4095; + buffer_string_prepare_append(env, extend); + } + + if (key_len > 127) { + len_enc[len_enc_len++] = ((key_len >> 24) & 0xff) | 0x80; + len_enc[len_enc_len++] = (key_len >> 16) & 0xff; + len_enc[len_enc_len++] = (key_len >> 8) & 0xff; + len_enc[len_enc_len++] = (key_len >> 0) & 0xff; + } else { + len_enc[len_enc_len++] = (key_len >> 0) & 0xff; + } + + if (val_len > 127) { + len_enc[len_enc_len++] = ((val_len >> 24) & 0xff) | 0x80; + len_enc[len_enc_len++] = (val_len >> 16) & 0xff; + len_enc[len_enc_len++] = (val_len >> 8) & 0xff; + len_enc[len_enc_len++] = (val_len >> 0) & 0xff; + } else { + len_enc[len_enc_len++] = (val_len >> 0) & 0xff; + } + + dst = buffer_string_prepare_append(env, len); + memcpy(dst, len_enc, len_enc_len); + memcpy(dst + len_enc_len, key, key_len); + memcpy(dst + len_enc_len + key_len, val, val_len); + buffer_commit(env, len); + + return 0; +} + +static void fcgi_header(FCGI_Header * header, unsigned char type, int request_id, int contentLength, unsigned char paddingLength) { + force_assert(contentLength <= FCGI_MAX_LENGTH); + + header->version = FCGI_VERSION_1; + header->type = type; + header->requestIdB0 = request_id & 0xff; + header->requestIdB1 = (request_id >> 8) & 0xff; + header->contentLengthB0 = contentLength & 0xff; + header->contentLengthB1 = (contentLength >> 8) & 0xff; + header->paddingLength = paddingLength; + header->reserved = 0; +} + +static handler_t fcgi_stdin_append(server *srv, handler_ctx *hctx) { + FCGI_Header header; + connection *con = hctx->remote_conn; + chunkqueue *req_cq = con->request_content_queue; + off_t offset, weWant; + const off_t req_cqlen = req_cq->bytes_in - req_cq->bytes_out; + int request_id = hctx->request_id; + UNUSED(srv); + + /* something to send ? */ + for (offset = 0; offset != req_cqlen; offset += weWant) { + weWant = req_cqlen - offset > FCGI_MAX_LENGTH ? FCGI_MAX_LENGTH : req_cqlen - offset; + + if (-1 != hctx->wb_reqlen) { + if (hctx->wb_reqlen >= 0) { + hctx->wb_reqlen += sizeof(header); + } else { + hctx->wb_reqlen -= sizeof(header); + } + } + + fcgi_header(&(header), FCGI_STDIN, request_id, weWant, 0); + (chunkqueue_is_empty(hctx->wb) || hctx->wb->first->type == MEM_CHUNK) /* else FILE_CHUNK for temp file */ + ? chunkqueue_append_mem(hctx->wb, (const char *)&header, sizeof(header)) + : chunkqueue_append_mem_min(hctx->wb, (const char *)&header, sizeof(header)); + chunkqueue_steal(hctx->wb, req_cq, weWant); + /*(hctx->wb_reqlen already includes content_length)*/ + } + + if (hctx->wb->bytes_in == hctx->wb_reqlen) { + /* terminate STDIN */ + /* (future: must defer ending FCGI_STDIN + * if might later upgrade protocols + * and then have more data to send) */ + fcgi_header(&(header), FCGI_STDIN, request_id, 0, 0); + chunkqueue_append_mem(hctx->wb, (const char *)&header, sizeof(header)); + hctx->wb_reqlen += (int)sizeof(header); + } + + return HANDLER_GO_ON; +} + +static handler_t fcgi_create_env(server *srv, handler_ctx *hctx) { + FCGI_BeginRequestRecord beginRecord; + FCGI_Header header; + int request_id; + + gw_host *host = hctx->host; + connection *con = hctx->remote_conn; + + http_cgi_opts opts = { + (hctx->gw_mode == FCGI_AUTHORIZER), + host->break_scriptfilename_for_php, + host->docroot, + host->strip_request_uri + }; + + size_t rsz = (size_t)(con->read_queue->bytes_out - hctx->wb->bytes_in); + buffer * const b = chunkqueue_prepend_buffer_open_sz(hctx->wb, rsz < 65536 ? rsz : con->header_len); + + /* send FCGI_BEGIN_REQUEST */ + + if (hctx->request_id == 0) { + hctx->request_id = 1; /* always use id 1 as we don't use multiplexing */ + } else { + log_error_write(srv, __FILE__, __LINE__, "sd", + "fcgi-request is already in use:", hctx->request_id); + } + request_id = hctx->request_id; + + fcgi_header(&(beginRecord.header), FCGI_BEGIN_REQUEST, request_id, sizeof(beginRecord.body), 0); + beginRecord.body.roleB0 = hctx->gw_mode; + beginRecord.body.roleB1 = 0; + beginRecord.body.flags = 0; + memset(beginRecord.body.reserved, 0, sizeof(beginRecord.body.reserved)); + + buffer_copy_string_len(b, (const char *)&beginRecord, sizeof(beginRecord)); + fcgi_header(&header, FCGI_PARAMS, request_id, 0, 0); /*(set aside space to fill in later)*/ + buffer_append_string_len(b, (const char *)&header, sizeof(header)); + + /* send FCGI_PARAMS */ + + if (0 != http_cgi_headers(srv, con, &opts, fcgi_env_add, b)) { + con->http_status = 400; + con->mode = DIRECT; + buffer_clear(b); + chunkqueue_remove_finished_chunks(hctx->wb); + return HANDLER_FINISHED; + } else { + fcgi_header(&(header), FCGI_PARAMS, request_id, + buffer_string_length(b) - sizeof(FCGI_BeginRequestRecord) - sizeof(FCGI_Header), 0); + memcpy(b->ptr+sizeof(FCGI_BeginRequestRecord), (const char *)&header, sizeof(header)); + + fcgi_header(&(header), FCGI_PARAMS, request_id, 0, 0); + buffer_append_string_len(b, (const char *)&header, sizeof(header)); + + hctx->wb_reqlen = buffer_string_length(b); + chunkqueue_prepend_buffer_commit(hctx->wb); + } + + if (con->request.content_length) { + /*chunkqueue_append_chunkqueue(hctx->wb, con->request_content_queue);*/ + if (con->request.content_length > 0) + hctx->wb_reqlen += con->request.content_length;/* (eventual) (minimal) total request size, not necessarily including all fcgi_headers around content length yet */ + else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/ + hctx->wb_reqlen = -hctx->wb_reqlen; + } + fcgi_stdin_append(srv, hctx); + + status_counter_inc(srv, CONST_STR_LEN("fastcgi.requests")); + return HANDLER_GO_ON; +} + +typedef struct { + unsigned int len; + int type; + int padding; + int request_id; +} fastcgi_response_packet; + +static int fastcgi_get_packet(server *srv, handler_ctx *hctx, fastcgi_response_packet *packet) { + FCGI_Header header; + size_t toread = sizeof(FCGI_Header), flen = 0; + off_t rblen = chunkqueue_length(hctx->rb); + if (rblen < (off_t)sizeof(FCGI_Header)) { + /* no header */ + if (hctx->conf.debug && 0 != rblen) { + log_error_write(srv, __FILE__, __LINE__, "sosds", "FastCGI: header too small:", rblen, "bytes <", sizeof(FCGI_Header), "bytes, waiting for more data"); + } + return -1; + } + + /* get at least the FastCGI header */ + for (chunk *c = hctx->rb->first; c; c = c->next) { + size_t weHave = buffer_string_length(c->mem) - c->offset; + if (weHave >= toread) { + memcpy((char *)&header + flen, c->mem->ptr + c->offset, toread); + break; + } + + memcpy((char *)&header + flen, c->mem->ptr + c->offset, weHave); + flen += weHave; + toread -= weHave; + } + + /* we have at least a header, now check how much we have to fetch */ + packet->len = (header.contentLengthB0 | (header.contentLengthB1 << 8)) + header.paddingLength; + packet->request_id = (header.requestIdB0 | (header.requestIdB1 << 8)); + packet->type = header.type; + packet->padding = header.paddingLength; + + if (packet->len > (unsigned int)rblen-sizeof(FCGI_Header)) { + return -1; /* we didn't get the full packet */ + } + + chunkqueue_mark_written(hctx->rb, sizeof(FCGI_Header)); + return 0; +} + +static void fastcgi_get_packet_body(buffer *b, handler_ctx *hctx, fastcgi_response_packet *packet) { + /* copy content; hctx->rb must contain at least packet->len content */ + size_t toread = packet->len - packet->padding; + buffer_string_prepare_append(b, toread); + for (chunk *c = hctx->rb->first; c; c = c->next) { + size_t weHave = buffer_string_length(c->mem) - c->offset; + if (weHave >= toread) { + buffer_append_string_len(b, c->mem->ptr + c->offset, toread); + break; + } + + buffer_append_string_len(b, c->mem->ptr + c->offset, weHave); + toread -= weHave; + } + chunkqueue_mark_written(hctx->rb, packet->len); +} + +static handler_t fcgi_recv_parse(server *srv, connection *con, struct http_response_opts_t *opts, buffer *b, size_t n) { + handler_ctx *hctx = (handler_ctx *)opts->pdata; + int fin = 0; + + if (0 == n) { + if (-1 == hctx->request_id) return HANDLER_FINISHED; /*(flag request ended)*/ + if (!(fdevent_event_get_interest(srv->ev, hctx->fd) & FDEVENT_IN) + && !(con->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_POLLRDHUP)) + return HANDLER_GO_ON; + log_error_write(srv, __FILE__, __LINE__, "ssdsb", + "unexpected end-of-file (perhaps the fastcgi process died):", + "pid:", hctx->proc->pid, + "socket:", hctx->proc->connection_name); + + return HANDLER_ERROR; + } + + chunkqueue_append_buffer(hctx->rb, b); + + /* + * parse the fastcgi packets and forward the content to the write-queue + * + */ + while (fin == 0) { + fastcgi_response_packet packet; + + /* check if we have at least one packet */ + if (0 != fastcgi_get_packet(srv, hctx, &packet)) { + /* no full packet */ + break; + } + + switch(packet.type) { + case FCGI_STDOUT: + if (packet.len == 0) break; + + /* is the header already finished */ + if (0 == con->file_started) { + /* split header from body */ + buffer *hdrs = hctx->response; + if (NULL == hdrs) { + hdrs = srv->tmp_buf; + buffer_clear(srv->tmp_buf); + } + fastcgi_get_packet_body(hdrs, hctx, &packet); + if (HANDLER_GO_ON != http_response_parse_headers(srv, con, &hctx->opts, hdrs)) { + hctx->send_content_body = 0; + fin = 1; + break; + } + if (0 == con->file_started) { + if (!hctx->response) { + hctx->response = chunk_buffer_acquire(); + buffer_copy_buffer(hctx->response, hdrs); + } + } + else if (hctx->gw_mode == GW_AUTHORIZER && + (con->http_status == 0 || con->http_status == 200)) { + /* authorizer approved request; ignore the content here */ + hctx->send_content_body = 0; + } + } else if (hctx->send_content_body) { + if (0 != http_chunk_transfer_cqlen(srv, con, hctx->rb, packet.len - packet.padding)) { + /* error writing to tempfile; + * truncate response or send 500 if nothing sent yet */ + fin = 1; + } + if (packet.padding) chunkqueue_mark_written(hctx->rb, packet.padding); + } else { + chunkqueue_mark_written(hctx->rb, packet.len); + } + break; + case FCGI_STDERR: + if (packet.len == 0) break; + + buffer_clear(srv->tmp_buf); + fastcgi_get_packet_body(srv->tmp_buf, hctx, &packet); + log_error_write_multiline_buffer(srv, __FILE__, __LINE__, srv->tmp_buf, "s", + "FastCGI-stderr:"); + + break; + case FCGI_END_REQUEST: + hctx->request_id = -1; /*(flag request ended)*/ + fin = 1; + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sd", + "FastCGI: header.type not handled: ", packet.type); + chunkqueue_mark_written(hctx->rb, packet.len); + break; + } + } + + return 0 == fin ? HANDLER_GO_ON : HANDLER_FINISHED; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int fcgi_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(exts); + PATCH(exts_auth); + PATCH(exts_resp); + PATCH(debug); + PATCH(balance); + PATCH(ext_mapping); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.server"))) { + PATCH(exts); + PATCH(exts_auth); + PATCH(exts_resp); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.debug"))) { + PATCH(debug); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.balance"))) { + PATCH(balance); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("fastcgi.map-extensions"))) { + PATCH(ext_mapping); + } + } + } + + return 0; +} +#undef PATCH + +static handler_t fcgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) { + plugin_data *p = p_d; + handler_t rc; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + fcgi_patch_connection(srv, con, p); + if (NULL == p->conf.exts) return HANDLER_GO_ON; + + rc = gw_check_extension(srv, con, p, uri_path_handler, 0); + if (HANDLER_GO_ON != rc) return rc; + + if (con->mode == p->id) { + handler_ctx *hctx = con->plugin_ctx[p->id]; + hctx->opts.backend = BACKEND_FASTCGI; + hctx->opts.parse = fcgi_recv_parse; + hctx->opts.pdata = hctx; + hctx->stdin_append = fcgi_stdin_append; + hctx->create_env = fcgi_create_env; + if (!hctx->rb) { + hctx->rb = chunkqueue_init(); + } + else { + chunkqueue_reset(hctx->rb); + } + } + + return HANDLER_GO_ON; +} + +/* uri-path handler */ +static handler_t fcgi_check_extension_1(server *srv, connection *con, void *p_d) { + return fcgi_check_extension(srv, con, p_d, 1); +} + +/* start request handler */ +static handler_t fcgi_check_extension_2(server *srv, connection *con, void *p_d) { + return fcgi_check_extension(srv, con, p_d, 0); +} + + +int mod_fastcgi_plugin_init(plugin *p); +int mod_fastcgi_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("fastcgi"); + + p->init = gw_init; + p->cleanup = gw_free; + p->set_defaults = mod_fastcgi_set_defaults; + p->connection_reset = gw_connection_reset; + p->handle_uri_clean = fcgi_check_extension_1; + p->handle_subrequest_start = fcgi_check_extension_2; + p->handle_subrequest = gw_handle_subrequest; + p->handle_trigger = gw_handle_trigger; + p->handle_waitpid = gw_handle_waitpid_cb; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_flv_streaming.c b/data/lighttpd/lighttpd-1.4.53/src/mod_flv_streaming.c new file mode 100644 index 000000000..cb6186f63 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_flv_streaming.c @@ -0,0 +1,224 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "http_chunk.h" +#include "http_header.h" + +#include "plugin.h" + +#include <stdlib.h> +#include <string.h> + +/* plugin config for all request/connections */ + +typedef struct { + array *extensions; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_flv_streaming_init) { + return calloc(1, sizeof(plugin_data)); +} + +/* detroy the plugin data */ +FREE_FUNC(mod_flv_streaming_free) { + plugin_data *p = p_d; + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + for (size_t i = 0; i < srv->config_context->used; ++i) { + plugin_config *s = p->config_storage[i]; + if (NULL == s) continue; + array_free(s->extensions); + free(s); + } + free(p->config_storage); + } + free(p); + UNUSED(srv); + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_flv_streaming_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "flv-streaming.extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->extensions = array_init(); + + cv[0].destination = s->extensions; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_vlist(s->extensions)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for flv-streaming.extensions; expected list of \"ext\""); + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_flv_streaming_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(extensions); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("flv-streaming.extensions"))) { + PATCH(extensions); + } + } + } + + return 0; +} +#undef PATCH + +static int split_get_params(array *get_params, buffer *qrystr) { + size_t is_key = 1, klen = 0; + char *key = qrystr->ptr, *val = NULL; + + if (buffer_string_is_empty(qrystr)) return 0; + for (size_t i = 0, len = buffer_string_length(qrystr); i <= len; ++i) { + switch(qrystr->ptr[i]) { + case '=': + if (is_key) { + val = qrystr->ptr + i + 1; + klen = (size_t)(qrystr->ptr + i - key); + is_key = 0; + } + + break; + case '&': + case '\0': /* fin symbol */ + if (!is_key) { + /* we need at least a = since the last & */ + array_insert_key_value(get_params, key, klen, val, qrystr->ptr + i - val); + } + + key = qrystr->ptr + i + 1; + val = NULL; + is_key = 1; + break; + } + } + + return 0; +} + +URIHANDLER_FUNC(mod_flv_streaming_path_handler) { + plugin_data *p = p_d; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + if (buffer_string_is_empty(con->physical.path)) return HANDLER_GO_ON; + + mod_flv_streaming_patch_connection(srv, con, p); + + if (!array_match_value_suffix(p->conf.extensions, con->physical.path)) { + /* not found */ + return HANDLER_GO_ON; + } + + { + data_string *get_param; + off_t start = 0, len = -1; + char *err = NULL; + /* if there is a start=[0-9]+ in the header use it as start, + * otherwise set start to beginning of file */ + /* if there is a end=[0-9]+ in the header use it as end pos, + * otherwise send rest of file, starting from start */ + + array_reset_data_strings(srv->split_vals); + split_get_params(srv->split_vals, con->uri.query); + + if (NULL != (get_param = (data_string *)array_get_element_klen(srv->split_vals, CONST_STR_LEN("start")))) { + if (buffer_string_is_empty(get_param->value)) return HANDLER_GO_ON; + start = strtoll(get_param->value->ptr, &err, 10); + if (*err != '\0') return HANDLER_GO_ON; + if (start < 0) return HANDLER_GO_ON; + } + + if (NULL != (get_param = (data_string *)array_get_element_klen(srv->split_vals, CONST_STR_LEN("end")))) { + off_t end; + if (buffer_string_is_empty(get_param->value)) return HANDLER_GO_ON; + end = strtoll(get_param->value->ptr, &err, 10); + if (*err != '\0') return HANDLER_GO_ON; + if (end < 0) return HANDLER_GO_ON; + len = (start < end ? end - start : start - end) + 1; + } + else if (0 == start) { + return HANDLER_GO_ON; + } + + /* let's build a flv header */ + http_chunk_append_mem(srv, con, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9")); + if (0 != http_chunk_append_file_range(srv, con, con->physical.path, start, len)) { + chunkqueue_reset(con->write_queue); + return HANDLER_GO_ON; + } + + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv")); + con->file_finished = 1; + return HANDLER_FINISHED; + } +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_flv_streaming_plugin_init(plugin *p); +int mod_flv_streaming_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("flv_streaming"); + + p->init = mod_flv_streaming_init; + p->handle_physical = mod_flv_streaming_path_handler; + p->set_defaults = mod_flv_streaming_set_defaults; + p->cleanup = mod_flv_streaming_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_geoip.c b/data/lighttpd/lighttpd-1.4.53/src/mod_geoip.c new file mode 100644 index 000000000..12c855d04 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_geoip.c @@ -0,0 +1,305 @@ +#include "first.h" + +#include <GeoIP.h> +#include <GeoIPCity.h> + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "http_header.h" +#include "plugin.h" + +#include <stdlib.h> +#include <string.h> + +/** + * + * $mod_geoip.c (v2.0) (13.09.2006 00:29:11) + * + * Name: + * mod_geoip.c + * + * Description: + * GeoIP module (plugin) for lighttpd. + * the module loads a geoip database of type "country" or "city" and + * sets new ENV vars based on ip record lookups. + * + * country db env's: + * GEOIP_COUNTRY_CODE + * GEOIP_COUNTRY_CODE3 + * GEOIP_COUNTRY_NAME + * + * city db env's: + * GEOIP_COUNTRY_CODE + * GEOIP_COUNTRY_CODE3 + * GEOIP_COUNTRY_NAME + * GEOIP_CITY_NAME + * GEOIP_CITY_POSTAL_CODE + * GEOIP_CITY_LATITUDE + * GEOIP_CITY_LONG_LATITUDE + * GEOIP_CITY_DMA_CODE + * GEOIP_CITY_AREA_CODE + * + * Usage (configuration options): + * geoip.db-filename = <path to the geoip or geocity database> + * geoip.memory-cache = <enable|disable> : default disabled + * if enabled, mod_geoip will load the database binary file to + * memory for very fast lookups. the only penalty is memory usage. + * + * Author: + * Ami E. Bizamcher (amix) + * duke.amix@gmail.com + * + * Note: + * GeoIP Library and API must be installed! + */ + + +/* plugin config for all request/connections */ + +typedef struct { + unsigned short mem_cache; + buffer *db_name; + GeoIP *gi; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_geoip_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + return p; +} + +/* destroy the plugin data */ +FREE_FUNC(mod_geoip_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (!s) continue; + + buffer_free(s->db_name); + + /* clean up */ + if (s->gi) GeoIP_delete(s->gi); + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_geoip_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "geoip.db-filename", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "geoip.memory-cache", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + int mode; + + s = calloc(1, sizeof(plugin_config)); + + s->db_name = buffer_init(); + s->mem_cache = 0; /* default: do not load db to cache */ + s->gi = NULL; + + cv[0].destination = s->db_name; + cv[1].destination = &(s->mem_cache); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + mode = GEOIP_STANDARD | GEOIP_CHECK_CACHE; + + /* country db filename is requeried! */ + if (!buffer_is_empty(s->db_name)) { + + /* let's start cooking */ + if (s->mem_cache != 0) + mode = GEOIP_MEMORY_CACHE | GEOIP_CHECK_CACHE; + + if (NULL == (s->gi = GeoIP_open(s->db_name->ptr, mode))) { + log_error_write(srv, __FILE__, __LINE__, "s", + "failed to open GeoIP database!!!"); + + return HANDLER_ERROR; + } + + /* is the db supported ? */ + if (s->gi->databaseType != GEOIP_COUNTRY_EDITION && + s->gi->databaseType != GEOIP_CITY_EDITION_REV0 && + s->gi->databaseType != GEOIP_CITY_EDITION_REV1) { + log_error_write(srv, __FILE__, __LINE__, "s", + "GeoIP database is of unsupported type!!!"); + } + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_geoip_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(db_name); + PATCH(mem_cache); + PATCH(gi); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("geoip.db-filename"))) { + PATCH(db_name); + } + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("geoip.memory-cache"))) { + PATCH(mem_cache); + } + } + } + + return 0; +} +#undef PATCH + +static handler_t mod_geoip_query (connection *con, plugin_data *p) { + GeoIPRecord *gir; + const char *remote_ip = con->dst_addr_buf->ptr; + + if (NULL != http_header_env_get(con, CONST_STR_LEN("GEOIP_COUNTRY_CODE"))) { + return HANDLER_GO_ON; + } + + if (p->conf.gi->databaseType == GEOIP_COUNTRY_EDITION) { + const char *returnedCountry; + + if (NULL != (returnedCountry = GeoIP_country_code_by_addr(p->conf.gi, remote_ip))) { + http_header_env_set(con, CONST_STR_LEN("GEOIP_COUNTRY_CODE"), returnedCountry, strlen(returnedCountry)); + } + + if (NULL != (returnedCountry = GeoIP_country_code3_by_addr(p->conf.gi, remote_ip))) { + http_header_env_set(con, CONST_STR_LEN("GEOIP_COUNTRY_CODE3"), returnedCountry, strlen(returnedCountry)); + } + + if (NULL != (returnedCountry = GeoIP_country_name_by_addr(p->conf.gi, remote_ip))) { + http_header_env_set(con, CONST_STR_LEN("GEOIP_COUNTRY_NAME"), returnedCountry, strlen(returnedCountry)); + } + + return HANDLER_GO_ON; + } + + /* if we are here, geo city is in use */ + + if (NULL != (gir = GeoIP_record_by_addr(p->conf.gi, remote_ip))) { + + http_header_env_set(con, CONST_STR_LEN("GEOIP_COUNTRY_CODE"), gir->country_code, strlen(gir->country_code)); + http_header_env_set(con, CONST_STR_LEN("GEOIP_COUNTRY_CODE3"), gir->country_code3, strlen(gir->country_code3)); + http_header_env_set(con, CONST_STR_LEN("GEOIP_COUNTRY_NAME"), gir->country_name, strlen(gir->country_name)); + http_header_env_set(con, CONST_STR_LEN("GEOIP_CITY_REGION"), gir->region, strlen(gir->region)); + http_header_env_set(con, CONST_STR_LEN("GEOIP_CITY_NAME"), gir->city, strlen(gir->city)); + http_header_env_set(con, CONST_STR_LEN("GEOIP_CITY_POSTAL_CODE"), gir->postal_code, strlen(gir->postal_code)); + + { + char latitude[32]; + snprintf(latitude, sizeof(latitude), "%f", gir->latitude); + http_header_env_set(con, CONST_STR_LEN("GEOIP_CITY_LATITUDE"), latitude, strlen(latitude)); + } + + { + char long_latitude[32]; + snprintf(long_latitude, sizeof(long_latitude), "%f", gir->longitude); + http_header_env_set(con, CONST_STR_LEN("GEOIP_CITY_LONG_LATITUDE"), long_latitude, strlen(long_latitude)); + } + + { + char dc[LI_ITOSTRING_LENGTH]; + li_utostrn(dc, sizeof(dc), gir->dma_code); + http_header_env_set(con, CONST_STR_LEN("GEOIP_CITY_DMA_CODE"), dc, strlen(dc)); + } + + { + char ac[LI_ITOSTRING_LENGTH]; + li_utostrn(ac, sizeof(ac), gir->area_code); + http_header_env_set(con, CONST_STR_LEN("GEOIP_CITY_AREA_CODE"), ac, strlen(ac)); + } + + GeoIPRecord_delete(gir); + } + + return HANDLER_GO_ON; +} + +CONNECTION_FUNC(mod_geoip_handle_request_env) { + plugin_data *p = p_d; + mod_geoip_patch_connection(srv, con, p); + if (buffer_is_empty(p->conf.db_name)) return HANDLER_GO_ON; + + return mod_geoip_query(con, p); +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_geoip_plugin_init(plugin *p); +int mod_geoip_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("geoip"); + + p->init = mod_geoip_init; + p->handle_request_env = mod_geoip_handle_request_env; + p->set_defaults = mod_geoip_set_defaults; + p->cleanup = mod_geoip_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_indexfile.c b/data/lighttpd/lighttpd-1.4.53/src/mod_indexfile.c new file mode 100644 index 000000000..a48681b17 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_indexfile.c @@ -0,0 +1,235 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "http_header.h" + +#include "plugin.h" + +#include "stat_cache.h" + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +/* plugin config for all request/connections */ + +typedef struct { + array *indexfiles; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + buffer *tmp_buf; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_indexfile_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->tmp_buf = buffer_init(); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_indexfile_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->indexfiles); + + free(s); + } + free(p->config_storage); + } + + buffer_free(p->tmp_buf); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_indexfile_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "index-file.names", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "server.indexfiles", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->indexfiles = array_init(); + + cv[0].destination = s->indexfiles; + cv[1].destination = s->indexfiles; /* old name for [0] */ + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_vlist(s->indexfiles)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for index-file.names; expected list of \"file\""); + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_indexfile_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(indexfiles); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.indexfiles"))) { + PATCH(indexfiles); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("index-file.names"))) { + PATCH(indexfiles); + } + } + } + + return 0; +} +#undef PATCH + +URIHANDLER_FUNC(mod_indexfile_subrequest) { + plugin_data *p = p_d; + size_t k; + stat_cache_entry *sce = NULL; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; + if (con->uri.path->ptr[buffer_string_length(con->uri.path) - 1] != '/') return HANDLER_GO_ON; + + mod_indexfile_patch_connection(srv, con, p); + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- handling the request as Indexfile"); + log_error_write(srv, __FILE__, __LINE__, "sb", "URI :", con->uri.path); + } + + /* indexfile */ + for (k = 0; k < p->conf.indexfiles->used; k++) { + data_string *ds = (data_string *)p->conf.indexfiles->data[k]; + + if (ds->value && ds->value->ptr[0] == '/') { + /* if the index-file starts with a prefix as use this file as + * index-generator */ + buffer_copy_buffer(p->tmp_buf, con->physical.doc_root); + } else { + buffer_copy_buffer(p->tmp_buf, con->physical.path); + } + buffer_append_string_buffer(p->tmp_buf, ds->value); + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, p->tmp_buf, &sce)) { + if (errno == EACCES) { + con->http_status = 403; + buffer_reset(con->physical.path); + + return HANDLER_FINISHED; + } + + if (errno != ENOENT && + errno != ENOTDIR) { + /* we have no idea what happend. let's tell the user so. */ + + con->http_status = 500; + + log_error_write(srv, __FILE__, __LINE__, "ssbsb", + "file not found ... or so: ", strerror(errno), + con->uri.path, + "->", con->physical.path); + + buffer_reset(con->physical.path); + + return HANDLER_FINISHED; + } + continue; + } + + if (ds->value && ds->value->ptr[0] == '/') { + /* replace uri.path */ + buffer_copy_buffer(con->uri.path, ds->value); + http_header_env_set(con, CONST_STR_LEN("PATH_TRANSLATED_DIRINDEX"), CONST_BUF_LEN(con->physical.path)); + } else { + /* append to uri.path the relative path to index file (/ -> /index.php) */ + buffer_append_string_buffer(con->uri.path, ds->value); + } + + buffer_copy_buffer(con->physical.path, p->tmp_buf); + + return HANDLER_GO_ON; + } + + /* not found */ + return HANDLER_GO_ON; +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_indexfile_plugin_init(plugin *p); +int mod_indexfile_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("indexfile"); + + p->init = mod_indexfile_init; + p->handle_subrequest_start = mod_indexfile_subrequest; + p->set_defaults = mod_indexfile_set_defaults; + p->cleanup = mod_indexfile_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_magnet.c b/data/lighttpd/lighttpd-1.4.53/src/mod_magnet.c new file mode 100644 index 000000000..dcb1fd8cb --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_magnet.c @@ -0,0 +1,1071 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "http_chunk.h" +#include "http_header.h" + +#include "plugin.h" + +#include "mod_magnet_cache.h" +#include "stat_cache.h" +#include "status_counter.h" +#include "etag.h" + +#include <stdlib.h> +#include <string.h> +#include <setjmp.h> + +#include <lua.h> +#include <lauxlib.h> + +#define LUA_RIDX_LIGHTTPD_SERVER "lighty.srv" +#define LUA_RIDX_LIGHTTPD_CONNECTION "lighty.con" + +#define MAGNET_CONFIG_RAW_URL "magnet.attract-raw-url-to" +#define MAGNET_CONFIG_PHYSICAL_PATH "magnet.attract-physical-path-to" +#define MAGNET_RESTART_REQUEST 99 + +/* plugin config for all request/connections */ + +static jmp_buf exceptionjmp; + +typedef struct { + array *url_raw; + array *physical_path; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + script_cache *cache; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_magnet_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->cache = script_cache_init(); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_magnet_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->url_raw); + array_free(s->physical_path); + + free(s); + } + free(p->config_storage); + } + + script_cache_free(p->cache); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_magnet_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { MAGNET_CONFIG_RAW_URL, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { MAGNET_CONFIG_PHYSICAL_PATH, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->url_raw = array_init(); + s->physical_path = array_init(); + + cv[0].destination = s->url_raw; + cv[1].destination = s->physical_path; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_vlist(s->url_raw)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for magnet.attract-raw-url-to; expected list of \"scriptpath\""); + return HANDLER_ERROR; + } + + if (!array_is_vlist(s->physical_path)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for magnet.attract-physical-path-to; expected list \"scriptpath\""); + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_magnet_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(url_raw); + PATCH(physical_path); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN(MAGNET_CONFIG_RAW_URL))) { + PATCH(url_raw); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(MAGNET_CONFIG_PHYSICAL_PATH))) { + PATCH(physical_path); + } + } + } + + return 0; +} +#undef PATCH + +#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 +/* lua5.1 backward compat definition */ +static void lua_pushglobaltable(lua_State *L) { /* (-0, +1, -) */ + lua_pushvalue(L, LUA_GLOBALSINDEX); +} +#endif + +static void magnet_setfenv_mainfn(lua_State *L, int funcIndex) { /* (-1, 0, -) */ +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502 + /* set "_ENV" upvalue, which should be the first upvalue of a "main" lua + * function if it uses any global names + */ + + const char* first_upvalue_name = lua_getupvalue(L, funcIndex, 1); + if (NULL == first_upvalue_name) return; /* doesn't have any upvalues */ + lua_pop(L, 1); /* only need the name of the upvalue, not the value */ + + if (0 != strcmp(first_upvalue_name, "_ENV")) return; + + if (NULL == lua_setupvalue(L, funcIndex, 1)) { + /* pop value if lua_setupvalue didn't set the (not existing) upvalue */ + lua_pop(L, 1); + } +#else + lua_setfenv(L, funcIndex); +#endif +} + +#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 +/* lua 5.2 already supports __pairs */ + +/* See http://lua-users.org/wiki/GeneralizedPairsAndIpairs for implementation details. + * Override the default pairs() function to allow us to use a __pairs metakey + */ +static int magnet_pairs(lua_State *L) { + luaL_checkany(L, 1); /* "self" */ + + if (luaL_getmetafield(L, 1, "__pairs")) { + /* call __pairs(self) */ + lua_pushvalue(L, 1); + lua_call(L, 1, 3); + } else { + /* call <original-pairs-method>(self) */ + lua_pushvalue(L, lua_upvalueindex(1)); + lua_pushvalue(L, 1); + lua_call(L, 1, 3); + } + return 3; +} +#endif + +static void magnet_push_buffer(lua_State *L, const buffer *b) { + if (!buffer_is_empty(b)) + lua_pushlstring(L, CONST_BUF_LEN(b)); + else + lua_pushnil(L); +} + +#if 0 +static int magnet_array_get_element(lua_State *L, const array *a) { + /* __index: param 1 is the (empty) table the value was not found in */ + size_t klen; + const char * const k = luaL_checklstring(L, 2, &klen); + data_string * const ds = (data_string *)array_get_element_klen(a, k, klen); + magnet_push_buffer(L, NULL != ds ? ds->value : NULL); + return 1; +} +#endif + +/* Define a function that will iterate over an array* (in upval 1) using current position (upval 2) */ +static int magnet_array_next(lua_State *L) { + data_unset *du; + data_string *ds; + data_integer *di; + + size_t pos = lua_tointeger(L, lua_upvalueindex(1)); + array *a = lua_touserdata(L, lua_upvalueindex(2)); + + lua_settop(L, 0); + + if (pos >= a->used) return 0; + if (NULL != (du = a->data[pos])) { + lua_pushlstring(L, CONST_BUF_LEN(du->key)); + switch (du->type) { + case TYPE_STRING: + ds = (data_string *)du; + magnet_push_buffer(L, ds->value); + break; + case TYPE_INTEGER: + di = (data_integer *)du; + lua_pushinteger(L, di->value); + break; + default: + lua_pushnil(L); + break; + } + + /* Update our positional upval to reflect our new current position */ + pos++; + lua_pushinteger(L, pos); + lua_replace(L, lua_upvalueindex(1)); + + /* Returning 2 items on the stack (key, value) */ + return 2; + } + return 0; +} + +/* Create the closure necessary to iterate over the array *a with the above function */ +static int magnet_array_pairs(lua_State *L, array *a) { + lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */ + lua_pushlightuserdata(L, a); /* Push our array *a into upval 2 */ + lua_pushcclosure(L, magnet_array_next, 2); /* Push our new closure with 2 upvals */ + return 1; +} + +static server* magnet_get_server(lua_State *L) { + server *srv; + + lua_getfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_SERVER); + srv = lua_touserdata(L, -1); + lua_pop(L, 1); + + return srv; +} + +static connection* magnet_get_connection(lua_State *L) { + connection *con; + + lua_getfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_CONNECTION); + con = lua_touserdata(L, -1); + lua_pop(L, 1); + + return con; +} + +typedef struct { + const char *ptr; + size_t len; +} const_buffer; + +static const_buffer magnet_checkconstbuffer(lua_State *L, int index) { + const_buffer cb; + cb.ptr = luaL_checklstring(L, index, &cb.len); + return cb; +} + +static buffer* magnet_checkbuffer(lua_State *L, int index) { + const_buffer cb = magnet_checkconstbuffer(L, index); + buffer *b = buffer_init(); + buffer_copy_string_len(b, cb.ptr, cb.len); + return b; +} + +static int magnet_print(lua_State *L) { + const_buffer cb = magnet_checkconstbuffer(L, 1); + log_error_write(magnet_get_server(L), __FILE__, __LINE__, "ss", + "(lua-print)", cb.ptr); + return 0; +} + +static int magnet_stat(lua_State *L) { + server *srv = magnet_get_server(L); + connection *con = magnet_get_connection(L); + stat_cache_entry *sce = NULL; + { + buffer *sb = magnet_checkbuffer(L, 1); + handler_t res; + + res = stat_cache_get_entry(srv, con, sb, &sce); + + if (HANDLER_GO_ON != res) { + buffer_free(sb); + lua_pushnil(L); + return 1; + } + + stat_cache_content_type_get(srv, con, sb, sce); + buffer_free(sb); + } + + lua_newtable(L); // return value + + lua_pushboolean(L, S_ISREG(sce->st.st_mode)); + lua_setfield(L, -2, "is_file"); + + lua_pushboolean(L, S_ISDIR(sce->st.st_mode)); + lua_setfield(L, -2, "is_dir"); + + lua_pushboolean(L, S_ISCHR(sce->st.st_mode)); + lua_setfield(L, -2, "is_char"); + + lua_pushboolean(L, S_ISBLK(sce->st.st_mode)); + lua_setfield(L, -2, "is_block"); + + lua_pushboolean(L, S_ISSOCK(sce->st.st_mode)); + lua_setfield(L, -2, "is_socket"); + + lua_pushboolean(L, S_ISLNK(sce->st.st_mode)); + lua_setfield(L, -2, "is_link"); + + lua_pushboolean(L, S_ISFIFO(sce->st.st_mode)); + lua_setfield(L, -2, "is_fifo"); + + lua_pushinteger(L, sce->st.st_mtime); + lua_setfield(L, -2, "st_mtime"); + + lua_pushinteger(L, sce->st.st_ctime); + lua_setfield(L, -2, "st_ctime"); + + lua_pushinteger(L, sce->st.st_atime); + lua_setfield(L, -2, "st_atime"); + + lua_pushinteger(L, sce->st.st_uid); + lua_setfield(L, -2, "st_uid"); + + lua_pushinteger(L, sce->st.st_gid); + lua_setfield(L, -2, "st_gid"); + + lua_pushinteger(L, sce->st.st_size); + lua_setfield(L, -2, "st_size"); + + lua_pushinteger(L, sce->st.st_ino); + lua_setfield(L, -2, "st_ino"); + + if (!buffer_string_is_empty(stat_cache_etag_get(sce, con->etag_flags))) { + /* we have to mutate the etag */ + etag_mutate(srv->tmp_buf, sce->etag); + lua_pushlstring(L, CONST_BUF_LEN(srv->tmp_buf)); + } else { + lua_pushnil(L); + } + lua_setfield(L, -2, "etag"); + + if (!buffer_string_is_empty(sce->content_type)) { + lua_pushlstring(L, CONST_BUF_LEN(sce->content_type)); + } else { + lua_pushnil(L); + } + lua_setfield(L, -2, "content-type"); + + return 1; +} + + +static int magnet_atpanic(lua_State *L) { + const_buffer cb = magnet_checkconstbuffer(L, 1); + log_error_write(magnet_get_server(L), __FILE__, __LINE__, "ss", + "(lua-atpanic)", cb.ptr); + longjmp(exceptionjmp, 1); +} + +static int magnet_reqhdr_get(lua_State *L) { + /* __index: param 1 is the (empty) table the value was not found in */ + connection *con = magnet_get_connection(L); + size_t klen; + const char * const k = luaL_checklstring(L, 2, &klen); + buffer * const vb = + http_header_request_get(con, HTTP_HEADER_UNSPECIFIED, k, klen); + magnet_push_buffer(L, NULL != vb ? vb : NULL); + return 1; +} + +static int magnet_reqhdr_pairs(lua_State *L) { + connection *con = magnet_get_connection(L); + return magnet_array_pairs(L, con->request.headers); +} + +static int magnet_status_get(lua_State *L) { + int *i; + server *srv = magnet_get_server(L); + + /* __index: param 1 is the (empty) table the value was not found in */ + const_buffer key = magnet_checkconstbuffer(L, 2); + i = status_counter_get_counter(srv, key.ptr, key.len); + lua_pushinteger(L, (lua_Integer)*i); + + return 1; +} + +static int magnet_status_set(lua_State *L) { + server *srv = magnet_get_server(L); + + /* __newindex: param 1 is the (empty) table the value is supposed to be set in */ + const_buffer key = magnet_checkconstbuffer(L, 2); + int counter = (int) luaL_checkinteger(L, 3); + + status_counter_set(srv, key.ptr, key.len, counter); + + return 0; +} + +static int magnet_status_pairs(lua_State *L) { + server *srv = magnet_get_server(L); + + return magnet_array_pairs(L, srv->status); +} + +typedef struct { + const char *name; + enum { + MAGNET_ENV_UNSET, + + MAGNET_ENV_PHYICAL_PATH, + MAGNET_ENV_PHYICAL_REL_PATH, + MAGNET_ENV_PHYICAL_DOC_ROOT, + MAGNET_ENV_PHYICAL_BASEDIR, + + MAGNET_ENV_URI_PATH, + MAGNET_ENV_URI_PATH_RAW, + MAGNET_ENV_URI_SCHEME, + MAGNET_ENV_URI_AUTHORITY, + MAGNET_ENV_URI_QUERY, + + MAGNET_ENV_REQUEST_METHOD, + MAGNET_ENV_REQUEST_URI, + MAGNET_ENV_REQUEST_ORIG_URI, + MAGNET_ENV_REQUEST_PATH_INFO, + MAGNET_ENV_REQUEST_REMOTE_IP, + MAGNET_ENV_REQUEST_PROTOCOL + } type; +} magnet_env_t; + +static const magnet_env_t magnet_env[] = { + { "physical.path", MAGNET_ENV_PHYICAL_PATH }, + { "physical.rel-path", MAGNET_ENV_PHYICAL_REL_PATH }, + { "physical.doc-root", MAGNET_ENV_PHYICAL_DOC_ROOT }, + { "physical.basedir", MAGNET_ENV_PHYICAL_BASEDIR }, + + { "uri.path", MAGNET_ENV_URI_PATH }, + { "uri.path-raw", MAGNET_ENV_URI_PATH_RAW }, + { "uri.scheme", MAGNET_ENV_URI_SCHEME }, + { "uri.authority", MAGNET_ENV_URI_AUTHORITY }, + { "uri.query", MAGNET_ENV_URI_QUERY }, + + { "request.method", MAGNET_ENV_REQUEST_METHOD }, + { "request.uri", MAGNET_ENV_REQUEST_URI }, + { "request.orig-uri", MAGNET_ENV_REQUEST_ORIG_URI }, + { "request.path-info", MAGNET_ENV_REQUEST_PATH_INFO }, + { "request.remote-ip", MAGNET_ENV_REQUEST_REMOTE_IP }, + { "request.protocol", MAGNET_ENV_REQUEST_PROTOCOL }, + + { NULL, MAGNET_ENV_UNSET } +}; + +static buffer *magnet_env_get_buffer_by_id(server *srv, connection *con, int id) { + buffer *dest = NULL; + + UNUSED(srv); + + /** + * map all internal variables to lua + * + */ + + switch (id) { + case MAGNET_ENV_PHYICAL_PATH: dest = con->physical.path; break; + case MAGNET_ENV_PHYICAL_REL_PATH: dest = con->physical.rel_path; break; + case MAGNET_ENV_PHYICAL_DOC_ROOT: dest = con->physical.doc_root; break; + case MAGNET_ENV_PHYICAL_BASEDIR: dest = con->physical.basedir; break; + + case MAGNET_ENV_URI_PATH: dest = con->uri.path; break; + case MAGNET_ENV_URI_PATH_RAW: dest = con->uri.path_raw; break; + case MAGNET_ENV_URI_SCHEME: dest = con->uri.scheme; break; + case MAGNET_ENV_URI_AUTHORITY: dest = con->uri.authority; break; + case MAGNET_ENV_URI_QUERY: dest = con->uri.query; break; + + case MAGNET_ENV_REQUEST_METHOD: + buffer_clear(srv->tmp_buf); + http_method_append(srv->tmp_buf, con->request.http_method); + dest = srv->tmp_buf; + break; + case MAGNET_ENV_REQUEST_URI: dest = con->request.uri; break; + case MAGNET_ENV_REQUEST_ORIG_URI: dest = con->request.orig_uri; break; + case MAGNET_ENV_REQUEST_PATH_INFO: dest = con->request.pathinfo; break; + case MAGNET_ENV_REQUEST_REMOTE_IP: dest = con->dst_addr_buf; break; + case MAGNET_ENV_REQUEST_PROTOCOL: + buffer_copy_string(srv->tmp_buf, get_http_version_name(con->request.http_version)); + dest = srv->tmp_buf; + break; + + case MAGNET_ENV_UNSET: break; + } + + return dest; +} + +static buffer *magnet_env_get_buffer(server *srv, connection *con, const char *key) { + size_t i; + + for (i = 0; magnet_env[i].name; i++) { + if (0 == strcmp(key, magnet_env[i].name)) break; + } + + return magnet_env_get_buffer_by_id(srv, con, magnet_env[i].type); +} + +static int magnet_env_get(lua_State *L) { + server *srv = magnet_get_server(L); + connection *con = magnet_get_connection(L); + + /* __index: param 1 is the (empty) table the value was not found in */ + const char *key = luaL_checkstring(L, 2); + magnet_push_buffer(L, magnet_env_get_buffer(srv, con, key)); + return 1; +} + +static int magnet_env_set(lua_State *L) { + server *srv = magnet_get_server(L); + connection *con = magnet_get_connection(L); + + /* __newindex: param 1 is the (empty) table the value is supposed to be set in */ + const char *key = luaL_checkstring(L, 2); + buffer *dest = NULL; + + luaL_checkany(L, 3); /* nil or a string */ + + if (NULL != (dest = magnet_env_get_buffer(srv, con, key))) { + if (lua_isnil(L, 3)) { + buffer_reset(dest); + } else { + const_buffer val = magnet_checkconstbuffer(L, 3); + buffer_copy_string_len(dest, val.ptr, val.len); + } + } else { + /* couldn't save */ + + return luaL_error(L, "couldn't store '%s' in lighty.env[]", key); + } + + return 0; +} + +static int magnet_env_next(lua_State *L) { + server *srv = magnet_get_server(L); + connection *con = magnet_get_connection(L); + const int pos = lua_tointeger(L, lua_upvalueindex(1)); + + /* ignore previous key: use upvalue for current pos */ + lua_settop(L, 0); + + if (NULL == magnet_env[pos].name) return 0; /* end of list */ + /* Update our positional upval to reflect our new current position */ + lua_pushinteger(L, pos + 1); + lua_replace(L, lua_upvalueindex(1)); + + /* key to return */ + lua_pushstring(L, magnet_env[pos].name); + + /* get value */ + magnet_push_buffer(L, magnet_env_get_buffer_by_id(srv, con, magnet_env[pos].type)); + + /* return 2 items on the stack (key, value) */ + return 2; +} + +static int magnet_env_pairs(lua_State *L) { + lua_pushinteger(L, 0); /* Push our current pos (the start) into upval 1 */ + lua_pushcclosure(L, magnet_env_next, 1); /* Push our new closure with 1 upvals */ + return 1; +} + +static int magnet_cgi_get(lua_State *L) { + /* __index: param 1 is the (empty) table the value was not found in */ + connection *con = magnet_get_connection(L); + size_t klen; + const char * const k = luaL_checklstring(L, 2, &klen); + buffer * const vb = http_header_env_get(con, k, klen); + magnet_push_buffer(L, NULL != vb ? vb : NULL); + return 1; +} + +static int magnet_cgi_set(lua_State *L) { + /* __newindex: param 1 is the (empty) table the value is supposed to be set in */ + connection *con = magnet_get_connection(L); + const_buffer key = magnet_checkconstbuffer(L, 2); + const_buffer val = magnet_checkconstbuffer(L, 3); + http_header_env_set(con, key.ptr, key.len, val.ptr, val.len); + return 0; +} + +static int magnet_cgi_pairs(lua_State *L) { + connection *con = magnet_get_connection(L); + + return magnet_array_pairs(L, con->environment); +} + + +static int magnet_copy_response_header(connection *con, lua_State *L, int lighty_table_ndx) { + force_assert(lua_istable(L, lighty_table_ndx)); + + lua_getfield(L, lighty_table_ndx, "header"); /* lighty.header */ + if (lua_istable(L, -1)) { + /* header is found, and is a table */ + + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_isstring(L, -1) && lua_isstring(L, -2)) { + const_buffer key = magnet_checkconstbuffer(L, -2); + const_buffer val = magnet_checkconstbuffer(L, -1); + enum http_header_e id = http_header_hkey_get(key.ptr, key.len); + + val.len + ? http_header_response_set(con, id, key.ptr, key.len, val.ptr, val.len) + : http_header_response_unset(con, id, key.ptr, key.len); + } + + lua_pop(L, 1); + } + } + lua_pop(L, 1); /* pop lighty.header */ + + return 0; +} + +/** + * walk through the content array + * + * content = { "<pre>", { file = "/content" } , "</pre>" } + * + * header["Content-Type"] = "text/html" + * + * return 200 + */ +static int magnet_attach_content(server *srv, connection *con, lua_State *L, int lighty_table_ndx) { + force_assert(lua_istable(L, lighty_table_ndx)); + + lua_getfield(L, lighty_table_ndx, "content"); /* lighty.content */ + if (lua_istable(L, -1)) { + int i; + /* content is found, and is a table */ + + for (i = 1; ; i++) { + lua_rawgeti(L, -1, i); + + /* -1 is the value and should be the value ... aka a table */ + if (lua_isstring(L, -1)) { + const_buffer data = magnet_checkconstbuffer(L, -1); + + chunkqueue_append_mem(con->write_queue, data.ptr, data.len); + } else if (lua_istable(L, -1)) { + lua_getfield(L, -1, "filename"); + lua_getfield(L, -2, "length"); /* (0-based) end of range (not actually "length") */ + lua_getfield(L, -3, "offset"); /* (0-based) start of range */ + + if (lua_isstring(L, -3)) { /* filename has to be a string */ + off_t off = (off_t) luaL_optinteger(L, -1, 0); + off_t len = (off_t) luaL_optinteger(L, -2, -1); /*(-1 to http_chunk_append_file_range() uses file size minus offset)*/ + if (off < 0) { + return luaL_error(L, "offset for '%s' is negative", lua_tostring(L, -3)); + } + + if (len >= off) { + len -= off; + } else if (-1 != len) { + return luaL_error(L, "offset > length for '%s'", lua_tostring(L, -3)); + } + + if (0 != len) { + buffer *fn = magnet_checkbuffer(L, -3); + int rc = http_chunk_append_file_range(srv, con, fn, off, len); + buffer_free(fn); + if (0 != rc) { + return luaL_error(L, "error opening file content '%s' at offset %lld", lua_tostring(L, -3), (long long)off); + } + } + } else { + return luaL_error(L, "content[%d] is a table and requires the field \"filename\"", i); + } + + lua_pop(L, 3); + } else if (lua_isnil(L, -1)) { + /* end of list */ + + lua_pop(L, 1); + + break; + } else { + return luaL_error(L, "content[%d] is neither a string nor a table: ", i); + } + + lua_pop(L, 1); /* pop the content[...] entry value */ + } + } else { + return luaL_error(L, "lighty.content has to be a table"); + } + lua_pop(L, 1); /* pop lighty.content */ + + return 0; +} + +static int traceback(lua_State *L) { + if (!lua_isstring(L, 1)) /* 'message' not a string? */ + return 1; /* keep it intact */ + lua_getglobal(L, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ + return 1; +} + +/* push traceback function before calling lua_pcall after narg arguments + * have been pushed (inserts it before the arguments). returns index for + * traceback function ("msgh" in lua_pcall) + */ +static int push_traceback(lua_State *L, int narg) { + int base = lua_gettop(L) - narg; /* function index */ + lua_pushcfunction(L, traceback); + lua_insert(L, base); + return base; +} + +static handler_t magnet_attract(server *srv, connection *con, plugin_data *p, buffer *name) { + lua_State *L; + int lua_return_value; + const int func_ndx = 1; + const int lighty_table_ndx = 2; + + /* get the script-context */ + L = script_cache_get_script(srv, con, p->cache, name); + + if (lua_isstring(L, -1)) { + log_error_write(srv, __FILE__, __LINE__, + "sbss", + "loading script", + name, + "failed:", + lua_tostring(L, -1)); + + lua_pop(L, 1); + + force_assert(lua_gettop(L) == 0); /* only the error should have been on the stack */ + + con->http_status = 500; + con->mode = DIRECT; + + return HANDLER_FINISHED; + } + + force_assert(lua_gettop(L) == 1); + force_assert(lua_isfunction(L, func_ndx)); + + lua_pushlightuserdata(L, srv); + lua_setfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_SERVER); + + lua_pushlightuserdata(L, con); + lua_setfield(L, LUA_REGISTRYINDEX, LUA_RIDX_LIGHTTPD_CONNECTION); + + lua_atpanic(L, magnet_atpanic); + + /** + * we want to create empty environment for our script + * + * setmetatable({}, {__index = _G}) + * + * if a function symbol is not defined in our env, __index will lookup + * in the global env. + * + * all variables created in the script-env will be thrown + * away at the end of the script run. + */ + lua_newtable(L); /* my empty environment aka {} (sp += 1) */ + + /* we have to overwrite the print function */ + lua_pushcfunction(L, magnet_print); /* (sp += 1) */ + lua_setfield(L, -2, "print"); /* -1 is the env we want to set(sp -= 1) */ + + /** + * lighty.request[] (ro) has the HTTP-request headers + * lighty.env[] (rw) has various url/physical file paths and + * request meta data; might contain nil values + * lighty.req_env[] (ro) has the cgi environment + * lighty.status[] (ro) has the status counters + * lighty.content[] (rw) is a table of string/file + * lighty.header[] (rw) is a array to set response headers + */ + + lua_newtable(L); /* lighty.* (sp += 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_newtable(L); /* the meta-table for the request-table (sp += 1) */ + lua_pushcfunction(L, magnet_reqhdr_get); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_reqhdr_pairs); /* (sp += 1) */ + lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ + lua_setmetatable(L, -2); /* tie the metatable to request (sp -= 1) */ + lua_setfield(L, -2, "request"); /* content = {} (sp -= 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_newtable(L); /* the meta-table for the env-table (sp += 1) */ + lua_pushcfunction(L, magnet_env_get); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_env_set); /* (sp += 1) */ + lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_env_pairs); /* (sp += 1) */ + lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ + lua_setmetatable(L, -2); /* tie the metatable to env (sp -= 1) */ + lua_setfield(L, -2, "env"); /* content = {} (sp -= 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_newtable(L); /* the meta-table for the req_env-table (sp += 1) */ + lua_pushcfunction(L, magnet_cgi_get); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_cgi_set); /* (sp += 1) */ + lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_cgi_pairs); /* (sp += 1) */ + lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ + lua_setmetatable(L, -2); /* tie the metatable to req_env (sp -= 1) */ + lua_setfield(L, -2, "req_env"); /* content = {} (sp -= 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_newtable(L); /* the meta-table for the status-table (sp += 1) */ + lua_pushcfunction(L, magnet_status_get); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_status_set); /* (sp += 1) */ + lua_setfield(L, -2, "__newindex"); /* (sp -= 1) */ + lua_pushcfunction(L, magnet_status_pairs); /* (sp += 1) */ + lua_setfield(L, -2, "__pairs"); /* (sp -= 1) */ + lua_setmetatable(L, -2); /* tie the metatable to statzs (sp -= 1) */ + lua_setfield(L, -2, "status"); /* content = {} (sp -= 1) */ + + /* add empty 'content' and 'header' tables */ + lua_newtable(L); /* {} (sp += 1) */ + lua_setfield(L, -2, "content"); /* content = {} (sp -= 1) */ + + lua_newtable(L); /* {} (sp += 1) */ + lua_setfield(L, -2, "header"); /* header = {} (sp -= 1) */ + + lua_pushinteger(L, MAGNET_RESTART_REQUEST); + lua_setfield(L, -2, "RESTART_REQUEST"); + + lua_pushcfunction(L, magnet_stat); /* (sp += 1) */ + lua_setfield(L, -2, "stat"); /* -1 is the env we want to set (sp -= 1) */ + + /* insert lighty table at index 2 */ + lua_pushvalue(L, -1); + lua_insert(L, lighty_table_ndx); + + lua_setfield(L, -2, "lighty"); /* lighty.* (sp -= 1) */ + +#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 + /* override the default pairs() function to our __pairs capable version; + * not needed for lua 5.2+ + */ + lua_getglobal(L, "pairs"); /* push original pairs() (sp += 1) */ + lua_pushcclosure(L, magnet_pairs, 1); + lua_setfield(L, -2, "pairs"); /* (sp -= 1) */ +#endif + + lua_newtable(L); /* the meta-table for the new env (sp += 1) */ + lua_pushglobaltable(L); /* (sp += 1) */ + lua_setfield(L, -2, "__index"); /* { __index = _G } (sp -= 1) */ + lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) (sp -= 1) */ + + magnet_setfenv_mainfn(L, 1); /* (sp -= 1) */ + + /* pcall will destroy the func value, duplicate it */ /* (sp += 1) */ + lua_pushvalue(L, func_ndx); + { + int errfunc = push_traceback(L, 0); + int ret = lua_pcall(L, 0, 1, errfunc); + lua_remove(L, errfunc); + + /* reset environment */ + lua_pushglobaltable(L); /* (sp += 1) */ + magnet_setfenv_mainfn(L, 1); /* (sp -= 1) */ + + if (0 != ret) { + log_error_write(srv, __FILE__, __LINE__, + "ss", + "lua_pcall():", + lua_tostring(L, -1)); + lua_pop(L, 2); /* remove the error-msg and the lighty table at index 2 */ + + force_assert(lua_gettop(L) == 1); /* only the function should be on the stack */ + + con->http_status = 500; + con->mode = DIRECT; + + return HANDLER_FINISHED; + } + } + + /* we should have the function, the lighty table and the return value on the stack */ + force_assert(lua_gettop(L) == 3); + + lua_return_value = (int) luaL_optinteger(L, -1, -1); + lua_pop(L, 1); /* pop return value */ + + magnet_copy_response_header(con, L, lighty_table_ndx); + + { + handler_t result = HANDLER_GO_ON; + + if (lua_return_value > 99) { + con->http_status = lua_return_value; + con->file_finished = 1; + + /* try { ...*/ + if (0 == setjmp(exceptionjmp)) { + magnet_attach_content(srv, con, L, lighty_table_ndx); + if (!chunkqueue_is_empty(con->write_queue)) { + con->mode = p->id; + } + } else { + lua_settop(L, 2); /* remove all but function and lighty table */ + /* } catch () { */ + con->http_status = 500; + con->mode = DIRECT; + } + + result = HANDLER_FINISHED; + } else if (MAGNET_RESTART_REQUEST == lua_return_value) { + result = HANDLER_COMEBACK; + } + + lua_pop(L, 1); /* pop the lighty table */ + force_assert(lua_gettop(L) == 1); /* only the function should remain on the stack */ + + return result; + } +} + +static handler_t magnet_attract_array(server *srv, connection *con, plugin_data *p, array *files) { + size_t i; + handler_t ret = HANDLER_GO_ON; + + /* no filename set */ + if (files->used == 0) return HANDLER_GO_ON; + + srv->request_env(srv, con); + + /** + * execute all files and jump out on the first !HANDLER_GO_ON + */ + for (i = 0; i < files->used && ret == HANDLER_GO_ON; i++) { + data_string *ds = (data_string *)files->data[i]; + + if (buffer_string_is_empty(ds->value)) continue; + + ret = magnet_attract(srv, con, p, ds->value); + } + + if (con->error_handler_saved_status) { + /* retrieve (possibly modified) REDIRECT_STATUS and store as number */ + unsigned long x; + buffer * const vb = http_header_env_get(con, CONST_STR_LEN("REDIRECT_STATUS")); + if (vb && (x = strtoul(vb->ptr, NULL, 10)) < 1000) + /*(simplified validity check x < 1000)*/ + con->error_handler_saved_status = + con->error_handler_saved_status > 0 ? (int)x : -(int)x; + } + + return ret; +} + +URIHANDLER_FUNC(mod_magnet_uri_handler) { + plugin_data *p = p_d; + + mod_magnet_patch_connection(srv, con, p); + + return magnet_attract_array(srv, con, p, p->conf.url_raw); +} + +URIHANDLER_FUNC(mod_magnet_physical) { + plugin_data *p = p_d; + + mod_magnet_patch_connection(srv, con, p); + + return magnet_attract_array(srv, con, p, p->conf.physical_path); +} + + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_magnet_plugin_init(plugin *p); +int mod_magnet_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("magnet"); + + p->init = mod_magnet_init; + p->handle_uri_clean = mod_magnet_uri_handler; + p->handle_physical = mod_magnet_physical; + p->set_defaults = mod_magnet_set_defaults; + p->cleanup = mod_magnet_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_magnet_cache.c b/data/lighttpd/lighttpd-1.4.53/src/mod_magnet_cache.c new file mode 100644 index 000000000..204bb8e16 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_magnet_cache.c @@ -0,0 +1,129 @@ +#include "first.h" + +#include "mod_magnet_cache.h" +#include "base.h" +#include "stat_cache.h" + +#include <stdlib.h> +#include <time.h> + +#include <lualib.h> +#include <lauxlib.h> + +static script *script_init() { + script *sc; + + sc = calloc(1, sizeof(*sc)); + sc->name = buffer_init(); + sc->etag = buffer_init(); + + return sc; +} + +static void script_free(script *sc) { + if (!sc) return; + + lua_pop(sc->L, 1); /* the function copy */ + + buffer_free(sc->name); + buffer_free(sc->etag); + + lua_close(sc->L); + + free(sc); +} + +script_cache *script_cache_init() { + script_cache *p; + + p = calloc(1, sizeof(*p)); + + return p; +} + +void script_cache_free(script_cache *p) { + size_t i; + + if (!p) return; + + for (i = 0; i < p->used; i++) { + script_free(p->ptr[i]); + } + + free(p->ptr); + + free(p); +} + +lua_State *script_cache_get_script(server *srv, connection *con, script_cache *cache, buffer *name) { + size_t i; + script *sc = NULL; + stat_cache_entry *sce; + + for (i = 0; i < cache->used; i++) { + sc = cache->ptr[i]; + + if (buffer_is_equal(name, sc->name)) { + sc->last_used = time(NULL); + + /* oops, the script failed last time */ + + if (lua_gettop(sc->L) == 0) break; + force_assert(lua_gettop(sc->L) == 1); + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, sc->name, &sce)) { + lua_pop(sc->L, 1); /* pop the old function */ + break; + } + + stat_cache_etag_get(sce, con->etag_flags); + if (!buffer_is_equal(sce->etag, sc->etag)) { + /* the etag is outdated, reload the function */ + lua_pop(sc->L, 1); + break; + } + + force_assert(lua_isfunction(sc->L, -1)); + + return sc->L; + } + + sc = NULL; + } + + /* if the script was script already loaded but either got changed or + * failed to load last time */ + if (sc == NULL) { + sc = script_init(); + + if (cache->size == 0) { + cache->size = 16; + cache->ptr = malloc(cache->size * sizeof(*(cache->ptr))); + } else if (cache->used == cache->size) { + cache->size += 16; + cache->ptr = realloc(cache->ptr, cache->size * sizeof(*(cache->ptr))); + } + + cache->ptr[cache->used++] = sc; + + buffer_copy_buffer(sc->name, name); + + sc->L = luaL_newstate(); + luaL_openlibs(sc->L); + } + + sc->last_used = time(NULL); + + if (0 != luaL_loadfile(sc->L, name->ptr)) { + /* oops, an error, return it */ + return sc->L; + } + + if (HANDLER_GO_ON == stat_cache_get_entry(srv, con, sc->name, &sce)) { + buffer_copy_buffer(sc->etag, stat_cache_etag_get(sce, con->etag_flags)); + } + + force_assert(lua_isfunction(sc->L, -1)); + + return sc->L; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_magnet_cache.h b/data/lighttpd/lighttpd-1.4.53/src/mod_magnet_cache.h new file mode 100644 index 000000000..04eb1ab18 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_magnet_cache.h @@ -0,0 +1,32 @@ +#ifndef _MOD_MAGNET_CACHE_H_ +#define _MOD_MAGNET_CACHE_H_ +#include "first.h" + +#include "base_decls.h" +#include "buffer.h" + +#include <lua.h> + +typedef struct { + buffer *name; + buffer *etag; + + lua_State *L; + + time_t last_used; /* LRU */ +} script; + +typedef struct { + script **ptr; + + size_t used; + size_t size; +} script_cache; + +script_cache *script_cache_init(void); +void script_cache_free(script_cache *cache); + +lua_State *script_cache_get_script(server *srv, connection *con, + script_cache *cache, buffer *name); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_mysql_vhost.c b/data/lighttpd/lighttpd-1.4.53/src/mod_mysql_vhost.c new file mode 100644 index 000000000..2af5cc2ba --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_mysql_vhost.c @@ -0,0 +1,380 @@ +#include "first.h" + +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <mysql.h> + +#include "base.h" +#include "plugin.h" +#include "fdevent.h" +#include "log.h" + +#include "stat_cache.h" + +/* + * Plugin for lighttpd to use MySQL + * for domain to directory lookups, + * i.e virtual hosts (vhosts). + * + * /ada@riksnet.se 2004-12-06 + */ + +typedef struct { + MYSQL *mysql; + buffer *mysql_query; + + buffer *mydb; + buffer *myuser; + buffer *mypass; + buffer *mysock; + + buffer *hostname; + unsigned short port; +} plugin_config; + +/* global plugin data */ +typedef struct { + PLUGIN_DATA; + + buffer *tmp_buf; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* per connection plugin data */ +typedef struct { + buffer *server_name; + buffer *document_root; +} plugin_connection_data; + +/* init the plugin data */ +INIT_FUNC(mod_mysql_vhost_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->tmp_buf = buffer_init(); + + return p; +} + +/* cleanup the plugin data */ +SERVER_FUNC(mod_mysql_vhost_cleanup) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (!s) continue; + + mysql_close(s->mysql); + + buffer_free(s->mysql_query); + buffer_free(s->mydb); + buffer_free(s->myuser); + buffer_free(s->mypass); + buffer_free(s->mysock); + buffer_free(s->hostname); + + free(s); + } + free(p->config_storage); + } + buffer_free(p->tmp_buf); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle the plugin per connection data */ +static void* mod_mysql_vhost_connection_data(server *srv, connection *con, void *p_d) +{ + plugin_data *p = p_d; + plugin_connection_data *c = con->plugin_ctx[p->id]; + + UNUSED(srv); + + if (c) return c; + c = calloc(1, sizeof(*c)); + + c->server_name = buffer_init(); + c->document_root = buffer_init(); + + return con->plugin_ctx[p->id] = c; +} + +/* destroy the plugin per connection data */ +CONNECTION_FUNC(mod_mysql_vhost_handle_connection_reset) { + plugin_data *p = p_d; + plugin_connection_data *c = con->plugin_ctx[p->id]; + + UNUSED(srv); + + if (!c) return HANDLER_GO_ON; + + buffer_free(c->server_name); + buffer_free(c->document_root); + + free(c); + + con->plugin_ctx[p->id] = NULL; + return HANDLER_GO_ON; +} + +/* set configuration values */ +SERVER_FUNC(mod_mysql_vhost_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "mysql-vhost.db", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "mysql-vhost.user", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "mysql-vhost.pass", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "mysql-vhost.sock", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "mysql-vhost.sql", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "mysql-vhost.hostname", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { "mysql-vhost.port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->mysql_query = buffer_init(); + s->mydb = buffer_init(); + s->myuser = buffer_init(); + s->mypass = buffer_init(); + s->mysock = buffer_init(); + s->hostname = buffer_init(); + s->port = 0; /* default port for mysql */ + s->mysql = NULL; + + cv[0].destination = s->mydb; + cv[1].destination = s->myuser; + cv[2].destination = s->mypass; + cv[3].destination = s->mysock; + cv[4].destination = s->mysql_query; + cv[5].destination = s->hostname; + cv[6].destination = &(s->port); + + p->config_storage[i] = s; + + if (config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + /* required: + * - username + * - database + * + * optional: + * - password, default: empty + * - socket, default: mysql default + * - hostname, if set overrides socket + * - port, default: 3306 + */ + + /* all have to be set */ + if (!(buffer_string_is_empty(s->myuser) || + buffer_string_is_empty(s->mydb))) { + + if (NULL == (s->mysql = mysql_init(NULL))) { + log_error_write(srv, __FILE__, __LINE__, "s", "mysql_init() failed, exiting..."); + return HANDLER_ERROR; + } + +#if MYSQL_VERSION_ID >= 50013 + /* in mysql versions above 5.0.3 the reconnect flag is off by default */ + { + char reconnect = 1; + mysql_options(s->mysql, MYSQL_OPT_RECONNECT, &reconnect); + } +#endif + +#define FOO(x) (buffer_string_is_empty(s->x) ? NULL : s->x->ptr) + +#if MYSQL_VERSION_ID >= 40100 + /* CLIENT_MULTI_STATEMENTS first appeared in 4.1 */ + if (!mysql_real_connect(s->mysql, FOO(hostname), FOO(myuser), FOO(mypass), + FOO(mydb), s->port, FOO(mysock), CLIENT_MULTI_STATEMENTS)) { +#else + if (!mysql_real_connect(s->mysql, FOO(hostname), FOO(myuser), FOO(mypass), + FOO(mydb), s->port, FOO(mysock), 0)) { +#endif + log_error_write(srv, __FILE__, __LINE__, "s", mysql_error(s->mysql)); + return HANDLER_ERROR; + } +#undef FOO + + fdevent_setfd_cloexec(s->mysql->net.fd); + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_mysql_vhost_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(mysql_query); + PATCH(mysql); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("mysql-vhost.sql"))) { + PATCH(mysql_query); + } + } + + if (s->mysql) { + PATCH(mysql); + } + } + + return 0; +} +#undef PATCH + + +/* handle document root request */ +CONNECTION_FUNC(mod_mysql_vhost_handle_docroot) { + plugin_data *p = p_d; + plugin_connection_data *c; + stat_cache_entry *sce; + + unsigned cols; + MYSQL_ROW row; + MYSQL_RES *result = NULL; + + /* no host specified? */ + if (buffer_string_is_empty(con->uri.authority)) return HANDLER_GO_ON; + + mod_mysql_vhost_patch_connection(srv, con, p); + + if (!p->conf.mysql) return HANDLER_GO_ON; + if (buffer_string_is_empty(p->conf.mysql_query)) return HANDLER_GO_ON; + + /* sets up connection data if not done yet */ + c = mod_mysql_vhost_connection_data(srv, con, p_d); + + /* check if cached this connection */ + if (buffer_is_equal(c->server_name, con->uri.authority)) goto GO_ON; + + /* build and run SQL query */ + buffer_clear(p->tmp_buf); + for (char *b = p->conf.mysql_query->ptr, *d; *b; b = d+1) { + if (NULL != (d = strchr(b, '?'))) { + /* escape the uri.authority */ + unsigned long to_len; + buffer_append_string_len(p->tmp_buf, b, (size_t)(d - b)); + buffer_string_prepare_append(p->tmp_buf, buffer_string_length(con->uri.authority) * 2); + to_len = mysql_real_escape_string(p->conf.mysql, + p->tmp_buf->ptr + buffer_string_length(p->tmp_buf), + CONST_BUF_LEN(con->uri.authority)); + if ((unsigned long)~0 == to_len) goto ERR500; + buffer_commit(p->tmp_buf, to_len); + } else { + d = p->conf.mysql_query->ptr + buffer_string_length(p->conf.mysql_query); + buffer_append_string_len(p->tmp_buf, b, (size_t)(d - b)); + break; + } + } + if (mysql_real_query(p->conf.mysql, CONST_BUF_LEN(p->tmp_buf))) { + log_error_write(srv, __FILE__, __LINE__, "s", mysql_error(p->conf.mysql)); + goto ERR500; + } + result = mysql_store_result(p->conf.mysql); + cols = mysql_num_fields(result); + row = mysql_fetch_row(result); + if (!row || cols < 1) { + /* no such virtual host */ + mysql_free_result(result); +#if MYSQL_VERSION_ID >= 40100 + while (mysql_next_result(p->conf.mysql) == 0); +#endif + return HANDLER_GO_ON; + } + + /* sanity check that really is a directory */ + buffer_copy_string(p->tmp_buf, row[0]); + buffer_append_slash(p->tmp_buf); + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, p->tmp_buf, &sce)) { + log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), p->tmp_buf); + goto ERR500; + } + if (!S_ISDIR(sce->st.st_mode)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "Not a directory", p->tmp_buf); + goto ERR500; + } + + /* cache the data */ + buffer_copy_buffer(c->server_name, con->uri.authority); + buffer_copy_buffer(c->document_root, p->tmp_buf); + + mysql_free_result(result); +#if MYSQL_VERSION_ID >= 40100 + while (mysql_next_result(p->conf.mysql) == 0); +#endif + + /* fix virtual server and docroot */ +GO_ON: + buffer_copy_buffer(con->server_name, c->server_name); + buffer_copy_buffer(con->physical.doc_root, c->document_root); + + return HANDLER_GO_ON; + +ERR500: + if (result) mysql_free_result(result); +#if MYSQL_VERSION_ID >= 40100 + while (mysql_next_result(p->conf.mysql) == 0); +#endif + con->http_status = 500; /* Internal Error */ + con->mode = DIRECT; + return HANDLER_FINISHED; +} + +/* this function is called at dlopen() time and inits the callbacks */ +int mod_mysql_vhost_plugin_init(plugin *p); +int mod_mysql_vhost_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("mysql_vhost"); + + p->init = mod_mysql_vhost_init; + p->cleanup = mod_mysql_vhost_cleanup; + p->connection_reset = mod_mysql_vhost_handle_connection_reset; + + p->set_defaults = mod_mysql_vhost_set_defaults; + p->handle_docroot = mod_mysql_vhost_handle_docroot; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_openssl.c b/data/lighttpd/lighttpd-1.4.53/src/mod_openssl.c new file mode 100644 index 000000000..2da3de238 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_openssl.c @@ -0,0 +1,2139 @@ +#include "first.h" + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#ifndef USE_OPENSSL_KERBEROS +#ifndef OPENSSL_NO_KRB5 +#define OPENSSL_NO_KRB5 +#endif +#endif + +#include "sys-crypto.h" + +#include <openssl/ssl.h> +#include <openssl/bio.h> +#include <openssl/bn.h> +#include <openssl/err.h> +#include <openssl/objects.h> +#include <openssl/pem.h> +#include <openssl/rand.h> +#ifndef OPENSSL_NO_DH +#include <openssl/dh.h> +#endif + +#if ! defined OPENSSL_NO_TLSEXT && ! defined SSL_CTRL_SET_TLSEXT_HOSTNAME +#define OPENSSL_NO_TLSEXT +#endif + +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL +#ifndef OPENSSL_NO_ECDH +#include <openssl/ecdh.h> +#endif +#endif + +#include "base.h" +#include "http_header.h" +#include "log.h" +#include "plugin.h" + +typedef struct { + SSL_CTX *ssl_ctx; /* not patched */ + /* SNI per host: with COMP_SERVER_SOCKET, COMP_HTTP_SCHEME, COMP_HTTP_HOST */ + EVP_PKEY *ssl_pemfile_pkey; + X509 *ssl_pemfile_x509; + STACK_OF(X509_NAME) *ssl_ca_file_cert_names; + + unsigned short ssl_verifyclient; + unsigned short ssl_verifyclient_enforce; + unsigned short ssl_verifyclient_depth; + unsigned short ssl_verifyclient_export_cert; + buffer *ssl_verifyclient_username; + + unsigned short ssl_disable_client_renegotiation; + unsigned short ssl_read_ahead; + unsigned short ssl_log_noise; + + /*(used only during startup; not patched)*/ + unsigned short ssl_enabled; /* only interesting for setting up listening sockets. don't use at runtime */ + unsigned short ssl_honor_cipher_order; /* determine SSL cipher in server-preferred order, not client-order */ + unsigned short ssl_empty_fragments; /* whether to not set SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ + unsigned short ssl_use_sslv2; + unsigned short ssl_use_sslv3; + buffer *ssl_pemfile; + buffer *ssl_privkey; + buffer *ssl_ca_file; + buffer *ssl_ca_crl_file; + buffer *ssl_ca_dn_file; + buffer *ssl_cipher_list; + buffer *ssl_dh_file; + buffer *ssl_ec_curve; + array *ssl_conf_cmd; + buffer *ssl_acme_tls_1; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; +} plugin_data; + +static int ssl_is_init; +/* need assigned p->id for deep access of module handler_ctx for connection + * i.e. handler_ctx *hctx = con->plugin_ctx[plugin_data_singleton->id]; */ +static plugin_data *plugin_data_singleton; +#define LOCAL_SEND_BUFSIZE (64 * 1024) +static char *local_send_buffer; + +typedef struct { + SSL *ssl; + connection *con; + int renegotiations; /* count of SSL_CB_HANDSHAKE_START */ + unsigned short request_env_patched; + unsigned short alpn; + plugin_config conf; + server *srv; +} handler_ctx; + + +static handler_ctx * +handler_ctx_init (void) +{ + handler_ctx *hctx = calloc(1, sizeof(*hctx)); + force_assert(hctx); + return hctx; +} + + +static void +handler_ctx_free (handler_ctx *hctx) +{ + if (hctx->ssl) SSL_free(hctx->ssl); + free(hctx); +} + + +INIT_FUNC(mod_openssl_init) +{ + plugin_data_singleton = (plugin_data *)calloc(1, sizeof(plugin_data)); + #ifdef DEBUG_WOLFSSL + wolfSSL_Debugging_ON(); + #endif + return plugin_data_singleton; +} + + +FREE_FUNC(mod_openssl_free) +{ + plugin_data *p = p_d; + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + for (size_t i = 0; i < srv->config_context->used; ++i) { + plugin_config *s = p->config_storage[i]; + int copy; + if (NULL == s) continue; + copy = s->ssl_enabled && buffer_string_is_empty(s->ssl_pemfile); + buffer_free(s->ssl_pemfile); + buffer_free(s->ssl_privkey); + buffer_free(s->ssl_ca_file); + buffer_free(s->ssl_ca_crl_file); + buffer_free(s->ssl_ca_dn_file); + buffer_free(s->ssl_cipher_list); + buffer_free(s->ssl_dh_file); + buffer_free(s->ssl_ec_curve); + buffer_free(s->ssl_verifyclient_username); + array_free(s->ssl_conf_cmd); + buffer_free(s->ssl_acme_tls_1); + + if (copy) continue; + SSL_CTX_free(s->ssl_ctx); + EVP_PKEY_free(s->ssl_pemfile_pkey); + X509_free(s->ssl_pemfile_x509); + if (NULL != s->ssl_ca_file_cert_names) + sk_X509_NAME_pop_free(s->ssl_ca_file_cert_names,X509_NAME_free); + } + for (size_t i = 0; i < srv->config_context->used; ++i) { + plugin_config *s = p->config_storage[i]; + if (NULL == s) continue; + + free(s); + } + free(p->config_storage); + } + + if (ssl_is_init) { + #if OPENSSL_VERSION_NUMBER >= 0x10100000L \ + && !defined(LIBRESSL_VERSION_NUMBER) + /*(OpenSSL libraries handle thread init and deinit) + * https://github.com/openssl/openssl/pull/1048 */ + #else + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); + #if OPENSSL_VERSION_NUMBER >= 0x10000000L + ERR_remove_thread_state(NULL); + #else + ERR_remove_state(0); + #endif + EVP_cleanup(); + #endif + + free(local_send_buffer); + } + + free(p); + + return HANDLER_GO_ON; +} + + +static int +safer_X509_NAME_oneline(X509_NAME *name, char *buf, size_t sz) +{ + BIO *bio = BIO_new(BIO_s_mem()); + if (bio) { + int len = X509_NAME_print_ex(bio, name, 0, XN_FLAG_ONELINE); + BIO_gets(bio, buf, (int)sz); /*(may be truncated if len >= sz)*/ + BIO_free(bio); + return len; /*return value has similar semantics to that of snprintf()*/ + } + else { + buf[0] = '\0'; + return -1; + } +} + + +static void +ssl_info_callback (const SSL *ssl, int where, int ret) +{ + UNUSED(ret); + + if (0 != (where & SSL_CB_HANDSHAKE_START)) { + handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl); + if (hctx->renegotiations >= 0) ++hctx->renegotiations; + } + #ifdef TLS1_3_VERSION + /* https://github.com/openssl/openssl/issues/5721 + * "TLSv1.3 unexpected InfoCallback after handshake completed" */ + if (0 != (where & SSL_CB_HANDSHAKE_DONE)) { + /* SSL_version() is valid after initial handshake completed */ + if (SSL_version(ssl) >= TLS1_3_VERSION) { + /* https://wiki.openssl.org/index.php/TLS1.3 + * "Renegotiation is not possible in a TLSv1.3 connection" */ + handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl); + hctx->renegotiations = -1; + } + } + #endif +} + +/* https://wiki.openssl.org/index.php/Manual:SSL_CTX_set_verify(3)#EXAMPLES */ +static int +verify_callback(int preverify_ok, X509_STORE_CTX *ctx) +{ + char buf[256]; + X509 *err_cert; + int err, depth; + SSL *ssl; + handler_ctx *hctx; + server *srv; + + err = X509_STORE_CTX_get_error(ctx); + depth = X509_STORE_CTX_get_error_depth(ctx); + + /* + * Retrieve the pointer to the SSL of the connection currently treated + * and the application specific data stored into the SSL object. + */ + ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + hctx = (handler_ctx *) SSL_get_app_data(ssl); + srv = hctx->srv; + + /* + * Catch a too long certificate chain. The depth limit set using + * SSL_CTX_set_verify_depth() is by purpose set to "limit+1" so + * that whenever the "depth>verify_depth" condition is met, we + * have violated the limit and want to log this error condition. + * We must do it here, because the CHAIN_TOO_LONG error would not + * be found explicitly; only errors introduced by cutting off the + * additional certificates would be logged. + */ + if (depth > hctx->conf.ssl_verifyclient_depth) { + preverify_ok = 0; + err = X509_V_ERR_CERT_CHAIN_TOO_LONG; + X509_STORE_CTX_set_error(ctx, err); + } + + if (preverify_ok && 0 == depth + && !buffer_string_is_empty(hctx->conf.ssl_ca_dn_file) + && !buffer_string_is_empty(hctx->conf.ssl_ca_file)) { + /* verify that client cert is issued by CA in ssl.ca-dn-file + * if both ssl.ca-dn-file and ssl.ca-file were configured */ + STACK_OF(X509_NAME) * const names = hctx->conf.ssl_ca_file_cert_names; + X509_NAME *issuer; + #if OPENSSL_VERSION_NUMBER >= 0x10002000L + err_cert = X509_STORE_CTX_get_current_cert(ctx); + #else + err_cert = ctx->current_cert; + #endif + if (NULL == err_cert) return !hctx->conf.ssl_verifyclient_enforce; + issuer = X509_get_issuer_name(err_cert); + #if 0 /*(?desirable/undesirable to have ssl_ca_file_cert_names sorted?)*/ + if (-1 != sk_X509_NAME_find(names, issuer)) + return preverify_ok; /* match */ + #else + for (int i = 0, len = sk_X509_NAME_num(names); i < len; ++i) { + if (0 == X509_NAME_cmp(sk_X509_NAME_value(names, i), issuer)) + return preverify_ok; /* match */ + } + #endif + + preverify_ok = 0; + err = X509_V_ERR_CERT_REJECTED; + X509_STORE_CTX_set_error(ctx, err); + } + + if (preverify_ok) { + return preverify_ok; + } + + #if OPENSSL_VERSION_NUMBER >= 0x10002000L + err_cert = X509_STORE_CTX_get_current_cert(ctx); + #else + err_cert = ctx->current_cert; + #endif + if (NULL == err_cert) return !hctx->conf.ssl_verifyclient_enforce; + safer_X509_NAME_oneline(X509_get_subject_name(err_cert),buf,sizeof(buf)); + log_error_write(srv, __FILE__, __LINE__, "SDSSSDSS", + "SSL: verify error:num=", err, ":", + X509_verify_cert_error_string(err), ":depth=", depth, + ":subject=", buf); + + /* + * At this point, err contains the last verification error. We can use + * it for something special + */ + if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY || + err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT)) { + safer_X509_NAME_oneline(X509_get_issuer_name(err_cert),buf,sizeof(buf)); + log_error_write(srv, __FILE__, __LINE__, "SS", "SSL: issuer=", buf); + } + + return !hctx->conf.ssl_verifyclient_enforce; +} + +#ifndef OPENSSL_NO_TLSEXT +static int mod_openssl_patch_connection (server *srv, connection *con, handler_ctx *hctx); + +static int +network_ssl_servername_callback (SSL *ssl, int *al, server *srv) +{ + const char *servername; + handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl); + connection *con = hctx->con; + size_t len; + UNUSED(al); + + buffer_copy_string(con->uri.scheme, "https"); + + servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (NULL == servername) { +#if 0 + /* this "error" just means the client didn't support it */ + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "failed to get TLS server name"); +#endif + return SSL_TLSEXT_ERR_NOACK; + } + len = strlen(servername); + if (len >= 1024) { /*(expecting < 256)*/ + log_error_write(srv, __FILE__, __LINE__, "sss", "SSL:", + "SNI name too long", servername); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + /* use SNI to patch mod_openssl config and then reset COMP_HTTP_HOST */ + buffer_copy_string_len(con->uri.authority, servername, len); + buffer_to_lower(con->uri.authority); + #if 0 + /*(con->uri.authority used below for configuration before request read; + * revisit for h2)*/ + if (0 != http_request_host_policy(con, con->uri.authority, con->uri.scheme)) + return SSL_TLSEXT_ERR_ALERT_FATAL; + #endif + + con->conditional_is_valid[COMP_HTTP_SCHEME] = 1; + con->conditional_is_valid[COMP_HTTP_HOST] = 1; + mod_openssl_patch_connection(srv, con, hctx); + /* reset COMP_HTTP_HOST so that conditions re-run after request hdrs read */ + /*(done in response.c:config_cond_cache_reset() after request hdrs read)*/ + /*config_cond_cache_reset_item(con, COMP_HTTP_HOST);*/ + /*buffer_clear(con->uri.authority);*/ + + if (NULL == hctx->conf.ssl_pemfile_x509 + || NULL == hctx->conf.ssl_pemfile_pkey) { + /* x509/pkey available <=> pemfile was set <=> pemfile got patched: + * so this should never happen, unless you nest $SERVER["socket"] */ + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + "no certificate/private key for TLS server name", + con->uri.authority); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + /* first set certificate! + * setting private key checks whether certificate matches it */ + if (1 != SSL_use_certificate(ssl, hctx->conf.ssl_pemfile_x509)) { + log_error_write(srv, __FILE__, __LINE__, "ssb:s", "SSL:", + "failed to set certificate for TLS server name", + con->uri.authority, + ERR_error_string(ERR_get_error(), NULL)); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + if (1 != SSL_use_PrivateKey(ssl, hctx->conf.ssl_pemfile_pkey)) { + log_error_write(srv, __FILE__, __LINE__, "ssb:s", "SSL:", + "failed to set private key for TLS server name", + con->uri.authority, + ERR_error_string(ERR_get_error(), NULL)); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + if (hctx->conf.ssl_verifyclient) { + int mode; + if (NULL == hctx->conf.ssl_ca_file_cert_names) { + log_error_write(srv, __FILE__, __LINE__, "ssb:s", "SSL:", + "can't verify client without ssl.ca-file " + "or ssl.ca-dn-file for TLS server name", + con->uri.authority, + ERR_error_string(ERR_get_error(), NULL)); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + SSL_set_client_CA_list( + ssl, SSL_dup_CA_list(hctx->conf.ssl_ca_file_cert_names)); + /* forcing verification here is really not that useful + * -- a client could just connect without SNI */ + mode = SSL_VERIFY_PEER; + if (hctx->conf.ssl_verifyclient_enforce) { + mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + } + SSL_set_verify(ssl, mode, verify_callback); + SSL_set_verify_depth(ssl, hctx->conf.ssl_verifyclient_depth + 1); + } else { + SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); + } + + return SSL_TLSEXT_ERR_OK; +} +#endif + + +static X509 * +x509_load_pem_file (server *srv, const char *file) +{ + BIO *in; + X509 *x = NULL; + + in = BIO_new(BIO_s_file()); + if (NULL == in) { + log_error_write(srv, __FILE__, __LINE__, "S", + "SSL: BIO_new(BIO_s_file()) failed"); + goto error; + } + + if (BIO_read_filename(in,file) <= 0) { + log_error_write(srv, __FILE__, __LINE__, "SSS", + "SSL: BIO_read_filename('", file,"') failed"); + goto error; + } + + x = PEM_read_bio_X509(in, NULL, NULL, NULL); + if (NULL == x) { + log_error_write(srv, __FILE__, __LINE__, "SSS", + "SSL: couldn't read X509 certificate from '", file,"'"); + goto error; + } + + BIO_free(in); + return x; + +error: + if (NULL != in) BIO_free(in); + return NULL; +} + + +static EVP_PKEY * +evp_pkey_load_pem_file (server *srv, const char *file) +{ + BIO *in; + EVP_PKEY *x = NULL; + + in = BIO_new(BIO_s_file()); + if (NULL == in) { + log_error_write(srv, __FILE__, __LINE__, "s", + "SSL: BIO_new(BIO_s_file()) failed"); + goto error; + } + + if (BIO_read_filename(in,file) <= 0) { + log_error_write(srv, __FILE__, __LINE__, "SSS", + "SSL: BIO_read_filename('", file,"') failed"); + goto error; + } + + x = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL); + if (NULL == x) { + log_error_write(srv, __FILE__, __LINE__, "SSS", + "SSL: couldn't read private key from '", file,"'"); + goto error; + } + + BIO_free(in); + return x; + +error: + if (NULL != in) BIO_free(in); + return NULL; +} + + +static int +network_openssl_load_pemfile (server *srv, plugin_config *s, size_t ndx) +{ + #ifdef OPENSSL_NO_TLSEXT + data_config *dc = (data_config *)srv->config_context->data[ndx]; + if ((ndx > 0 && (COMP_SERVER_SOCKET != dc->comp + || dc->cond != CONFIG_COND_EQ)) || !s->ssl_enabled) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "ssl.pemfile only works in SSL socket binding context " + "as openssl version does not support TLS extensions"); + return -1; + } + #else + UNUSED(ndx); + #endif + + s->ssl_pemfile_x509 = x509_load_pem_file(srv, s->ssl_pemfile->ptr); + if (NULL == s->ssl_pemfile_x509) return -1; + s->ssl_pemfile_pkey = !buffer_string_is_empty(s->ssl_privkey) + ? evp_pkey_load_pem_file(srv, s->ssl_privkey->ptr) + : evp_pkey_load_pem_file(srv, s->ssl_pemfile->ptr); + if (NULL == s->ssl_pemfile_pkey) return -1; + + if (!X509_check_private_key(s->ssl_pemfile_x509, s->ssl_pemfile_pkey)) { + log_error_write(srv, __FILE__, __LINE__, "sssbb", "SSL:", + "Private key does not match the certificate public key," + " reason:", ERR_error_string(ERR_get_error(), NULL), + s->ssl_pemfile, s->ssl_privkey); + return -1; + } + + return 0; +} + + +#ifndef OPENSSL_NO_TLSEXT + +#if OPENSSL_VERSION_NUMBER >= 0x10002000 + +static int +mod_openssl_acme_tls_1 (SSL *ssl, handler_ctx *hctx) +{ + server *srv = hctx->srv; + buffer *b = srv->tmp_buf; + buffer *name = hctx->con->uri.authority; + X509 *ssl_pemfile_x509 = NULL; + EVP_PKEY *ssl_pemfile_pkey = NULL; + size_t len; + int rc = SSL_TLSEXT_ERR_ALERT_FATAL; + + /* check if acme-tls/1 protocol is enabled (path to dir of cert(s) is set)*/ + if (buffer_string_is_empty(hctx->conf.ssl_acme_tls_1)) + return SSL_TLSEXT_ERR_NOACK; /*(reuse value here for not-configured)*/ + buffer_copy_buffer(b, hctx->conf.ssl_acme_tls_1); + buffer_append_slash(b); + + /* check if SNI set server name (required for acme-tls/1 protocol) + * and perform simple path checks for no '/' + * and no leading '.' (e.g. ignore "." or ".." or anything beginning '.') */ + if (buffer_string_is_empty(name)) return rc; + if (NULL != strchr(name->ptr, '/')) return rc; + if (name->ptr[0] == '.') return rc; + #if 0 + if (0 != http_request_host_policy(hctx->con, name, hctx->con->uri.scheme)) + return rc; + #endif + buffer_append_string_buffer(b, name); + len = buffer_string_length(b); + + do { + buffer_append_string_len(b, CONST_STR_LEN(".crt.pem")); + ssl_pemfile_x509 = x509_load_pem_file(srv, b->ptr); + if (NULL == ssl_pemfile_x509) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + "Failed to load acme-tls/1 pemfile:", b); + break; + } + + buffer_string_set_length(b, len); /*(remove ".crt.pem")*/ + buffer_append_string_len(b, CONST_STR_LEN(".key.pem")); + ssl_pemfile_pkey = evp_pkey_load_pem_file(srv, b->ptr); + if (NULL == ssl_pemfile_pkey) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + "Failed to load acme-tls/1 pemfile:", b); + break; + } + + #if 0 /* redundant with below? */ + if (!X509_check_private_key(ssl_pemfile_x509, ssl_pemfile_pkey)) { + log_error_write(srv, __FILE__, __LINE__, "sssb", "SSL:", + "Private key does not match acme-tls/1 certificate public key," + " reason:" ERR_error_string(ERR_get_error(), NULL), b); + break; + } + #endif + + /* first set certificate! + * setting private key checks whether certificate matches it */ + if (1 != SSL_use_certificate(ssl, ssl_pemfile_x509)) { + log_error_write(srv, __FILE__, __LINE__, "ssb:s", "SSL:", + "failed to set acme-tls/1 certificate for TLS server name", + name, ERR_error_string(ERR_get_error(), NULL)); + break; + } + + if (1 != SSL_use_PrivateKey(ssl, ssl_pemfile_pkey)) { + log_error_write(srv, __FILE__, __LINE__, "ssb:s", "SSL:", + "failed to set acme-tls/1 private key for TLS server name", + name, ERR_error_string(ERR_get_error(), NULL)); + break; + } + + rc = SSL_TLSEXT_ERR_OK; + } while (0); + + if (ssl_pemfile_pkey) EVP_PKEY_free(ssl_pemfile_pkey); + if (ssl_pemfile_x509) X509_free(ssl_pemfile_x509); + + return rc; +} + +enum { + MOD_OPENSSL_ALPN_HTTP11 = 1 + ,MOD_OPENSSL_ALPN_HTTP10 = 2 + ,MOD_OPENSSL_ALPN_H2 = 3 + ,MOD_OPENSSL_ALPN_ACME_TLS_1 = 4 +}; + +/* https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids */ +static int +mod_openssl_alpn_select_cb (SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) +{ + handler_ctx *hctx = (handler_ctx *) SSL_get_app_data(ssl); + unsigned short proto; + UNUSED(arg); + + for (unsigned int i = 0, n; i < inlen; i += n) { + n = in[i++]; + if (i+n > inlen) break; + switch (n) { + #if 0 + case 2: /* "h2" */ + if (in[i] == 'h' && in[i+1] == '2') { + proto = MOD_OPENSSL_ALPN_H2; + break; + } + continue; + #endif + case 8: /* "http/1.1" "http/1.0" */ + if (0 == memcmp(in+i, "http/1.", 7)) { + if (in[i+7] == '1') { + proto = MOD_OPENSSL_ALPN_HTTP11; + break; + } + if (in[i+7] == '0') { + proto = MOD_OPENSSL_ALPN_HTTP10; + break; + } + } + continue; + case 10: /* "acme-tls/1" */ + if (0 == memcmp(in+i, "acme-tls/1", 10)) { + int rc = mod_openssl_acme_tls_1(ssl, hctx); + if (rc == SSL_TLSEXT_ERR_OK) { + proto = MOD_OPENSSL_ALPN_ACME_TLS_1; + break; + } + /* (use SSL_TLSEXT_ERR_NOACK for not-configured) */ + if (rc == SSL_TLSEXT_ERR_NOACK) continue; + return rc; + } + continue; + default: + continue; + } + + hctx->alpn = proto; + *out = in+i; + *outlen = n; + return SSL_TLSEXT_ERR_OK; + } + + #if OPENSSL_VERSION_NUMBER < 0x10100000L + return SSL_TLSEXT_ERR_NOACK; + #else + return SSL_TLSEXT_ERR_ALERT_FATAL; + #endif +} + +#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000 */ + +#endif /* OPENSSL_NO_TLSEXT */ + + +static int +network_openssl_ssl_conf_cmd (server *srv, plugin_config *s) +{ + #ifdef SSL_CONF_FLAG_CMDLINE + + int rc = 0; + data_string *ds; + SSL_CONF_CTX * const cctx = SSL_CONF_CTX_new(); + SSL_CONF_CTX_set_ssl_ctx(cctx, s->ssl_ctx); + SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE + | SSL_CONF_FLAG_SERVER + | SSL_CONF_FLAG_SHOW_ERRORS + | SSL_CONF_FLAG_CERTIFICATE); + + /* always disable null and export ciphers */ + ds = (data_string *) + array_get_element_klen(s->ssl_conf_cmd, + CONST_STR_LEN("CipherString")); + if (NULL != ds) { + buffer_append_string_len(ds->value, + CONST_STR_LEN(":!aNULL:!eNULL:!EXP")); + } + + for (size_t i = 0; i < s->ssl_conf_cmd->used; ++i) { + ds = (data_string *)s->ssl_conf_cmd->data[i]; + ERR_clear_error(); + if (SSL_CONF_cmd(cctx, ds->key->ptr, ds->value->ptr) <= 0) { + log_error_write(srv, __FILE__, __LINE__, "ssbbss", "SSL:", + "SSL_CONF_cmd", ds->key, ds->value, ":", + ERR_error_string(ERR_get_error(), NULL)); + rc = -1; + break; + } + } + + if (0 == rc && 1 != SSL_CONF_CTX_finish(cctx)) { + log_error_write(srv, __FILE__, __LINE__, "sss", "SSL:", + "SSL_CONF_CTX_finish():", + ERR_error_string(ERR_get_error(), NULL)); + rc = -1; + } + + SSL_CONF_CTX_free(cctx); + return rc; + + #else + + UNUSED(s); + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "ssl.openssl.ssl-conf-cmd not available; ignored"); + return 0; + + #endif +} + + +static int +network_init_ssl (server *srv, void *p_d) +{ + plugin_data *p = p_d; + + #ifndef OPENSSL_NO_DH + /* 1024-bit MODP Group with 160-bit prime order subgroup (RFC5114) + * -----BEGIN DH PARAMETERS----- + * MIIBDAKBgQCxC4+WoIDgHd6S3l6uXVTsUsmfvPsGo8aaap3KUtI7YWBz4oZ1oj0Y + * mDjvHi7mUsAT7LSuqQYRIySXXDzUm4O/rMvdfZDEvXCYSI6cIZpzck7/1vrlZEc4 + * +qMaT/VbzMChUa9fDci0vUW/N982XBpl5oz9p21NpwjfH7K8LkpDcQKBgQCk0cvV + * w/00EmdlpELvuZkF+BBN0lisUH/WQGz/FCZtMSZv6h5cQVZLd35pD1UE8hMWAhe0 + * sBuIal6RVH+eJ0n01/vX07mpLuGQnQ0iY/gKdqaiTAh6CR9THb8KAWm2oorWYqTR + * jnOvoy13nVkY0IvIhY9Nzvl8KiSFXm7rIrOy5QICAKA= + * -----END DH PARAMETERS----- + */ + + static const unsigned char dh1024_p[]={ + 0xB1,0x0B,0x8F,0x96,0xA0,0x80,0xE0,0x1D,0xDE,0x92,0xDE,0x5E, + 0xAE,0x5D,0x54,0xEC,0x52,0xC9,0x9F,0xBC,0xFB,0x06,0xA3,0xC6, + 0x9A,0x6A,0x9D,0xCA,0x52,0xD2,0x3B,0x61,0x60,0x73,0xE2,0x86, + 0x75,0xA2,0x3D,0x18,0x98,0x38,0xEF,0x1E,0x2E,0xE6,0x52,0xC0, + 0x13,0xEC,0xB4,0xAE,0xA9,0x06,0x11,0x23,0x24,0x97,0x5C,0x3C, + 0xD4,0x9B,0x83,0xBF,0xAC,0xCB,0xDD,0x7D,0x90,0xC4,0xBD,0x70, + 0x98,0x48,0x8E,0x9C,0x21,0x9A,0x73,0x72,0x4E,0xFF,0xD6,0xFA, + 0xE5,0x64,0x47,0x38,0xFA,0xA3,0x1A,0x4F,0xF5,0x5B,0xCC,0xC0, + 0xA1,0x51,0xAF,0x5F,0x0D,0xC8,0xB4,0xBD,0x45,0xBF,0x37,0xDF, + 0x36,0x5C,0x1A,0x65,0xE6,0x8C,0xFD,0xA7,0x6D,0x4D,0xA7,0x08, + 0xDF,0x1F,0xB2,0xBC,0x2E,0x4A,0x43,0x71, + }; + + static const unsigned char dh1024_g[]={ + 0xA4,0xD1,0xCB,0xD5,0xC3,0xFD,0x34,0x12,0x67,0x65,0xA4,0x42, + 0xEF,0xB9,0x99,0x05,0xF8,0x10,0x4D,0xD2,0x58,0xAC,0x50,0x7F, + 0xD6,0x40,0x6C,0xFF,0x14,0x26,0x6D,0x31,0x26,0x6F,0xEA,0x1E, + 0x5C,0x41,0x56,0x4B,0x77,0x7E,0x69,0x0F,0x55,0x04,0xF2,0x13, + 0x16,0x02,0x17,0xB4,0xB0,0x1B,0x88,0x6A,0x5E,0x91,0x54,0x7F, + 0x9E,0x27,0x49,0xF4,0xD7,0xFB,0xD7,0xD3,0xB9,0xA9,0x2E,0xE1, + 0x90,0x9D,0x0D,0x22,0x63,0xF8,0x0A,0x76,0xA6,0xA2,0x4C,0x08, + 0x7A,0x09,0x1F,0x53,0x1D,0xBF,0x0A,0x01,0x69,0xB6,0xA2,0x8A, + 0xD6,0x62,0xA4,0xD1,0x8E,0x73,0xAF,0xA3,0x2D,0x77,0x9D,0x59, + 0x18,0xD0,0x8B,0xC8,0x85,0x8F,0x4D,0xCE,0xF9,0x7C,0x2A,0x24, + 0x85,0x5E,0x6E,0xEB,0x22,0xB3,0xB2,0xE5, + }; + #endif + + /* load SSL certificates */ + for (size_t i = 0; i < srv->config_context->used; ++i) { + plugin_config *s = p->config_storage[i]; + #ifndef SSL_OP_NO_COMPRESSION + #define SSL_OP_NO_COMPRESSION 0 + #endif + #ifndef SSL_MODE_RELEASE_BUFFERS /* OpenSSL >= 1.0.0 */ + #define SSL_MODE_RELEASE_BUFFERS 0 + #endif + long ssloptions = SSL_OP_ALL + | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + | SSL_OP_NO_COMPRESSION; + + if (s->ssl_enabled) { + if (buffer_string_is_empty(s->ssl_pemfile)) { + /* inherit ssl settings from global scope + * (if only ssl.engine = "enable" and no other ssl.* settings)*/ + if (0 != i && p->config_storage[0]->ssl_enabled) { + s->ssl_ctx = p->config_storage[0]->ssl_ctx; + continue; + } + /* PEM file is require */ + log_error_write(srv, __FILE__, __LINE__, "s", + "ssl.pemfile has to be set " + "when ssl.engine = \"enable\""); + return -1; + } + } + + if (buffer_string_is_empty(s->ssl_pemfile) + && buffer_string_is_empty(s->ssl_ca_dn_file) + && buffer_string_is_empty(s->ssl_ca_file)) continue; + + if (ssl_is_init == 0) { + #if OPENSSL_VERSION_NUMBER >= 0x10100000L \ + && !defined(LIBRESSL_VERSION_NUMBER) + OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS + |OPENSSL_INIT_LOAD_CRYPTO_STRINGS,NULL); + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS + |OPENSSL_INIT_ADD_ALL_DIGESTS + |OPENSSL_INIT_LOAD_CONFIG, NULL); + #else + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); + #endif + ssl_is_init = 1; + + if (0 == RAND_status()) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "not enough entropy in the pool"); + return -1; + } + + local_send_buffer = malloc(LOCAL_SEND_BUFSIZE); + force_assert(NULL != local_send_buffer); + } + + if (!buffer_string_is_empty(s->ssl_pemfile)) { + #ifdef OPENSSL_NO_TLSEXT + data_config *dc = (data_config *)srv->config_context->data[i]; + if (COMP_HTTP_HOST == dc->comp) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "can't use ssl.pemfile with $HTTP[\"host\"], " + "openssl version does not support TLS " + "extensions"); + return -1; + } + #endif + if (network_openssl_load_pemfile(srv, s, i)) return -1; + } + + + if (!buffer_string_is_empty(s->ssl_ca_dn_file)) { + s->ssl_ca_file_cert_names = + SSL_load_client_CA_file(s->ssl_ca_dn_file->ptr); + if (NULL == s->ssl_ca_file_cert_names) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), + s->ssl_ca_dn_file); + } + } + + if (NULL == s->ssl_ca_file_cert_names + && !buffer_string_is_empty(s->ssl_ca_file)) { + s->ssl_ca_file_cert_names = + SSL_load_client_CA_file(s->ssl_ca_file->ptr); + if (NULL == s->ssl_ca_file_cert_names) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), + s->ssl_ca_file); + } + } + + if (buffer_string_is_empty(s->ssl_pemfile) || !s->ssl_enabled) continue; + + #if OPENSSL_VERSION_NUMBER >= 0x10100000L + s->ssl_ctx = (!s->ssl_use_sslv2 && !s->ssl_use_sslv3) + ? SSL_CTX_new(TLS_server_method()) + : SSL_CTX_new(SSLv23_server_method()); + #else + s->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + #endif + if (NULL == s->ssl_ctx) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + /* completely useless identifier; + * required for client cert verification to work with sessions */ + if (0 == SSL_CTX_set_session_id_context( + s->ssl_ctx,(const unsigned char*)CONST_STR_LEN("lighttpd"))){ + log_error_write(srv, __FILE__, __LINE__, "ss:s", "SSL:", + "failed to set session context", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + if (s->ssl_empty_fragments) { + #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS + ssloptions &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + #else + ssloptions &= ~0x00000800L; /* hardcode constant */ + log_error_write(srv, __FILE__, __LINE__, "ss", "WARNING: SSL:", + "'insert empty fragments' not supported by the " + "openssl version used to compile lighttpd with"); + #endif + } + + SSL_CTX_set_options(s->ssl_ctx, ssloptions); + SSL_CTX_set_info_callback(s->ssl_ctx, ssl_info_callback); + + #ifndef HAVE_WOLFSSL_SSL_H /*(wolfSSL does not support SSLv2)*/ + if (!s->ssl_use_sslv2 && 0 != SSL_OP_NO_SSLv2) { + /* disable SSLv2 */ + if ((SSL_OP_NO_SSLv2 + & SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv2)) + != SSL_OP_NO_SSLv2) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + } + #endif + + if (!s->ssl_use_sslv3 && 0 != SSL_OP_NO_SSLv3) { + /* disable SSLv3 */ + if ((SSL_OP_NO_SSLv3 + & SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv3)) + != SSL_OP_NO_SSLv3) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + } + + if (!buffer_string_is_empty(s->ssl_cipher_list)) { + /* Disable support for low encryption ciphers */ + if (SSL_CTX_set_cipher_list(s->ssl_ctx,s->ssl_cipher_list->ptr)!=1){ + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + if (s->ssl_honor_cipher_order) { + SSL_CTX_set_options(s->ssl_ctx,SSL_OP_CIPHER_SERVER_PREFERENCE); + } + } + + #ifndef OPENSSL_NO_DH + { + DH *dh; + /* Support for Diffie-Hellman key exchange */ + if (!buffer_string_is_empty(s->ssl_dh_file)) { + /* DH parameters from file */ + BIO *bio; + bio = BIO_new_file((char *) s->ssl_dh_file->ptr, "r"); + if (bio == NULL) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "SSL: Unable to open file", + s->ssl_dh_file->ptr); + return -1; + } + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + if (dh == NULL) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "SSL: PEM_read_bio_DHparams failed", + s->ssl_dh_file->ptr); + return -1; + } + } else { + BIGNUM *dh_p, *dh_g; + /* Default DH parameters from RFC5114 */ + dh = DH_new(); + if (dh == NULL) { + log_error_write(srv, __FILE__, __LINE__, "s", + "SSL: DH_new () failed"); + return -1; + } + dh_p = BN_bin2bn(dh1024_p,sizeof(dh1024_p), NULL); + dh_g = BN_bin2bn(dh1024_g,sizeof(dh1024_g), NULL); + if ((dh_p == NULL) || (dh_g == NULL)) { + DH_free(dh); + log_error_write(srv, __FILE__, __LINE__, "s", + "SSL: BN_bin2bn () failed"); + return -1; + } + #if OPENSSL_VERSION_NUMBER < 0x10100000L \ + || defined(LIBRESSL_VERSION_NUMBER) + dh->p = dh_p; + dh->g = dh_g; + dh->length = 160; + #else + DH_set0_pqg(dh, dh_p, NULL, dh_g); + DH_set_length(dh, 160); + #endif + } + SSL_CTX_set_tmp_dh(s->ssl_ctx,dh); + SSL_CTX_set_options(s->ssl_ctx,SSL_OP_SINGLE_DH_USE); + DH_free(dh); + } + #else + if (!buffer_string_is_empty(s->ssl_dh_file)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "SSL: openssl compiled without DH support, " + "can't load parameters from", s->ssl_dh_file->ptr); + } + #endif + + #if OPENSSL_VERSION_NUMBER >= 0x0090800fL + #ifndef OPENSSL_NO_ECDH + { + int nid = 0; + /* Support for Elliptic-Curve Diffie-Hellman key exchange */ + if (!buffer_string_is_empty(s->ssl_ec_curve)) { + /* OpenSSL only supports the "named curves" + * from RFC 4492, section 5.1.1. */ + nid = OBJ_sn2nid((char *) s->ssl_ec_curve->ptr); + if (nid == 0) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "SSL: Unknown curve name", + s->ssl_ec_curve->ptr); + return -1; + } + } else { + #if OPENSSL_VERSION_NUMBER < 0x10002000 + /* Default curve */ + nid = OBJ_sn2nid("prime256v1"); + #elif OPENSSL_VERSION_NUMBER < 0x10100000L \ + || defined(LIBRESSL_VERSION_NUMBER) + if (!SSL_CTX_set_ecdh_auto(s->ssl_ctx, 1)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "SSL: SSL_CTX_set_ecdh_auto() failed"); + } + #endif + } + if (nid) { + EC_KEY *ecdh; + ecdh = EC_KEY_new_by_curve_name(nid); + if (ecdh == NULL) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "SSL: Unable to create curve", + s->ssl_ec_curve->ptr); + return -1; + } + SSL_CTX_set_tmp_ecdh(s->ssl_ctx,ecdh); + SSL_CTX_set_options(s->ssl_ctx,SSL_OP_SINGLE_ECDH_USE); + EC_KEY_free(ecdh); + } + } + #endif + #endif + + /* load all ssl.ca-files specified in the config into each SSL_CTX + * to be prepared for SNI */ + for (size_t j = 0; j < srv->config_context->used; ++j) { + plugin_config *s1 = p->config_storage[j]; + + if (!buffer_string_is_empty(s1->ssl_ca_dn_file)) { + if (1 != SSL_CTX_load_verify_locations( + s->ssl_ctx, s1->ssl_ca_dn_file->ptr, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), + s1->ssl_ca_dn_file); + return -1; + } + } + if (!buffer_string_is_empty(s1->ssl_ca_file)) { + if (1 != SSL_CTX_load_verify_locations( + s->ssl_ctx, s1->ssl_ca_file->ptr, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), + s1->ssl_ca_file); + return -1; + } + } + } + + if (s->ssl_verifyclient) { + int mode; + if (NULL == s->ssl_ca_file_cert_names) { + log_error_write(srv, __FILE__, __LINE__, "s", + "SSL: You specified ssl.verifyclient.activate " + "but no ssl.ca-file or ssl.ca-dn-file"); + return -1; + } + SSL_CTX_set_client_CA_list( + s->ssl_ctx, SSL_dup_CA_list(s->ssl_ca_file_cert_names)); + mode = SSL_VERIFY_PEER; + if (s->ssl_verifyclient_enforce) { + mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + } + SSL_CTX_set_verify(s->ssl_ctx, mode, verify_callback); + SSL_CTX_set_verify_depth(s->ssl_ctx, s->ssl_verifyclient_depth + 1); + if (!buffer_string_is_empty(s->ssl_ca_crl_file)) { + X509_STORE *store = SSL_CTX_get_cert_store(s->ssl_ctx); + if (1 != X509_STORE_load_locations(store, s->ssl_ca_crl_file->ptr, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), s->ssl_ca_crl_file); + return -1; + } + X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + } + } + + if (1 != SSL_CTX_use_certificate_chain_file(s->ssl_ctx, + s->ssl_pemfile->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), + s->ssl_pemfile); + return -1; + } + + if (1 != SSL_CTX_use_PrivateKey(s->ssl_ctx, s->ssl_pemfile_pkey)) { + log_error_write(srv, __FILE__, __LINE__, "ssbb", "SSL:", + ERR_error_string(ERR_get_error(), NULL), + s->ssl_pemfile, s->ssl_privkey); + return -1; + } + + if (SSL_CTX_check_private_key(s->ssl_ctx) != 1) { + log_error_write(srv, __FILE__, __LINE__, "sssbb", "SSL:", + "Private key does not match the certificate public " + "key, reason:", + ERR_error_string(ERR_get_error(), NULL), + s->ssl_pemfile, s->ssl_privkey); + return -1; + } + SSL_CTX_set_default_read_ahead(s->ssl_ctx, s->ssl_read_ahead); + SSL_CTX_set_mode(s->ssl_ctx, SSL_CTX_get_mode(s->ssl_ctx) + | SSL_MODE_ENABLE_PARTIAL_WRITE + | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER + | SSL_MODE_RELEASE_BUFFERS); + + #ifndef OPENSSL_NO_TLSEXT + if (!SSL_CTX_set_tlsext_servername_callback( + s->ssl_ctx, network_ssl_servername_callback) || + !SSL_CTX_set_tlsext_servername_arg(s->ssl_ctx, srv)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + "failed to initialize TLS servername callback, " + "openssl library does not support TLS servername " + "extension"); + return -1; + } + + #if OPENSSL_VERSION_NUMBER >= 0x10002000 + SSL_CTX_set_alpn_select_cb(s->ssl_ctx,mod_openssl_alpn_select_cb,NULL); + #endif + #endif + + if (s->ssl_conf_cmd->used) { + if (0 != network_openssl_ssl_conf_cmd(srv, s)) return -1; + } + } + + return 0; +} + + +SETDEFAULTS_FUNC(mod_openssl_set_defaults) +{ + plugin_data *p = p_d; + config_values_t cv[] = { + { "debug.log-ssl-noise", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "ssl.engine", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "ssl.pemfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "ssl.ca-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "ssl.dh-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "ssl.ec-curve", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { "ssl.cipher-list", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ + { "ssl.honor-cipher-order", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ + { "ssl.empty-fragments", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ + { "ssl.disable-client-renegotiation", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ + { "ssl.read-ahead", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ + { "ssl.verifyclient.activate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ + { "ssl.verifyclient.enforce", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ + { "ssl.verifyclient.depth", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ + { "ssl.verifyclient.username", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ + { "ssl.verifyclient.exportcert", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 15 */ + { "ssl.use-sslv2", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 16 */ + { "ssl.use-sslv3", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 17 */ + { "ssl.ca-crl-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 18 */ + { "ssl.ca-dn-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 19 */ + { "ssl.openssl.ssl-conf-cmd", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 20 */ + { "ssl.acme-tls-1", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 21 */ + { "ssl.privkey", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 22 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (size_t i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s = calloc(1, sizeof(plugin_config)); + + s->ssl_enabled = 0; + s->ssl_pemfile = buffer_init(); + s->ssl_privkey = buffer_init(); + s->ssl_ca_file = buffer_init(); + s->ssl_ca_crl_file = buffer_init(); + s->ssl_ca_dn_file = buffer_init(); + s->ssl_cipher_list = buffer_init(); + s->ssl_dh_file = buffer_init(); + s->ssl_ec_curve = buffer_init(); + s->ssl_honor_cipher_order = 1; + s->ssl_empty_fragments = 0; + s->ssl_use_sslv2 = 0; + s->ssl_use_sslv3 = 0; + s->ssl_verifyclient = 0; + s->ssl_verifyclient_enforce = 1; + s->ssl_verifyclient_username = buffer_init(); + s->ssl_verifyclient_depth = 9; + s->ssl_verifyclient_export_cert = 0; + s->ssl_disable_client_renegotiation = 1; + s->ssl_read_ahead = (0 == i) + ? 0 + : p->config_storage[0]->ssl_read_ahead; + if (0 != i) buffer_copy_buffer(s->ssl_ca_crl_file, p->config_storage[0]->ssl_ca_crl_file); + if (0 != i) buffer_copy_buffer(s->ssl_ca_dn_file, p->config_storage[0]->ssl_ca_dn_file); + s->ssl_conf_cmd = (0 == i) + ? array_init() + : array_init_array(p->config_storage[0]->ssl_conf_cmd); + s->ssl_acme_tls_1 = buffer_init(); + + cv[0].destination = &(s->ssl_log_noise); + cv[1].destination = &(s->ssl_enabled); + cv[2].destination = s->ssl_pemfile; + cv[3].destination = s->ssl_ca_file; + cv[4].destination = s->ssl_dh_file; + cv[5].destination = s->ssl_ec_curve; + cv[6].destination = s->ssl_cipher_list; + cv[7].destination = &(s->ssl_honor_cipher_order); + cv[8].destination = &(s->ssl_empty_fragments); + cv[9].destination = &(s->ssl_disable_client_renegotiation); + cv[10].destination = &(s->ssl_read_ahead); + cv[11].destination = &(s->ssl_verifyclient); + cv[12].destination = &(s->ssl_verifyclient_enforce); + cv[13].destination = &(s->ssl_verifyclient_depth); + cv[14].destination = s->ssl_verifyclient_username; + cv[15].destination = &(s->ssl_verifyclient_export_cert); + cv[16].destination = &(s->ssl_use_sslv2); + cv[17].destination = &(s->ssl_use_sslv3); + cv[18].destination = s->ssl_ca_crl_file; + cv[19].destination = s->ssl_ca_dn_file; + cv[20].destination = s->ssl_conf_cmd; + cv[21].destination = s->ssl_acme_tls_1; + cv[22].destination = s->ssl_privkey; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (0 != i && s->ssl_enabled && buffer_string_is_empty(s->ssl_pemfile)){ + /* inherit ssl settings from global scope (in network_init_ssl()) + * (if only ssl.engine = "enable" and no other ssl.* settings)*/ + for (size_t j = 0; j < config->value->used; ++j) { + buffer *k = config->value->data[j]->key; + if (0 == strncmp(k->ptr, "ssl.", sizeof("ssl.")-1) + && !buffer_is_equal_string(k, CONST_STR_LEN("ssl.engine"))){ + log_error_write(srv, __FILE__, __LINE__, "sb", + "ssl.pemfile has to be set in same scope " + "as other ssl.* directives, unless only " + "ssl.engine is set, inheriting ssl.* from " + "global scope", k); + return HANDLER_ERROR; + } + } + } + + if (0 != i && s->ssl_enabled && config->comp != COMP_SERVER_SOCKET) { + log_error_write(srv, __FILE__, __LINE__, "s", + "ssl.engine is valid only in global scope " + "or $SERVER[\"socket\"] condition"); + } + + if (!array_is_kvstring(s->ssl_conf_cmd)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "ssl.openssl.ssl-conf-cmd must be array " + "of \"key\" => \"value\" strings"); + } + } + + if (0 != network_init_ssl(srv, p)) return HANDLER_ERROR; + + return HANDLER_GO_ON; +} + + +#define PATCH(x) \ + hctx->conf.x = s->x; +static int +mod_openssl_patch_connection (server *srv, connection *con, handler_ctx *hctx) +{ + plugin_config *s = plugin_data_singleton->config_storage[0]; + + /*PATCH(ssl_enabled);*//*(not patched)*/ + /*PATCH(ssl_pemfile);*//*(not patched)*/ + /*PATCH(ssl_privkey);*//*(not patched)*/ + PATCH(ssl_pemfile_x509); + PATCH(ssl_pemfile_pkey); + PATCH(ssl_ca_file); + /*PATCH(ssl_ca_crl_file);*//*(not patched)*/ + PATCH(ssl_ca_dn_file); + PATCH(ssl_ca_file_cert_names); + /*PATCH(ssl_cipher_list);*//*(not patched)*/ + /*PATCH(ssl_dh_file);*//*(not patched)*/ + /*PATCH(ssl_ec_curve);*//*(not patched)*/ + /*PATCH(ssl_honor_cipher_order);*//*(not patched)*/ + /*PATCH(ssl_empty_fragments);*//*(not patched)*/ + /*PATCH(ssl_use_sslv2);*//*(not patched)*/ + /*PATCH(ssl_use_sslv3);*//*(not patched)*/ + /*PATCH(ssl_conf_cmd);*//*(not patched)*/ + + PATCH(ssl_verifyclient); + PATCH(ssl_verifyclient_enforce); + PATCH(ssl_verifyclient_depth); + PATCH(ssl_verifyclient_username); + PATCH(ssl_verifyclient_export_cert); + PATCH(ssl_disable_client_renegotiation); + PATCH(ssl_read_ahead); + PATCH(ssl_acme_tls_1); + + PATCH(ssl_log_noise); + + /* skip the first, the global context */ + for (size_t i = 1; i < srv->config_context->used; ++i) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = plugin_data_singleton->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (size_t j = 0; j < dc->value->used; ++j) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.pemfile"))) { + /*PATCH(ssl_pemfile);*//*(not patched)*/ + /*PATCH(ssl_privkey);*//*(not patched)*/ + PATCH(ssl_pemfile_x509); + PATCH(ssl_pemfile_pkey); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.ca-file"))) { + PATCH(ssl_ca_file); + PATCH(ssl_ca_file_cert_names); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.ca-dn-file"))) { + PATCH(ssl_ca_dn_file); + PATCH(ssl_ca_file_cert_names); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.activate"))) { + PATCH(ssl_verifyclient); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.enforce"))) { + PATCH(ssl_verifyclient_enforce); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.depth"))) { + PATCH(ssl_verifyclient_depth); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.username"))) { + PATCH(ssl_verifyclient_username); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.verifyclient.exportcert"))) { + PATCH(ssl_verifyclient_export_cert); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.disable-client-renegotiation"))) { + PATCH(ssl_disable_client_renegotiation); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.read-ahead"))) { + PATCH(ssl_read_ahead); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.acme-tls-1"))) { + PATCH(ssl_acme_tls_1); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("debug.log-ssl-noise"))) { + PATCH(ssl_log_noise); + #if 0 /*(not patched)*/ + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.ca-crl-file"))) { + PATCH(ssl_ca_crl_file); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.honor-cipher-order"))) { + PATCH(ssl_honor_cipher_order); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.empty-fragments"))) { + PATCH(ssl_empty_fragments); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.use-sslv2"))) { + PATCH(ssl_use_sslv2); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.use-sslv3"))) { + PATCH(ssl_use_sslv3); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.cipher-list"))) { + PATCH(ssl_cipher_list); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.dh-file"))) { + PATCH(ssl_dh_file); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.ec-curve"))) { + PATCH(ssl_ec_curve); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.engine"))) { + PATCH(ssl_enabled); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.openssl.ssl-conf-cmd"))) { + PATCH(ssl_conf_cmd); + #endif + } + } + } + + return 0; +} +#undef PATCH + + +static int +load_next_chunk (server *srv, chunkqueue *cq, off_t max_bytes, + const char **data, size_t *data_len) +{ + chunk *c = cq->first; + + /* local_send_buffer is a 64k sendbuffer (LOCAL_SEND_BUFSIZE) + * + * it has to stay at the same location all the time to satisfy the needs + * of SSL_write to pass the SAME parameter in case of a _WANT_WRITE + * + * buffer is allocated once, is NOT realloced (note: not thread-safe) + * + * (Note: above restriction no longer true since SSL_CTX_set_mode() is + * called with SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER) + * */ + + force_assert(NULL != c); + + switch (c->type) { + case MEM_CHUNK: + *data = NULL; + *data_len = 0; + do { + size_t have; + + force_assert(c->offset >= 0 + && c->offset <= (off_t)buffer_string_length(c->mem)); + + have = buffer_string_length(c->mem) - c->offset; + + /* copy small mem chunks into single large buffer before SSL_write() + * to reduce number times write() called underneath SSL_write() and + * potentially reduce number of packets generated if TCP_NODELAY */ + if (*data_len) { + size_t space = LOCAL_SEND_BUFSIZE - *data_len; + if (have > space) + have = space; + if (have > (size_t)max_bytes - *data_len) + have = (size_t)max_bytes - *data_len; + if (*data != local_send_buffer) { + memcpy(local_send_buffer, *data, *data_len); + *data = local_send_buffer; + } + memcpy(local_send_buffer+*data_len,c->mem->ptr+c->offset,have); + *data_len += have; + continue; + } + + if ((off_t) have > max_bytes) have = max_bytes; + + *data = c->mem->ptr + c->offset; + *data_len = have; + } while ((c = c->next) && c->type == MEM_CHUNK + && *data_len < LOCAL_SEND_BUFSIZE + && (off_t) *data_len < max_bytes); + return 0; + + case FILE_CHUNK: + if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; + + { + off_t offset, toSend; + + force_assert(c->offset >= 0 && c->offset <= c->file.length); + offset = c->file.start + c->offset; + toSend = c->file.length - c->offset; + + if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE; + if (toSend > max_bytes) toSend = max_bytes; + + if (-1 == lseek(c->file.fd, offset, SEEK_SET)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "lseek: ", strerror(errno)); + return -1; + } + if (-1 == (toSend = read(c->file.fd, local_send_buffer, toSend))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "read: ", strerror(errno)); + return -1; + } + + *data = local_send_buffer; + *data_len = toSend; + } + return 0; + } + + return -1; +} + + +static int +connection_write_cq_ssl (server *srv, connection *con, + chunkqueue *cq, off_t max_bytes) +{ + handler_ctx *hctx = con->plugin_ctx[plugin_data_singleton->id]; + SSL *ssl = hctx->ssl; + + chunkqueue_remove_finished_chunks(cq); + + while (max_bytes > 0 && NULL != cq->first) { + const char *data; + size_t data_len; + int r; + + if (0 != load_next_chunk(srv,cq,max_bytes,&data,&data_len)) return -1; + + /** + * SSL_write man-page + * + * WARNING + * When an SSL_write() operation has to be repeated because of + * SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be + * repeated with the same arguments. + */ + + ERR_clear_error(); + r = SSL_write(ssl, data, data_len); + + if (hctx->renegotiations > 1 + && hctx->conf.ssl_disable_client_renegotiation) { + log_error_write(srv, __FILE__, __LINE__, "s", + "SSL: renegotiation initiated by client, killing connection"); + return -1; + } + + if (r <= 0) { + int ssl_r; + unsigned long err; + + switch ((ssl_r = SSL_get_error(ssl, r))) { + case SSL_ERROR_WANT_READ: + con->is_readable = -1; + return 0; /* try again later */ + case SSL_ERROR_WANT_WRITE: + con->is_writable = -1; + return 0; /* try again later */ + case SSL_ERROR_SYSCALL: + /* perhaps we have error waiting in our error-queue */ + if (0 != (err = ERR_get_error())) { + do { + log_error_write(srv, __FILE__, __LINE__, "sdds", + "SSL:", ssl_r, r, + ERR_error_string(err, NULL)); + } while((err = ERR_get_error())); + } else if (r == -1) { + /* no, but we have errno */ + switch(errno) { + case EPIPE: + case ECONNRESET: + return -2; + default: + log_error_write(srv, __FILE__, __LINE__, "sddds", + "SSL:", ssl_r, r, errno, + strerror(errno)); + break; + } + } else { + /* neither error-queue nor errno ? */ + log_error_write(srv, __FILE__, __LINE__, "sddds", + "SSL (error):", ssl_r, r, errno, + strerror(errno)); + } + break; + + case SSL_ERROR_ZERO_RETURN: + /* clean shutdown on the remote side */ + + if (r == 0) return -2; + + /* fall through */ + default: + while((err = ERR_get_error())) { + log_error_write(srv, __FILE__, __LINE__, "sdds", + "SSL:", ssl_r, r, + ERR_error_string(err, NULL)); + } + break; + } + return -1; + } + + chunkqueue_mark_written(cq, r); + max_bytes -= r; + + if ((size_t) r < data_len) break; /* try again later */ + } + + return 0; +} + + +static int +connection_read_cq_ssl (server *srv, connection *con, + chunkqueue *cq, off_t max_bytes) +{ + handler_ctx *hctx = con->plugin_ctx[plugin_data_singleton->id]; + int r, ssl_err, len; + char *mem = NULL; + size_t mem_len = 0; + + /*(code transform assumption; minimize diff)*/ + force_assert(cq == con->read_queue); + UNUSED(max_bytes); + + ERR_clear_error(); + do { + len = SSL_pending(hctx->ssl); + mem_len = len < 2048 ? 2048 : (size_t)len; + mem = chunkqueue_get_memory(con->read_queue, &mem_len); +#if 0 + /* overwrite everything with 0 */ + memset(mem, 0, mem_len); +#endif + + len = SSL_read(hctx->ssl, mem, mem_len); + if (len > 0) { + chunkqueue_use_memory(con->read_queue, len); + con->bytes_read += len; + } else { + chunkqueue_use_memory(con->read_queue, 0); + } + + if (hctx->renegotiations > 1 + && hctx->conf.ssl_disable_client_renegotiation) { + log_error_write(srv, __FILE__, __LINE__, "s", + "SSL: renegotiation initiated by client, killing connection"); + return -1; + } + + #if OPENSSL_VERSION_NUMBER >= 0x10002000 + if (hctx->alpn) { + if (hctx->alpn == MOD_OPENSSL_ALPN_ACME_TLS_1) { + chunkqueue_reset(con->read_queue); + /* initiate handshake in order to send ServerHello. + * Once TLS handshake is complete, return -1 to result in + * CON_STATE_ERROR so that socket connection is quickly closed*/ + if (1 == SSL_do_handshake(hctx->ssl)) return -1; + len = -1; + break; + } + hctx->alpn = 0; + } + #endif + } while (len > 0 + && (hctx->conf.ssl_read_ahead || SSL_pending(hctx->ssl) > 0)); + + if (len < 0) { + int oerrno = errno; + switch ((r = SSL_get_error(hctx->ssl, len))) { + case SSL_ERROR_WANT_WRITE: + con->is_writable = -1; + /* fall through */ + case SSL_ERROR_WANT_READ: + con->is_readable = 0; + + /* the manual says we have to call SSL_read with the same arguments + * next time. we ignore this restriction; no one has complained + * about it in 1.5 yet, so it probably works anyway. + */ + + return 0; + case SSL_ERROR_SYSCALL: + /** + * man SSL_get_error() + * + * SSL_ERROR_SYSCALL + * Some I/O error occurred. The OpenSSL error queue may contain + * more information on the error. If the error queue is empty + * (i.e. ERR_get_error() returns 0), ret can be used to find out + * more about the error: If ret == 0, an EOF was observed that + * violates the protocol. If ret == -1, the underlying BIO + * reported an I/O error (for socket I/O on Unix systems, consult + * errno for details). + * + */ + while((ssl_err = ERR_get_error())) { + /* get all errors from the error-queue */ + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + r, ERR_error_string(ssl_err, NULL)); + } + + switch(oerrno) { + default: + /* (oerrno should be something like ECONNABORTED not 0 + * if client disconnected before anything was sent + * (e.g. TCP connection probe), but it does not appear + * that openssl provides such notification, not even + * something like SSL_R_SSL_HANDSHAKE_FAILURE) */ + if (0==oerrno && 0==cq->bytes_in && !hctx->conf.ssl_log_noise) + break; + + log_error_write(srv, __FILE__, __LINE__, "sddds", "SSL:", + len, r, oerrno, + strerror(oerrno)); + break; + } + + break; + case SSL_ERROR_ZERO_RETURN: + /* clean shutdown on the remote side */ + + if (r == 0) { + /* FIXME: later */ + } + + /* fall through */ + default: + while((ssl_err = ERR_get_error())) { + switch (ERR_GET_REASON(ssl_err)) { + case SSL_R_SSL_HANDSHAKE_FAILURE: + #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_CA + case SSL_R_TLSV1_ALERT_UNKNOWN_CA: + #endif + #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN + case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN: + #endif + #ifdef SSL_R_SSLV3_ALERT_BAD_CERTIFICATE + case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE: + #endif + if (!hctx->conf.ssl_log_noise) continue; + break; + default: + break; + } + /* get all errors from the error-queue */ + log_error_write(srv, __FILE__, __LINE__, "sds", "SSL:", + r, ERR_error_string(ssl_err, NULL)); + } + break; + } + return -1; + } else if (len == 0) { + con->is_readable = 0; + /* the other end close the connection -> KEEP-ALIVE */ + + return -2; + } else { + return 0; + } +} + + +CONNECTION_FUNC(mod_openssl_handle_con_accept) +{ + plugin_data *p = p_d; + handler_ctx *hctx; + server_socket *srv_sock = con->srv_socket; + if (!srv_sock->is_ssl) return HANDLER_GO_ON; + + hctx = handler_ctx_init(); + hctx->con = con; + hctx->srv = srv; + con->plugin_ctx[p->id] = hctx; + mod_openssl_patch_connection(srv, con, hctx); + + /* connect fd to SSL */ + hctx->ssl = SSL_new(p->config_storage[srv_sock->sidx]->ssl_ctx); + if (NULL == hctx->ssl) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return HANDLER_ERROR; + } + + buffer_copy_string_len(con->proto, CONST_STR_LEN("https")); + con->network_read = connection_read_cq_ssl; + con->network_write = connection_write_cq_ssl; + SSL_set_app_data(hctx->ssl, hctx); + SSL_set_accept_state(hctx->ssl); + + if (1 != (SSL_set_fd(hctx->ssl, con->fd))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", + ERR_error_string(ERR_get_error(), NULL)); + return HANDLER_ERROR; + } + + return HANDLER_GO_ON; +} + + +static void +mod_openssl_close_notify(server *srv, handler_ctx *hctx); + + +CONNECTION_FUNC(mod_openssl_handle_con_shut_wr) +{ + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (NULL == hctx) return HANDLER_GO_ON; + + if (SSL_is_init_finished(hctx->ssl)) { + mod_openssl_close_notify(srv, hctx); + } + + return HANDLER_GO_ON; +} + + +static void +mod_openssl_close_notify(server *srv, handler_ctx *hctx) +{ + int ret, ssl_r; + unsigned long err; + ERR_clear_error(); + switch ((ret = SSL_shutdown(hctx->ssl))) { + case 1: + /* ok */ + break; + case 0: + /* wait for fd-event + * + * FIXME: wait for fdevent and call SSL_shutdown again + * + */ + + /* Drain SSL read buffers in case pending records need processing. + * Limit to reading 16k to avoid denial of service when the CPU + * processing TLS is slower than arrival speed of TLS data packets. + * + * references: + * + * "New session ticket breaks bidirectional shutdown of TLS 1.3 connection" + * https://github.com/openssl/openssl/issues/6262 + * + * The peer is still allowed to send data after receiving the + * "close notify" event. If the peer did send data it need to be + * processed by calling SSL_read() before calling SSL_shutdown() a + * second time. SSL_read() will indicate the end of the peer data by + * returning <= 0 and SSL_get_error() returning + * SSL_ERROR_ZERO_RETURN. It is recommended to call SSL_read() + * between SSL_shutdown() calls. + * + * Additional discussion in "Auto retry in shutdown" + * https://github.com/openssl/openssl/pull/6340 + */ + err = 0; + do { + char buf[4096]; + ret = SSL_read(hctx->ssl, buf, (int)sizeof(buf)); + } while (ret > 0 && (err += (unsigned long)ret) < 16384); + + ERR_clear_error(); + if (-1 != (ret = SSL_shutdown(hctx->ssl))) break; + + /* fall through */ + default: + + switch ((ssl_r = SSL_get_error(hctx->ssl, ret))) { + case SSL_ERROR_ZERO_RETURN: + break; + case SSL_ERROR_WANT_WRITE: + /*con->is_writable=-1;*//*(no effect; shutdown() called below)*/ + case SSL_ERROR_WANT_READ: + break; + case SSL_ERROR_SYSCALL: + /* perhaps we have error waiting in our error-queue */ + if (0 != (err = ERR_get_error())) { + do { + log_error_write(srv, __FILE__, __LINE__, "sdds", + "SSL:", ssl_r, ret, + ERR_error_string(err, NULL)); + } while((err = ERR_get_error())); + } else if (errno != 0) { + /*ssl bug (see lighttpd ticket #2213): sometimes errno==0*/ + switch(errno) { + case EPIPE: + case ECONNRESET: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sddds", + "SSL (error):", ssl_r, ret, errno, + strerror(errno)); + break; + } + } + + break; + default: + while((err = ERR_get_error())) { + log_error_write(srv, __FILE__, __LINE__, "sdds", + "SSL:", ssl_r, ret, + ERR_error_string(err, NULL)); + } + + break; + } + } + ERR_clear_error(); +} + + +CONNECTION_FUNC(mod_openssl_handle_con_close) +{ + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (NULL != hctx) { + handler_ctx_free(hctx); + con->plugin_ctx[p->id] = NULL; + } + + UNUSED(srv); + return HANDLER_GO_ON; +} + + +static void +https_add_ssl_client_entries (server *srv, connection *con, handler_ctx *hctx) +{ + X509 *xs; + X509_NAME *xn; + int i, nentries; + + long vr = SSL_get_verify_result(hctx->ssl); + if (vr != X509_V_OK) { + char errstr[256]; + ERR_error_string_n(vr, errstr, sizeof(errstr)); + buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("FAILED:")); + buffer_append_string(srv->tmp_buf, errstr); + http_header_env_set(con, + CONST_STR_LEN("SSL_CLIENT_VERIFY"), + CONST_BUF_LEN(srv->tmp_buf)); + return; + } else if (!(xs = SSL_get_peer_certificate(hctx->ssl))) { + http_header_env_set(con, + CONST_STR_LEN("SSL_CLIENT_VERIFY"), + CONST_STR_LEN("NONE")); + return; + } else { + http_header_env_set(con, + CONST_STR_LEN("SSL_CLIENT_VERIFY"), + CONST_STR_LEN("SUCCESS")); + } + + xn = X509_get_subject_name(xs); + { + char buf[256]; + int len = safer_X509_NAME_oneline(xn, buf, sizeof(buf)); + if (len > 0) { + if (len >= (int)sizeof(buf)) len = (int)sizeof(buf)-1; + http_header_env_set(con, + CONST_STR_LEN("SSL_CLIENT_S_DN"), + buf, (size_t)len); + } + } + buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("SSL_CLIENT_S_DN_")); + for (i = 0, nentries = X509_NAME_entry_count(xn); i < nentries; ++i) { + int xobjnid; + const char * xobjsn; + X509_NAME_ENTRY *xe; + + if (!(xe = X509_NAME_get_entry(xn, i))) { + continue; + } + xobjnid = OBJ_obj2nid((ASN1_OBJECT*)X509_NAME_ENTRY_get_object(xe)); + xobjsn = OBJ_nid2sn(xobjnid); + if (xobjsn) { + buffer_string_set_length(srv->tmp_buf,sizeof("SSL_CLIENT_S_DN_")-1); + buffer_append_string(srv->tmp_buf, xobjsn); + http_header_env_set(con, + CONST_BUF_LEN(srv->tmp_buf), + (const char*)X509_NAME_ENTRY_get_data(xe)->data, + X509_NAME_ENTRY_get_data(xe)->length); + } + } + + { + ASN1_INTEGER *xsn = X509_get_serialNumber(xs); + BIGNUM *serialBN = ASN1_INTEGER_to_BN(xsn, NULL); + char *serialHex = BN_bn2hex(serialBN); + http_header_env_set(con, + CONST_STR_LEN("SSL_CLIENT_M_SERIAL"), + serialHex, strlen(serialHex)); + OPENSSL_free(serialHex); + BN_free(serialBN); + } + + if (!buffer_string_is_empty(hctx->conf.ssl_verifyclient_username)) { + /* pick one of the exported values as "REMOTE_USER", for example + * ssl.verifyclient.username = "SSL_CLIENT_S_DN_UID" + * or + * ssl.verifyclient.username = "SSL_CLIENT_S_DN_emailAddress" + */ + buffer *varname = hctx->conf.ssl_verifyclient_username; + buffer *vb = http_header_env_get(con, CONST_BUF_LEN(varname)); + if (vb) { /* same as http_auth.c:http_auth_setenv() */ + http_header_env_set(con, + CONST_STR_LEN("REMOTE_USER"), + CONST_BUF_LEN(vb)); + http_header_env_set(con, + CONST_STR_LEN("AUTH_TYPE"), + CONST_STR_LEN("SSL_CLIENT_VERIFY")); + } + } + + if (hctx->conf.ssl_verifyclient_export_cert) { + BIO *bio; + if (NULL != (bio = BIO_new(BIO_s_mem()))) { + buffer *cert = srv->tmp_buf; + int n; + + PEM_write_bio_X509(bio, xs); + n = BIO_pending(bio); + + buffer_string_prepare_copy(cert, n); + BIO_read(bio, cert->ptr, n); + BIO_free(bio); + buffer_commit(cert, n); + http_header_env_set(con, + CONST_STR_LEN("SSL_CLIENT_CERT"), + CONST_BUF_LEN(cert)); + } + } + X509_free(xs); +} + + +static void +http_cgi_ssl_env (server *srv, connection *con, handler_ctx *hctx) +{ + const char *s; + const SSL_CIPHER *cipher; + UNUSED(srv); + + s = SSL_get_version(hctx->ssl); + http_header_env_set(con, + CONST_STR_LEN("SSL_PROTOCOL"), + s, strlen(s)); + + if ((cipher = SSL_get_current_cipher(hctx->ssl))) { + int usekeysize, algkeysize; + char buf[LI_ITOSTRING_LENGTH]; + s = SSL_CIPHER_get_name(cipher); + http_header_env_set(con, + CONST_STR_LEN("SSL_CIPHER"), + s, strlen(s)); + usekeysize = SSL_CIPHER_get_bits(cipher, &algkeysize); + li_itostrn(buf, sizeof(buf), usekeysize); + http_header_env_set(con, + CONST_STR_LEN("SSL_CIPHER_USEKEYSIZE"), + buf, strlen(buf)); + li_itostrn(buf, sizeof(buf), algkeysize); + http_header_env_set(con, + CONST_STR_LEN("SSL_CIPHER_ALGKEYSIZE"), + buf, strlen(buf)); + } +} + + +CONNECTION_FUNC(mod_openssl_handle_request_env) +{ + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (NULL == hctx) return HANDLER_GO_ON; + if (hctx->request_env_patched) return HANDLER_GO_ON; + hctx->request_env_patched = 1; + + http_cgi_ssl_env(srv, con, hctx); + if (hctx->conf.ssl_verifyclient) { + https_add_ssl_client_entries(srv, con, hctx); + } + + return HANDLER_GO_ON; +} + + +CONNECTION_FUNC(mod_openssl_handle_uri_raw) +{ + /* mod_openssl must be loaded prior to mod_auth + * if mod_openssl is configured to set REMOTE_USER based on client cert */ + /* mod_openssl must be loaded after mod_extforward + * if mod_openssl config is based on lighttpd.conf remote IP conditional + * using remote IP address set by mod_extforward, *unless* PROXY protocol + * is enabled with extforward.hap-PROXY = "enable", in which case the + * reverse is true: mod_extforward must be loaded after mod_openssl */ + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (NULL == hctx) return HANDLER_GO_ON; + + mod_openssl_patch_connection(srv, con, hctx); + if (hctx->conf.ssl_verifyclient) { + mod_openssl_handle_request_env(srv, con, p); + } + + return HANDLER_GO_ON; +} + + +CONNECTION_FUNC(mod_openssl_handle_request_reset) +{ + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (NULL == hctx) return HANDLER_GO_ON; + + hctx->request_env_patched = 0; + + UNUSED(srv); + return HANDLER_GO_ON; +} + + +int mod_openssl_plugin_init (plugin *p); +int mod_openssl_plugin_init (plugin *p) +{ + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("openssl"); + p->init = mod_openssl_init; + p->cleanup = mod_openssl_free; + p->priv_defaults= mod_openssl_set_defaults; + + p->handle_connection_accept = mod_openssl_handle_con_accept; + p->handle_connection_shut_wr = mod_openssl_handle_con_shut_wr; + p->handle_connection_close = mod_openssl_handle_con_close; + p->handle_uri_raw = mod_openssl_handle_uri_raw; + p->handle_request_env = mod_openssl_handle_request_env; + p->connection_reset = mod_openssl_handle_request_reset; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_proxy.c b/data/lighttpd/lighttpd-1.4.53/src/mod_proxy.c new file mode 100644 index 000000000..91f9c2f3c --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_proxy.c @@ -0,0 +1,1038 @@ +#include "first.h" + +#include <string.h> +#include <stdlib.h> + +#include "gw_backend.h" +#include "base.h" +#include "array.h" +#include "buffer.h" +#include "http_kv.h" +#include "http_header.h" +#include "log.h" +#include "sock_addr.h" +#include "status_counter.h" + +/** + * + * HTTP reverse proxy + * + * TODO: - HTTP/1.1 + * - HTTP/1.1 persistent connection with upstream servers + */ + +/* (future: might split struct and move part to http-header-glue.c) */ +typedef struct http_header_remap_opts { + const array *urlpaths; + const array *hosts_request; + const array *hosts_response; + int https_remap; + int upgrade; + int connect_method; + /*(not used in plugin_config, but used in handler_ctx)*/ + const buffer *http_host; + const buffer *forwarded_host; + const data_string *forwarded_urlpath; +} http_header_remap_opts; + +typedef enum { + PROXY_FORWARDED_NONE = 0x00, + PROXY_FORWARDED_FOR = 0x01, + PROXY_FORWARDED_PROTO = 0x02, + PROXY_FORWARDED_HOST = 0x04, + PROXY_FORWARDED_BY = 0x08, + PROXY_FORWARDED_REMOTE_USER = 0x10 +} proxy_forwarded_t; + +typedef struct { + gw_plugin_config gw; + array *forwarded_params; + array *header_params; + unsigned short replace_http_host; + unsigned int forwarded; + + http_header_remap_opts header; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +static int proxy_check_extforward; + +typedef struct { + gw_handler_ctx gw; + http_response_opts opts; + plugin_config conf; +} handler_ctx; + + +INIT_FUNC(mod_proxy_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + return p; +} + + +FREE_FUNC(mod_proxy_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->forwarded_params); + array_free(s->header_params); + + /*assert(0 == offsetof(s->gw));*/ + gw_plugin_config_free(&s->gw); + /*free(s);*//*free'd by gw_plugin_config_free()*/ + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_proxy_set_defaults) { + plugin_data *p = p_d; + data_unset *du; + size_t i = 0; + + config_values_t cv[] = { + { "proxy.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "proxy.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "proxy.balance", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "proxy.replace-http-host", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "proxy.forwarded", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "proxy.header", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { "proxy.map-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->gw.debug = 0; + s->replace_http_host = 0; + s->forwarded_params = array_init(); + s->forwarded = PROXY_FORWARDED_NONE; + s->header_params = array_init(); + s->gw.ext_mapping = array_init(); + + cv[0].destination = NULL; /* T_CONFIG_LOCAL */ + cv[1].destination = &(s->gw.debug); + cv[2].destination = NULL; /* T_CONFIG_LOCAL */ + cv[3].destination = &(s->replace_http_host); + cv[4].destination = s->forwarded_params; + cv[5].destination = s->header_params; + cv[6].destination = s->gw.ext_mapping; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + du = array_get_element(config->value, "proxy.server"); + if (!gw_set_defaults_backend(srv, (gw_plugin_data *)p, du, i, 0)) { + return HANDLER_ERROR; + } + + du = array_get_element(config->value, "proxy.balance"); + if (!gw_set_defaults_balance(srv, &s->gw, du)) { + return HANDLER_ERROR; + } + + /* disable check-local for all exts (default enabled) */ + if (s->gw.exts) { /*(check after gw_set_defaults_backend())*/ + for (size_t j = 0; j < s->gw.exts->used; ++j) { + gw_extension *ex = s->gw.exts->exts[j]; + for (size_t n = 0; n < ex->used; ++n) { + ex->hosts[n]->check_local = 0; + } + } + } + + if (!array_is_kvany(s->forwarded_params)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for proxy.forwarded; expected ( \"param\" => \"value\" )"); + return HANDLER_ERROR; + } + for (size_t j = 0, used = s->forwarded_params->used; j < used; ++j) { + proxy_forwarded_t param; + du = s->forwarded_params->data[j]; + if (buffer_is_equal_string(du->key, CONST_STR_LEN("by"))) { + param = PROXY_FORWARDED_BY; + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("for"))) { + param = PROXY_FORWARDED_FOR; + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("host"))) { + param = PROXY_FORWARDED_HOST; + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proto"))) { + param = PROXY_FORWARDED_PROTO; + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("remote_user"))) { + param = PROXY_FORWARDED_REMOTE_USER; + } else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "proxy.forwarded keys must be one of: by, for, host, proto, remote_user, but not:", du->key); + return HANDLER_ERROR; + } + if (du->type == TYPE_STRING) { + data_string *ds = (data_string *)du; + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("enable"))) { + s->forwarded |= param; + } else if (!buffer_is_equal_string(ds->value, CONST_STR_LEN("disable"))) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "proxy.forwarded values must be one of: 0, 1, enable, disable; error for key:", du->key); + return HANDLER_ERROR; + } + } else if (du->type == TYPE_INTEGER) { + data_integer *di = (data_integer *)du; + if (di->value) s->forwarded |= param; + } else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "proxy.forwarded values must be one of: 0, 1, enable, disable; error for key:", du->key); + return HANDLER_ERROR; + } + } + + if (!array_is_kvany(s->header_params)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for proxy.header; expected ( \"param\" => ( \"key\" => \"value\" ) )"); + return HANDLER_ERROR; + } + for (size_t j = 0, used = s->header_params->used; j < used; ++j) { + data_array *da = (data_array *)s->header_params->data[j]; + if (buffer_is_equal_string(da->key, CONST_STR_LEN("https-remap"))) { + data_string *ds = (data_string *)da; + if (ds->type != TYPE_STRING) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for proxy.header; expected \"enable\" or \"disable\" for https-remap"); + return HANDLER_ERROR; + } + s->header.https_remap = !buffer_is_equal_string(ds->value, CONST_STR_LEN("disable")) + && !buffer_is_equal_string(ds->value, CONST_STR_LEN("0")); + continue; + } + else if (buffer_is_equal_string(da->key, CONST_STR_LEN("upgrade"))) { + data_string *ds = (data_string *)da; + if (ds->type != TYPE_STRING) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for proxy.header; expected \"upgrade\" => \"enable\" or \"disable\""); + return HANDLER_ERROR; + } + s->header.upgrade = !buffer_is_equal_string(ds->value, CONST_STR_LEN("disable")) + && !buffer_is_equal_string(ds->value, CONST_STR_LEN("0")); + continue; + } + else if (buffer_is_equal_string(da->key, CONST_STR_LEN("connect"))) { + data_string *ds = (data_string *)da; + if (ds->type != TYPE_STRING) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for proxy.header; expected \"connect\" => \"enable\" or \"disable\""); + return HANDLER_ERROR; + } + s->header.connect_method = !buffer_is_equal_string(ds->value, CONST_STR_LEN("disable")) + && !buffer_is_equal_string(ds->value, CONST_STR_LEN("0")); + continue; + } + if (da->type != TYPE_ARRAY || !array_is_kvstring(da->value)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "unexpected value for proxy.header; expected ( \"param\" => ( \"key\" => \"value\" ) ) near key", da->key); + return HANDLER_ERROR; + } + if (buffer_is_equal_string(da->key, CONST_STR_LEN("map-urlpath"))) { + s->header.urlpaths = da->value; + } + else if (buffer_is_equal_string(da->key, CONST_STR_LEN("map-host-request"))) { + s->header.hosts_request = da->value; + } + else if (buffer_is_equal_string(da->key, CONST_STR_LEN("map-host-response"))) { + s->header.hosts_response = da->value; + } + else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "unexpected key for proxy.header; expected ( \"param\" => ( \"key\" => \"value\" ) ) near key", da->key); + return HANDLER_ERROR; + } + } + } + + for (i = 0; i < srv->srvconf.modules->used; i++) { + data_string *ds = (data_string *)srv->srvconf.modules->data[i]; + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("mod_extforward"))) { + proxy_check_extforward = 1; + break; + } + } + + return HANDLER_GO_ON; +} + + +/* (future: might move to http-header-glue.c) */ +static const buffer * http_header_remap_host_match (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req, size_t alen) +{ + const array *hosts = is_req + ? remap_hdrs->hosts_request + : remap_hdrs->hosts_response; + if (hosts) { + const char * const s = b->ptr+off; + for (size_t i = 0, used = hosts->used; i < used; ++i) { + const data_string * const ds = (data_string *)hosts->data[i]; + const buffer *k = ds->key; + size_t mlen = buffer_string_length(k); + if (1 == mlen && k->ptr[0] == '-') { + /* match with authority provided in Host (if is_req) + * (If no Host in client request, then matching against empty + * string will probably not match, and no remap will be + * performed) */ + k = is_req + ? remap_hdrs->http_host + : remap_hdrs->forwarded_host; + if (NULL == k) continue; + mlen = buffer_string_length(k); + } + if (mlen == alen && 0 == strncasecmp(s, k->ptr, alen)) { + if (buffer_is_equal_string(ds->value, CONST_STR_LEN("-"))) { + return remap_hdrs->http_host; + } + else if (!buffer_string_is_empty(ds->value)) { + /*(save first matched request host for response match)*/ + if (is_req && NULL == remap_hdrs->forwarded_host) + remap_hdrs->forwarded_host = ds->value; + return ds->value; + } /*(else leave authority as-is and stop matching)*/ + break; + } + } + } + return NULL; +} + + +/* (future: might move to http-header-glue.c) */ +static size_t http_header_remap_host (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req, size_t alen) +{ + const buffer * const m = + http_header_remap_host_match(b, off, remap_hdrs, is_req, alen); + if (NULL == m) return alen; /*(no match; return original authority length)*/ + + buffer_substr_replace(b, off, alen, m); + return buffer_string_length(m); /*(length of replacement authority)*/ +} + + +/* (future: might move to http-header-glue.c) */ +static size_t http_header_remap_urlpath (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req) +{ + const array *urlpaths = remap_hdrs->urlpaths; + if (urlpaths) { + const char * const s = b->ptr+off; + const size_t plen = buffer_string_length(b) - off; /*(urlpath len)*/ + if (is_req) { /* request */ + for (size_t i = 0, used = urlpaths->used; i < used; ++i) { + const data_string * const ds = (data_string *)urlpaths->data[i]; + const size_t mlen = buffer_string_length(ds->key); + if (mlen <= plen && 0 == memcmp(s, ds->key->ptr, mlen)) { + if (NULL == remap_hdrs->forwarded_urlpath) + remap_hdrs->forwarded_urlpath = ds; + buffer_substr_replace(b, off, mlen, ds->value); + return buffer_string_length(ds->value);/*(replacement len)*/ + } + } + } + else { /* response; perform reverse map */ + if (NULL != remap_hdrs->forwarded_urlpath) { + const data_string * const ds = remap_hdrs->forwarded_urlpath; + const size_t mlen = buffer_string_length(ds->value); + if (mlen <= plen && 0 == memcmp(s, ds->value->ptr, mlen)) { + buffer_substr_replace(b, off, mlen, ds->key); + return buffer_string_length(ds->key); /*(replacement len)*/ + } + } + for (size_t i = 0, used = urlpaths->used; i < used; ++i) { + const data_string * const ds = (data_string *)urlpaths->data[i]; + const size_t mlen = buffer_string_length(ds->value); + if (mlen <= plen && 0 == memcmp(s, ds->value->ptr, mlen)) { + buffer_substr_replace(b, off, mlen, ds->key); + return buffer_string_length(ds->key); /*(replacement len)*/ + } + } + } + } + return 0; +} + + +/* (future: might move to http-header-glue.c) */ +static void http_header_remap_uri (buffer *b, size_t off, http_header_remap_opts *remap_hdrs, int is_req) +{ + /* find beginning of URL-path (might be preceded by scheme://authority + * (caller should make sure any leading whitespace is prior to offset) */ + if (b->ptr[off] != '/') { + char *s = b->ptr+off; + size_t alen; /*(authority len (host len))*/ + size_t slen; /*(scheme len)*/ + const buffer *m; + /* skip over scheme and authority of URI to find beginning of URL-path + * (value might conceivably be relative URL-path instead of URI) */ + if (NULL == (s = strchr(s, ':')) || s[1] != '/' || s[2] != '/') return; + slen = s - (b->ptr+off); + s += 3; + off = (size_t)(s - b->ptr); + if (NULL != (s = strchr(s, '/'))) { + alen = (size_t)(s - b->ptr) - off; + if (0 == alen) return; /*(empty authority, e.g. "http:///")*/ + } + else { + alen = buffer_string_length(b) - off; + if (0 == alen) return; /*(empty authority, e.g. "http:///")*/ + buffer_append_string_len(b, CONST_STR_LEN("/")); + } + + /* remap authority (if configured) and set offset to url-path */ + m = http_header_remap_host_match(b, off, remap_hdrs, is_req, alen); + if (NULL != m) { + if (remap_hdrs->https_remap + && (is_req ? 5==slen && 0==memcmp(b->ptr+off-slen-3,"https",5) + : 4==slen && 0==memcmp(b->ptr+off-slen-3,"http",4))){ + if (is_req) { + memcpy(b->ptr+off-slen-3+4,"://",3); /*("https"=>"http")*/ + --off; + ++alen; + } + else {/*(!is_req)*/ + memcpy(b->ptr+off-slen-3+4,"s://",4); /*("http" =>"https")*/ + ++off; + --alen; + } + } + buffer_substr_replace(b, off, alen, m); + alen = buffer_string_length(m);/*(length of replacement authority)*/ + } + off += alen; + } + + /* remap URLs (if configured) */ + http_header_remap_urlpath(b, off, remap_hdrs, is_req); +} + + +/* (future: might move to http-header-glue.c) */ +static void http_header_remap_setcookie (buffer *b, size_t off, http_header_remap_opts *remap_hdrs) +{ + /* Given the special-case of Set-Cookie and the (too) loosely restricted + * characters allowed, for best results, the Set-Cookie value should be the + * entire string in b from offset to end of string. In response headers, + * lighttpd may concatenate multiple Set-Cookie headers into single entry + * in con->response.headers, separated by "\r\nSet-Cookie: " */ + for (char *s = b->ptr+off, *e; *s; s = e) { + size_t len; + { + while (*s != ';' && *s != '\n' && *s != '\0') ++s; + if (*s == '\n') { + /*(include +1 for '\n', but leave ' ' for ++s below)*/ + s += sizeof("Set-Cookie:"); + } + if ('\0' == *s) return; + do { ++s; } while (*s == ' ' || *s == '\t'); + if ('\0' == *s) return; + e = s+1; + if ('=' == *s) continue; + /*(interested only in Domain and Path attributes)*/ + while (*e != '=' && *e != '\0') ++e; + if ('\0' == *e) return; + ++e; + switch ((int)(e - s - 1)) { + case 4: + if (0 == strncasecmp(s, "path", 4)) { + if (*e == '"') ++e; + if (*e != '/') continue; + off = (size_t)(e - b->ptr); + len = http_header_remap_urlpath(b, off, remap_hdrs, 0); + e = b->ptr+off+len; /*(b may have been reallocated)*/ + continue; + } + break; + case 6: + if (0 == strncasecmp(s, "domain", 6)) { + size_t alen = 0; + if (*e == '"') ++e; + if (*e == '.') ++e; + if (*e == ';') continue; + off = (size_t)(e - b->ptr); + for (char c; (c = e[alen]) != ';' && c != ' ' && c != '\t' + && c != '\r' && c != '\0'; ++alen); + len = http_header_remap_host(b, off, remap_hdrs, 0, alen); + e = b->ptr+off+len; /*(b may have been reallocated)*/ + continue; + } + break; + default: + break; + } + } + } +} + + +static void buffer_append_string_backslash_escaped(buffer *b, const char *s, size_t len) { + /* (future: might move to buffer.c) */ + size_t j = 0; + char *p; + + buffer_string_prepare_append(b, len*2 + 4); + p = b->ptr + buffer_string_length(b); + + for (size_t i = 0; i < len; ++i) { + int c = s[i]; + if (c == '"' || c == '\\' || c == 0x7F || (c < 0x20 && c != '\t')) + p[j++] = '\\'; + p[j++] = c; + } + + buffer_commit(b, j); +} + +static void proxy_set_Forwarded(connection *con, const unsigned int flags) { + buffer *b = NULL, *efor = NULL, *eproto = NULL, *ehost = NULL; + int semicolon = 0; + + if (proxy_check_extforward) { + efor = + http_header_env_get(con, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_FOR")); + eproto = + http_header_env_get(con, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_PROTO")); + ehost = + http_header_env_get(con, CONST_STR_LEN("_L_EXTFORWARD_ACTUAL_HOST")); + } + + /* note: set "Forwarded" prior to updating X-Forwarded-For (below) */ + + if (flags) + b = http_header_request_get(con, HTTP_HEADER_FORWARDED, CONST_STR_LEN("Forwarded")); + + if (flags && NULL == b) { + buffer *xff = + http_header_request_get(con, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For")); + http_header_request_set(con, HTTP_HEADER_FORWARDED, + CONST_STR_LEN("Forwarded"), + CONST_STR_LEN("x")); /*(must not be blank for _get below)*/ + #ifdef __COVERITY__ + force_assert(NULL != b); /*(not NULL because created directly above)*/ + #endif + b = http_header_request_get(con, HTTP_HEADER_FORWARDED, CONST_STR_LEN("Forwarded")); + buffer_clear(b); + if (NULL != xff) { + /* use X-Forwarded-For contents to seed Forwarded */ + char *s = xff->ptr; + size_t used = buffer_string_length(xff); + for (size_t i=0, j, ipv6; i < used; ++i) { + while (s[i] == ' ' || s[i] == '\t' || s[i] == ',') ++i; + if (s[i] == '\0') break; + j = i; + do { + ++i; + } while (s[i]!=' ' && s[i]!='\t' && s[i]!=',' && s[i]!='\0'); + buffer_append_string_len(b, CONST_STR_LEN("for=")); + /* over-simplified test expecting only IPv4 or IPv6 addresses, + * (not expecting :port, so treat existence of colon as IPv6, + * and not expecting unix paths, especially not containing ':') + * quote all strings, backslash-escape since IPs not validated*/ + ipv6 = (NULL != memchr(s+j, ':', i-j)); /*(over-simplified) */ + buffer_append_string_len(b, CONST_STR_LEN("\"")); + if (ipv6) + buffer_append_string_len(b, CONST_STR_LEN("[")); + buffer_append_string_backslash_escaped(b, s+j, i-j); + if (ipv6) + buffer_append_string_len(b, CONST_STR_LEN("]")); + buffer_append_string_len(b, CONST_STR_LEN("\"")); + buffer_append_string_len(b, CONST_STR_LEN(", ")); + } + } + } else if (flags) { /*(NULL != b)*/ + buffer_append_string_len(b, CONST_STR_LEN(", ")); + } + + if (flags & PROXY_FORWARDED_FOR) { + int family = sock_addr_get_family(&con->dst_addr); + buffer_append_string_len(b, CONST_STR_LEN("for=")); + if (NULL != efor) { + /* over-simplified test expecting only IPv4 or IPv6 addresses, + * (not expecting :port, so treat existence of colon as IPv6, + * and not expecting unix paths, especially not containing ':') + * quote all strings and backslash-escape since IPs not validated + * (should be IP from original con->dst_addr_buf, + * so trustable and without :port) */ + int ipv6 = (NULL != strchr(efor->ptr, ':')); + buffer_append_string_len(b, CONST_STR_LEN("\"")); + if (ipv6) buffer_append_string_len(b, CONST_STR_LEN("[")); + buffer_append_string_backslash_escaped( + b, CONST_BUF_LEN(efor)); + if (ipv6) buffer_append_string_len(b, CONST_STR_LEN("]")); + buffer_append_string_len(b, CONST_STR_LEN("\"")); + } else if (family == AF_INET) { + /*(Note: if :port is added, then must be quoted-string: + * e.g. for="...:port")*/ + buffer_append_string_buffer(b, con->dst_addr_buf); + } else if (family == AF_INET6) { + buffer_append_string_len(b, CONST_STR_LEN("\"[")); + buffer_append_string_buffer(b, con->dst_addr_buf); + buffer_append_string_len(b, CONST_STR_LEN("]\"")); + } else { + buffer_append_string_len(b, CONST_STR_LEN("\"")); + buffer_append_string_backslash_escaped( + b, CONST_BUF_LEN(con->dst_addr_buf)); + buffer_append_string_len(b, CONST_STR_LEN("\"")); + } + semicolon = 1; + } + + if (flags & PROXY_FORWARDED_BY) { + int family = sock_addr_get_family(&con->srv_socket->addr); + /* Note: getsockname() and inet_ntop() are expensive operations. + * (recommendation: do not to enable by=... unless required) + * future: might use con->srv_socket->srv_token if addr is not + * INADDR_ANY or in6addr_any, but must omit optional :port + * from con->srv_socket->srv_token for consistency */ + + if (semicolon) buffer_append_string_len(b, CONST_STR_LEN(";")); + buffer_append_string_len(b, CONST_STR_LEN("by=")); + buffer_append_string_len(b, CONST_STR_LEN("\"")); + #ifdef HAVE_SYS_UN_H + /* special-case: might need to encode unix domain socket path */ + if (family == AF_UNIX) { + buffer_append_string_backslash_escaped( + b, CONST_BUF_LEN(con->srv_socket->srv_token)); + } + else + #endif + { + sock_addr addr; + socklen_t addrlen = sizeof(addr); + if (0 == getsockname(con->fd,(struct sockaddr *)&addr, &addrlen)) { + sock_addr_stringify_append_buffer(b, &addr); + } + } + buffer_append_string_len(b, CONST_STR_LEN("\"")); + semicolon = 1; + } + + if (flags & PROXY_FORWARDED_PROTO) { + /* expecting "http" or "https" + * (not checking if quoted-string and encoding needed) */ + if (semicolon) buffer_append_string_len(b, CONST_STR_LEN(";")); + buffer_append_string_len(b, CONST_STR_LEN("proto=")); + if (NULL != eproto) { + buffer_append_string_buffer(b, eproto); + } else if (con->srv_socket->is_ssl) { + buffer_append_string_len(b, CONST_STR_LEN("https")); + } else { + buffer_append_string_len(b, CONST_STR_LEN("http")); + } + semicolon = 1; + } + + if (flags & PROXY_FORWARDED_HOST) { + if (NULL != ehost) { + if (semicolon) + buffer_append_string_len(b, CONST_STR_LEN(";")); + buffer_append_string_len(b, CONST_STR_LEN("host=\"")); + buffer_append_string_backslash_escaped( + b, CONST_BUF_LEN(ehost)); + buffer_append_string_len(b, CONST_STR_LEN("\"")); + semicolon = 1; + } else if (!buffer_string_is_empty(con->request.http_host)) { + if (semicolon) + buffer_append_string_len(b, CONST_STR_LEN(";")); + buffer_append_string_len(b, CONST_STR_LEN("host=\"")); + buffer_append_string_backslash_escaped( + b, CONST_BUF_LEN(con->request.http_host)); + buffer_append_string_len(b, CONST_STR_LEN("\"")); + semicolon = 1; + } + } + + if (flags & PROXY_FORWARDED_REMOTE_USER) { + buffer *remote_user = + http_header_env_get(con, CONST_STR_LEN("REMOTE_USER")); + if (NULL != remote_user) { + if (semicolon) + buffer_append_string_len(b, CONST_STR_LEN(";")); + buffer_append_string_len(b, CONST_STR_LEN("remote_user=\"")); + buffer_append_string_backslash_escaped( + b, CONST_BUF_LEN(remote_user)); + buffer_append_string_len(b, CONST_STR_LEN("\"")); + semicolon = 1; + } + } + + /* legacy X-* headers, including X-Forwarded-For */ + + b = (NULL != efor) ? efor : con->dst_addr_buf; + http_header_request_set(con, HTTP_HEADER_X_FORWARDED_FOR, + CONST_STR_LEN("X-Forwarded-For"), + CONST_BUF_LEN(b)); + + b = (NULL != ehost) ? ehost : con->request.http_host; + if (!buffer_string_is_empty(b)) { + http_header_request_set(con, HTTP_HEADER_OTHER, + CONST_STR_LEN("X-Host"), + CONST_BUF_LEN(b)); + http_header_request_set(con, HTTP_HEADER_OTHER, + CONST_STR_LEN("X-Forwarded-Host"), + CONST_BUF_LEN(b)); + } + + b = (NULL != eproto) ? eproto : con->uri.scheme; + http_header_request_set(con, HTTP_HEADER_X_FORWARDED_PROTO, + CONST_STR_LEN("X-Forwarded-Proto"), + CONST_BUF_LEN(b)); +} + + +static handler_t proxy_create_env(server *srv, gw_handler_ctx *gwhctx) { + handler_ctx *hctx = (handler_ctx *)gwhctx; + connection *con = hctx->gw.remote_conn; + const int remap_headers = (NULL != hctx->conf.header.urlpaths + || NULL != hctx->conf.header.hosts_request); + const int upgrade = hctx->conf.header.upgrade + && (NULL != http_header_request_get(con, HTTP_HEADER_UPGRADE, CONST_STR_LEN("Upgrade"))); + size_t rsz = (size_t)(con->read_queue->bytes_out - hctx->gw.wb->bytes_in); + buffer * const b = chunkqueue_prepend_buffer_open_sz(hctx->gw.wb, rsz < 65536 ? rsz : con->header_len); + + /* build header */ + + /* request line */ + http_method_append(b, con->request.http_method); + buffer_append_string_len(b, CONST_STR_LEN(" ")); + buffer_append_string_buffer(b, con->request.uri); + if (remap_headers) + http_header_remap_uri(b, buffer_string_length(b) - buffer_string_length(con->request.uri), &hctx->conf.header, 1); + if (!upgrade) + buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.0\r\n")); + else + buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.1\r\n")); + + if (hctx->conf.replace_http_host && !buffer_string_is_empty(hctx->gw.host->id)) { + if (hctx->gw.conf.debug > 1) { + log_error_write(srv, __FILE__, __LINE__, "SBS", + "proxy - using \"", hctx->gw.host->id, "\" as HTTP Host"); + } + buffer_append_string_len(b, CONST_STR_LEN("Host: ")); + buffer_append_string_buffer(b, hctx->gw.host->id); + buffer_append_string_len(b, CONST_STR_LEN("\r\n")); + } else if (!buffer_string_is_empty(con->request.http_host)) { + buffer_append_string_len(b, CONST_STR_LEN("Host: ")); + buffer_append_string_buffer(b, con->request.http_host); + if (remap_headers) { + size_t alen = buffer_string_length(con->request.http_host); + http_header_remap_host(b, buffer_string_length(b) - alen, &hctx->conf.header, 1, alen); + } + buffer_append_string_len(b, CONST_STR_LEN("\r\n")); + } + + /* "Forwarded" and legacy X- headers */ + proxy_set_Forwarded(con, hctx->conf.forwarded); + + if (HTTP_METHOD_GET != con->request.http_method + && HTTP_METHOD_HEAD != con->request.http_method + && con->request.content_length >= 0) { + /* set Content-Length if client sent Transfer-Encoding: chunked + * and not streaming to backend (request body has been fully received) */ + buffer *vb = http_header_request_get(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); + if (NULL == vb) { + char buf[LI_ITOSTRING_LENGTH]; + li_itostrn(buf, sizeof(buf), con->request.content_length); + http_header_request_set(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length"), buf, strlen(buf)); + } + } + + /* request header */ + for (size_t i = 0, used = con->request.headers->used; i < used; ++i) { + data_string *ds = (data_string *)con->request.headers->data[i]; + const size_t klen = buffer_string_length(ds->key); + size_t vlen; + switch (klen) { + default: + break; + case 4: + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Host"))) continue; /*(handled further above)*/ + break; + case 10: + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Connection"))) continue; + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Set-Cookie"))) continue; /*(response header only; avoid accidental reflection)*/ + break; + case 16: + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy-Connection"))) continue; + break; + case 5: + /* Do not emit HTTP_PROXY in environment. + * Some executables use HTTP_PROXY to configure + * outgoing proxy. See also https://httpoxy.org/ */ + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Proxy"))) continue; + break; + case 0: + continue; + } + + vlen = buffer_string_length(ds->value); + if (0 == vlen) continue; + + if (buffer_string_space(b) < klen + vlen + 4) { + size_t extend = b->size * 2 - buffer_string_length(b); + extend = extend > klen + vlen + 4 ? extend : klen + vlen + 4 + 4095; + buffer_string_prepare_append(b, extend); + } + + buffer_append_string_len(b, ds->key->ptr, klen); + buffer_append_string_len(b, CONST_STR_LEN(": ")); + buffer_append_string_len(b, ds->value->ptr, vlen); + buffer_append_string_len(b, CONST_STR_LEN("\r\n")); + + if (!remap_headers) continue; + + /* check for hdrs for which to remap URIs in-place after append to b */ + + switch (klen) { + default: + continue; + #if 0 /* "URI" is HTTP response header (non-standard; historical in Apache) */ + case 3: + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("URI"))) break; + continue; + #endif + #if 0 /* "Location" is HTTP response header */ + case 8: + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Location"))) break; + continue; + #endif + case 11: /* "Destination" is WebDAV request header */ + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Destination"))) break; + continue; + case 16: /* "Content-Location" may be HTTP request or response header */ + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("Content-Location"))) break; + continue; + } + + http_header_remap_uri(b, buffer_string_length(b) - vlen - 2, &hctx->conf.header, 1); + } + + if (!upgrade) + buffer_append_string_len(b, CONST_STR_LEN("Connection: close\r\n\r\n")); + else + buffer_append_string_len(b, CONST_STR_LEN("Connection: close, upgrade\r\n\r\n")); + + hctx->gw.wb_reqlen = buffer_string_length(b); + chunkqueue_prepend_buffer_commit(hctx->gw.wb); + + if (con->request.content_length) { + chunkqueue_append_chunkqueue(hctx->gw.wb, con->request_content_queue); + if (con->request.content_length > 0) + hctx->gw.wb_reqlen += con->request.content_length; /* total req size */ + else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/ + hctx->gw.wb_reqlen = -hctx->gw.wb_reqlen; + } + + status_counter_inc(srv, CONST_STR_LEN("proxy.requests")); + return HANDLER_GO_ON; +} + + +static handler_t proxy_create_env_connect(server *srv, gw_handler_ctx *gwhctx) { + handler_ctx *hctx = (handler_ctx *)gwhctx; + connection *con = hctx->gw.remote_conn; + con->http_status = 200; /* OK */ + con->file_started = 1; + gw_set_transparent(srv, &hctx->gw); + http_response_upgrade_read_body_unknown(srv, con); + + status_counter_inc(srv, CONST_STR_LEN("proxy.requests")); + return HANDLER_GO_ON; +} + + +#define PATCH(x) \ + p->conf.x = s->x; +#define PATCH_GW(x) \ + p->conf.gw.x = s->gw.x; +static int mod_proxy_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH_GW(exts); + PATCH_GW(exts_auth); + PATCH_GW(exts_resp); + PATCH_GW(debug); + PATCH_GW(ext_mapping); + PATCH_GW(balance); + PATCH(replace_http_host); + PATCH(forwarded); + PATCH(header); /*(copies struct)*/ + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.server"))) { + PATCH_GW(exts); + PATCH_GW(exts_auth); + PATCH_GW(exts_resp); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.debug"))) { + PATCH_GW(debug); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.balance"))) { + PATCH_GW(balance); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.map-extensions"))) { + PATCH_GW(ext_mapping); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.replace-http-host"))) { + PATCH(replace_http_host); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.forwarded"))) { + PATCH(forwarded); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("proxy.header"))) { + PATCH(header); /*(copies struct)*/ + } + } + } + + return 0; +} +#undef PATCH_GW +#undef PATCH + +static handler_t proxy_response_headers(server *srv, connection *con, struct http_response_opts_t *opts) { + /* response headers just completed */ + handler_ctx *hctx = (handler_ctx *)opts->pdata; + + if (con->response.htags & HTTP_HEADER_UPGRADE) { + if (hctx->conf.header.upgrade && con->http_status == 101) { + /* 101 Switching Protocols; transition to transparent proxy */ + gw_set_transparent(srv, &hctx->gw); + http_response_upgrade_read_body_unknown(srv, con); + } + else { + con->response.htags &= ~HTTP_HEADER_UPGRADE; + #if 0 + /* preserve prior questionable behavior; likely broken behavior + * anyway if backend thinks connection is being upgraded but client + * does not receive Connection: upgrade */ + http_header_response_unset(con, HTTP_HEADER_UPGRADE, + CONST_STR_LEN("Upgrade")) + #endif + } + } + + /* rewrite paths, if needed */ + + if (NULL == hctx->conf.header.urlpaths + && NULL == hctx->conf.header.hosts_response) + return HANDLER_GO_ON; + + if (con->response.htags & HTTP_HEADER_LOCATION) { + buffer *vb = http_header_response_get(con, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location")); + if (vb) http_header_remap_uri(vb, 0, &hctx->conf.header, 0); + } + if (con->response.htags & HTTP_HEADER_CONTENT_LOCATION) { + buffer *vb = http_header_response_get(con, HTTP_HEADER_CONTENT_LOCATION, CONST_STR_LEN("Content-Location")); + if (vb) http_header_remap_uri(vb, 0, &hctx->conf.header, 0); + } + if (con->response.htags & HTTP_HEADER_SET_COOKIE) { + buffer *vb = http_header_response_get(con, HTTP_HEADER_SET_COOKIE, CONST_STR_LEN("Set-Cookie")); + if (vb) http_header_remap_setcookie(vb, 0, &hctx->conf.header); + } + + return HANDLER_GO_ON; +} + +static handler_t mod_proxy_check_extension(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + handler_t rc; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + mod_proxy_patch_connection(srv, con, p); + if (NULL == p->conf.gw.exts) return HANDLER_GO_ON; + + rc = gw_check_extension(srv, con, (gw_plugin_data *)p, 1, sizeof(handler_ctx)); + if (HANDLER_GO_ON != rc) return rc; + + if (con->mode == p->id) { + handler_ctx *hctx = con->plugin_ctx[p->id]; + hctx->gw.create_env = proxy_create_env; + hctx->gw.response = chunk_buffer_acquire(); + hctx->gw.opts.backend = BACKEND_PROXY; + hctx->gw.opts.pdata = hctx; + hctx->gw.opts.headers = proxy_response_headers; + + hctx->conf = p->conf; /*(copies struct)*/ + hctx->conf.header.http_host = con->request.http_host; + hctx->conf.header.upgrade &= (con->request.http_version == HTTP_VERSION_1_1); + /* mod_proxy currently sends all backend requests as http. + * https-remap is a flag since it might not be needed if backend + * honors Forwarded or X-Forwarded-Proto headers, e.g. by using + * lighttpd mod_extforward or similar functionality in backend*/ + if (hctx->conf.header.https_remap) { + hctx->conf.header.https_remap = + buffer_is_equal_string(con->uri.scheme, CONST_STR_LEN("https")); + } + + if (con->request.http_method == HTTP_METHOD_CONNECT) { + /*(note: not requiring HTTP/1.1 due to too many non-compliant + * clients such as 'openssl s_client')*/ + if (hctx->conf.header.connect_method) { + hctx->gw.create_env = proxy_create_env_connect; + } + else { + con->http_status = 405; /* Method Not Allowed */ + con->mode = DIRECT; + return HANDLER_FINISHED; + } + } + } + + return HANDLER_GO_ON; +} + + +int mod_proxy_plugin_init(plugin *p); +int mod_proxy_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("proxy"); + + p->init = mod_proxy_init; + p->cleanup = mod_proxy_free; + p->set_defaults = mod_proxy_set_defaults; + p->connection_reset = gw_connection_reset; + p->handle_uri_clean = mod_proxy_check_extension; + p->handle_subrequest = gw_handle_subrequest; + p->handle_trigger = gw_handle_trigger; + p->handle_waitpid = gw_handle_waitpid_cb; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_redirect.c b/data/lighttpd/lighttpd-1.4.53/src/mod_redirect.c new file mode 100644 index 000000000..94c76d8ee --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_redirect.c @@ -0,0 +1,203 @@ +#include "first.h" + +#include "base.h" +#include "keyvalue.h" +#include "log.h" +#include "buffer.h" +#include "burl.h" +#include "http_header.h" + +#include "plugin.h" + +#include <stdlib.h> +#include <string.h> + +typedef struct { + pcre_keyvalue_buffer *redirect; + data_config *context; /* to which apply me */ + unsigned short redirect_code; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +INIT_FUNC(mod_redirect_init) { + return calloc(1, sizeof(plugin_data)); +} + +FREE_FUNC(mod_redirect_free) { + plugin_data *p = p_d; + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + if (NULL == s) continue; + pcre_keyvalue_buffer_free(s->redirect); + free(s); + } + free(p->config_storage); + } + + free(p); + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_redirect_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "url.redirect", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "url.redirect-code", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + /* 0 */ + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + size_t j; + data_unset *du; + data_array *da; + + s = calloc(1, sizeof(plugin_config)); + s->redirect = pcre_keyvalue_buffer_init(); + s->redirect_code = 301; + + cv[0].destination = s->redirect; + cv[1].destination = &(s->redirect_code); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (s->redirect_code < 100 || s->redirect_code >= 1000) s->redirect_code = 301; + + if (NULL == (du = array_get_element(config->value, "url.redirect"))) { + /* no url.redirect defined */ + continue; + } + + da = (data_array *)du; + + if (du->type != TYPE_ARRAY || !array_is_kvstring(da->value)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for url.redirect; expected list of \"regex\" => \"redirect\""); + return HANDLER_ERROR; + } + + for (j = 0; j < da->value->used; j++) { + data_string *ds = (data_string *)da->value->data[j]; + if (srv->srvconf.http_url_normalize) { + pcre_keyvalue_burl_normalize_key(ds->key, srv->tmp_buf); + pcre_keyvalue_burl_normalize_value(ds->value, srv->tmp_buf); + } + if (0 != pcre_keyvalue_buffer_append(srv, s->redirect, ds->key, ds->value)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "pcre-compile failed for", ds->key); + return HANDLER_ERROR; + } + } + } + + return HANDLER_GO_ON; +} + +static int mod_redirect_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + p->conf.redirect = s->redirect; + p->conf.redirect_code = s->redirect_code; + p->conf.context = NULL; + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (0 == strcmp(du->key->ptr, "url.redirect")) { + p->conf.redirect = s->redirect; + p->conf.context = dc; + } else if (0 == strcmp(du->key->ptr, "url.redirect-code")) { + p->conf.redirect_code = s->redirect_code; + } + } + } + + return 0; +} + +URIHANDLER_FUNC(mod_redirect_uri_handler) { + plugin_data *p = p_d; + struct burl_parts_t burl; + pcre_keyvalue_ctx ctx; + handler_t rc; + + mod_redirect_patch_connection(srv, con, p); + if (!p->conf.redirect->used) return HANDLER_GO_ON; + ctx.cache = p->conf.context + ? &con->cond_cache[p->conf.context->context_ndx] + : NULL; + ctx.burl = &burl; + burl.scheme = con->uri.scheme; + burl.authority = con->uri.authority; + burl.port = sock_addr_get_port(&con->srv_socket->addr); + burl.path = con->uri.path_raw; + burl.query = con->uri.query; + if (buffer_string_is_empty(burl.authority)) + burl.authority = con->server_name; + + /* redirect URL on match + * e.g. redirect /base/ to /index.php?section=base + */ + rc = pcre_keyvalue_buffer_process(p->conf.redirect, &ctx, + con->request.uri, srv->tmp_buf); + if (HANDLER_FINISHED == rc) { + http_header_response_set(con, HTTP_HEADER_LOCATION, + CONST_STR_LEN("Location"), + CONST_BUF_LEN(srv->tmp_buf)); + con->http_status = p->conf.redirect_code; + con->mode = DIRECT; + con->file_finished = 1; + } + else if (HANDLER_ERROR == rc) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "pcre_exec() error while processing uri:", + con->request.uri); + } + return rc; +} + +int mod_redirect_plugin_init(plugin *p); +int mod_redirect_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("redirect"); + + p->init = mod_redirect_init; + p->handle_uri_clean = mod_redirect_uri_handler; + p->set_defaults = mod_redirect_set_defaults; + p->cleanup = mod_redirect_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_rewrite.c b/data/lighttpd/lighttpd-1.4.53/src/mod_rewrite.c new file mode 100644 index 000000000..1280d9784 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_rewrite.c @@ -0,0 +1,340 @@ +#include "first.h" + +#include "base.h" +#include "keyvalue.h" +#include "log.h" +#include "buffer.h" +#include "burl.h" + +#include "plugin.h" +#include "stat_cache.h" + +#include <stdlib.h> +#include <string.h> + +typedef struct { + pcre_keyvalue_buffer *rewrite; + pcre_keyvalue_buffer *rewrite_NF; + data_config *context, *context_NF; /* to which apply me */ + int rewrite_repeat_idx, rewrite_NF_repeat_idx; +} plugin_config; + +typedef struct { + enum { REWRITE_STATE_UNSET, REWRITE_STATE_FINISHED} state; + int loops; +} handler_ctx; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +static handler_ctx * handler_ctx_init(void) { + handler_ctx * hctx; + + hctx = calloc(1, sizeof(*hctx)); + + hctx->state = REWRITE_STATE_UNSET; + hctx->loops = 0; + + return hctx; +} + +static void handler_ctx_free(handler_ctx *hctx) { + free(hctx); +} + +INIT_FUNC(mod_rewrite_init) { + return calloc(1, sizeof(plugin_data)); +} + +FREE_FUNC(mod_rewrite_free) { + plugin_data *p = p_d; + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + if (NULL == s) continue; + pcre_keyvalue_buffer_free(s->rewrite); + pcre_keyvalue_buffer_free(s->rewrite_NF); + free(s); + } + free(p->config_storage); + } + + free(p); + return HANDLER_GO_ON; +} + +static int parse_config_entry(server *srv, array *ca, pcre_keyvalue_buffer *kvb, const char *option, size_t olen) { + data_unset *du; + + if (NULL != (du = array_get_element_klen(ca, option, olen))) { + data_array *da; + size_t j; + + da = (data_array *)du; + + if (du->type != TYPE_ARRAY || !array_is_kvstring(da->value)) { + log_error_write(srv, __FILE__, __LINE__, "SSS", + "unexpected value for ", option, "; expected list of \"regex\" => \"subst\""); + return HANDLER_ERROR; + } + + for (j = 0; j < da->value->used; j++) { + data_string *ds = (data_string *)da->value->data[j]; + if (srv->srvconf.http_url_normalize) { + pcre_keyvalue_burl_normalize_key(ds->key, srv->tmp_buf); + pcre_keyvalue_burl_normalize_value(ds->value, srv->tmp_buf); + } + if (0 != pcre_keyvalue_buffer_append(srv, kvb, ds->key, ds->value)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "pcre-compile failed for", ds->key); + return HANDLER_ERROR; + } + } + } + + return 0; +} + +SETDEFAULTS_FUNC(mod_rewrite_set_defaults) { + size_t i = 0; + config_values_t cv[] = { + { "url.rewrite-repeat", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "url.rewrite-once", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + + /* these functions only rewrite if the target is not already in the filestore + * + * url.rewrite-repeat-if-not-file is the equivalent of url.rewrite-repeat + * url.rewrite-if-not-file is the equivalent of url.rewrite-once + * + */ + { "url.rewrite-repeat-if-not-file", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "url.rewrite-if-not-file", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + + /* old names, still supported + * + * url.rewrite remapped to url.rewrite-once + * url.rewrite-final is url.rewrite-once + * + */ + { "url.rewrite", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "url.rewrite-final", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + plugin_data *p = p_d; + + if (!p) return HANDLER_ERROR; + + /* 0 */ + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->rewrite = pcre_keyvalue_buffer_init(); + s->rewrite_NF = pcre_keyvalue_buffer_init(); + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + parse_config_entry(srv, config->value, s->rewrite, CONST_STR_LEN("url.rewrite-once")); + parse_config_entry(srv, config->value, s->rewrite, CONST_STR_LEN("url.rewrite-final")); + parse_config_entry(srv, config->value, s->rewrite_NF, CONST_STR_LEN("url.rewrite-if-not-file")); + s->rewrite_NF_repeat_idx = (int)s->rewrite_NF->used; + parse_config_entry(srv, config->value, s->rewrite_NF, CONST_STR_LEN("url.rewrite-repeat-if-not-file")); + parse_config_entry(srv, config->value, s->rewrite, CONST_STR_LEN("url.rewrite")); + s->rewrite_repeat_idx = (int)s->rewrite->used; + parse_config_entry(srv, config->value, s->rewrite, CONST_STR_LEN("url.rewrite-repeat")); + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_rewrite_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(rewrite); + PATCH(rewrite_NF); + p->conf.context = NULL; + p->conf.context_NF = NULL; + PATCH(rewrite_repeat_idx); + PATCH(rewrite_NF_repeat_idx); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite"))) { + PATCH(rewrite); + p->conf.context = dc; + PATCH(rewrite_repeat_idx); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-once"))) { + PATCH(rewrite); + p->conf.context = dc; + PATCH(rewrite_repeat_idx); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-repeat"))) { + PATCH(rewrite); + p->conf.context = dc; + PATCH(rewrite_repeat_idx); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-if-not-file"))) { + PATCH(rewrite_NF); + p->conf.context_NF = dc; + PATCH(rewrite_NF_repeat_idx); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-repeat-if-not-file"))) { + PATCH(rewrite_NF); + p->conf.context_NF = dc; + PATCH(rewrite_NF_repeat_idx); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-final"))) { + PATCH(rewrite); + p->conf.context = dc; + PATCH(rewrite_repeat_idx); + } + } + } + + return 0; +} + +URIHANDLER_FUNC(mod_rewrite_con_reset) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (con->plugin_ctx[p->id]) { + handler_ctx_free(con->plugin_ctx[p->id]); + con->plugin_ctx[p->id] = NULL; + } + + return HANDLER_GO_ON; +} + +static handler_t process_rewrite_rules(server *srv, connection *con, plugin_data *p, pcre_keyvalue_buffer *kvb, int repeat_idx) { + handler_ctx *hctx; + struct burl_parts_t burl; + pcre_keyvalue_ctx ctx; + handler_t rc; + + if (con->plugin_ctx[p->id]) { + hctx = con->plugin_ctx[p->id]; + + if (hctx->loops++ > 100) { + data_config *dc = p->conf.context; + if (NULL == dc) { + log_error_write(srv, __FILE__, __LINE__, "s", + "ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request"); + return HANDLER_ERROR; + } + log_error_write(srv, __FILE__, __LINE__, "SbbSBS", + "ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request, perhaps you want to use url.rewrite-once instead of url.rewrite-repeat ($", dc->comp_key, dc->op, "\"", dc->string, "\")"); + + return HANDLER_ERROR; + } + + if (hctx->state == REWRITE_STATE_FINISHED) return HANDLER_GO_ON; + } + + ctx.cache = p->conf.context ? &con->cond_cache[p->conf.context->context_ndx] : NULL; + ctx.burl = &burl; + burl.scheme = con->uri.scheme; + burl.authority = con->uri.authority; + burl.port = sock_addr_get_port(&con->srv_socket->addr); + burl.path = con->uri.path_raw; + burl.query = con->uri.query; + if (buffer_string_is_empty(burl.authority)) + burl.authority = con->server_name; + + rc = pcre_keyvalue_buffer_process(kvb, &ctx, con->request.uri, srv->tmp_buf); + if (HANDLER_FINISHED == rc && !buffer_is_empty(srv->tmp_buf) && srv->tmp_buf->ptr[0] == '/') { + buffer_copy_buffer(con->request.uri, srv->tmp_buf); + if (con->plugin_ctx[p->id] == NULL) { + hctx = handler_ctx_init(); + con->plugin_ctx[p->id] = hctx; + } else { + hctx = con->plugin_ctx[p->id]; + } + if (ctx.m < repeat_idx) hctx->state = REWRITE_STATE_FINISHED; + buffer_reset(con->physical.path); + rc = HANDLER_COMEBACK; + } + else if (HANDLER_FINISHED == rc) { + rc = HANDLER_ERROR; + log_error_write(srv, __FILE__, __LINE__, "sb", + "mod_rewrite invalid result (not beginning with '/') while processing uri:", + con->request.uri); + } + else if (HANDLER_ERROR == rc) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "pcre_exec() error while processing uri:", + con->request.uri); + } + return rc; +} + +URIHANDLER_FUNC(mod_rewrite_physical) { + plugin_data *p = p_d; + stat_cache_entry *sce; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + mod_rewrite_patch_connection(srv, con, p); + p->conf.context = p->conf.context_NF; + if (!p->conf.rewrite_NF->used) return HANDLER_GO_ON; + + /* skip if physical.path is a regular file */ + sce = NULL; + if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + if (S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON; + } + + return process_rewrite_rules(srv, con, p, p->conf.rewrite_NF, p->conf.rewrite_NF_repeat_idx); +} + +URIHANDLER_FUNC(mod_rewrite_uri_handler) { + plugin_data *p = p_d; + + mod_rewrite_patch_connection(srv, con, p); + if (!p->conf.rewrite->used) return HANDLER_GO_ON; + + return process_rewrite_rules(srv, con, p, p->conf.rewrite, p->conf.rewrite_repeat_idx); +} + +int mod_rewrite_plugin_init(plugin *p); +int mod_rewrite_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("rewrite"); + + p->init = mod_rewrite_init; + /* it has to stay _raw as we are matching on uri + querystring + */ + + p->handle_uri_raw = mod_rewrite_uri_handler; + p->handle_physical = mod_rewrite_physical; + p->cleanup = mod_rewrite_free; + p->connection_reset = mod_rewrite_con_reset; + p->set_defaults = mod_rewrite_set_defaults; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_rrdtool.c b/data/lighttpd/lighttpd-1.4.53/src/mod_rrdtool.c new file mode 100644 index 000000000..009943d01 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_rrdtool.c @@ -0,0 +1,482 @@ +#include "first.h" + +#include "base.h" +#include "fdevent.h" +#include "log.h" + +#include "plugin.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <time.h> + +typedef struct { + buffer *path_rrdtool_bin; + buffer *path_rrd; + + double requests, *requests_ptr; + double bytes_written, *bytes_written_ptr; + double bytes_read, *bytes_read_ptr; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + buffer *cmd; + buffer *resp; + + int read_fd, write_fd; + pid_t rrdtool_pid; + pid_t srv_pid; + + int rrdtool_running; + time_t rrdtool_startup_ts; + + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +INIT_FUNC(mod_rrd_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->resp = buffer_init(); + p->cmd = buffer_init(); + + return p; +} + +FREE_FUNC(mod_rrd_free) { + plugin_data *p = p_d; + size_t i; + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->path_rrdtool_bin); + buffer_free(s->path_rrd); + + free(s); + } + } + buffer_free(p->cmd); + buffer_free(p->resp); + + free(p->config_storage); + + if (p->read_fd >= 0) close(p->read_fd); + if (p->write_fd >= 0) close(p->write_fd); + + if (p->rrdtool_pid > 0 && p->srv_pid == srv->pid) { + /* collect status */ + while (-1 == waitpid(p->rrdtool_pid, NULL, 0) && errno == EINTR) ; + } + + free(p); + + return HANDLER_GO_ON; +} + +static int mod_rrd_create_pipe(server *srv, plugin_data *p) { + char *args[3]; + int to_rrdtool_fds[2]; + int from_rrdtool_fds[2]; + /* mod_rrdtool does not work with server.max-workers > 0 + * since the data between workers is not aggregated, + * and it is not valid to send data to rrdtool more than once a sec + * (which would happen with multiple workers writing to same pipe) + * If pipes were to be shared, then existing pipes would need to be + * reused here, if they already exist (not -1), and after flushing + * existing contents (read and discard from read-end of pipes). */ + if (pipe(to_rrdtool_fds)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "pipe failed: ", strerror(errno)); + return -1; + } + if (pipe(from_rrdtool_fds)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "pipe failed: ", strerror(errno)); + return -1; + } + fdevent_setfd_cloexec(to_rrdtool_fds[1]); + fdevent_setfd_cloexec(from_rrdtool_fds[0]); + *(const char **)&args[0] = p->conf.path_rrdtool_bin->ptr; + *(const char **)&args[1] = "-"; + args[2] = NULL; + + p->rrdtool_pid = fdevent_fork_execve(args[0], args, NULL, to_rrdtool_fds[0], from_rrdtool_fds[1], -1, -1); + + if (-1 != p->rrdtool_pid) { + close(from_rrdtool_fds[1]); + close(to_rrdtool_fds[0]); + if (p->read_fd >= 0) close(p->read_fd); + if (p->write_fd >= 0) close(p->write_fd); + p->write_fd = to_rrdtool_fds[1]; + p->read_fd = from_rrdtool_fds[0]; + p->srv_pid = srv->pid; + return 0; + } else { + log_error_write(srv, __FILE__, __LINE__, "SBss", "fork/exec(", p->conf.path_rrdtool_bin, "):", strerror(errno)); + close(to_rrdtool_fds[0]); + close(to_rrdtool_fds[1]); + close(from_rrdtool_fds[0]); + close(from_rrdtool_fds[1]); + return -1; + } +} + +/* read/write wrappers to catch EINTR */ + +/* write to blocking socket; blocks until all data is sent, write returns 0 or an error (apart from EINTR) occurs. */ +static ssize_t safe_write(int fd, const void *buf, size_t count) { + ssize_t res, sum = 0; + + for (;;) { + res = write(fd, buf, count); + if (res >= 0) { + sum += res; + /* do not try again if res == 0 */ + if (res == 0 || (size_t) res == count) return sum; + count -= res; + buf = (const char*) buf + res; + continue; + } + switch (errno) { + case EINTR: + continue; + default: + return -1; + } + } +} + +/* this assumes we get enough data on a successful read */ +static ssize_t safe_read(int fd, buffer *b) { + ssize_t res; + + buffer_string_prepare_copy(b, 4095); + + do { + res = read(fd, b->ptr, b->size-1); + } while (-1 == res && errno == EINTR); + + if (res >= 0) buffer_commit(b, res); + return res; +} + +static int mod_rrdtool_create_rrd(server *srv, plugin_data *p, plugin_config *s) { + struct stat st; + + /* check if DB already exists */ + if (0 == stat(s->path_rrd->ptr, &st)) { + /* check if it is plain file */ + if (!S_ISREG(st.st_mode)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "not a regular file:", s->path_rrd); + return HANDLER_ERROR; + } + + /* still create DB if it's empty file */ + if (st.st_size > 0) { + return HANDLER_GO_ON; + } + } + + /* create a new one */ + buffer_copy_string_len(p->cmd, CONST_STR_LEN("create ")); + buffer_append_string_buffer(p->cmd, s->path_rrd); + buffer_append_string_len(p->cmd, CONST_STR_LEN( + " --step 60 " + "DS:InOctets:ABSOLUTE:600:U:U " + "DS:OutOctets:ABSOLUTE:600:U:U " + "DS:Requests:ABSOLUTE:600:U:U " + "RRA:AVERAGE:0.5:1:600 " + "RRA:AVERAGE:0.5:6:700 " + "RRA:AVERAGE:0.5:24:775 " + "RRA:AVERAGE:0.5:288:797 " + "RRA:MAX:0.5:1:600 " + "RRA:MAX:0.5:6:700 " + "RRA:MAX:0.5:24:775 " + "RRA:MAX:0.5:288:797 " + "RRA:MIN:0.5:1:600 " + "RRA:MIN:0.5:6:700 " + "RRA:MIN:0.5:24:775 " + "RRA:MIN:0.5:288:797\n")); + + if (-1 == (safe_write(p->write_fd, CONST_BUF_LEN(p->cmd)))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "rrdtool-write: failed", strerror(errno)); + + return HANDLER_ERROR; + } + + if (-1 == safe_read(p->read_fd, p->resp)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "rrdtool-read: failed", strerror(errno)); + + return HANDLER_ERROR; + } + + if (p->resp->ptr[0] != 'O' || + p->resp->ptr[1] != 'K') { + log_error_write(srv, __FILE__, __LINE__, "sbb", + "rrdtool-response:", p->cmd, p->resp); + + return HANDLER_ERROR; + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_rrd_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(path_rrdtool_bin); + PATCH(path_rrd); + + p->conf.bytes_written_ptr = &(s->bytes_written); + p->conf.bytes_read_ptr = &(s->bytes_read); + p->conf.requests_ptr = &(s->requests); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("rrdtool.db-name"))) { + PATCH(path_rrd); + /* get pointers to double values */ + + p->conf.bytes_written_ptr = &(s->bytes_written); + p->conf.bytes_read_ptr = &(s->bytes_read); + p->conf.requests_ptr = &(s->requests); + } + } + } + + return 0; +} +#undef PATCH + +static int mod_rrd_exec(server *srv, plugin_data *p) { + if (mod_rrd_create_pipe(srv, p)) { + return -1; + } + + p->rrdtool_running = 1; + p->rrdtool_startup_ts = srv->cur_ts; + return 0; +} + +SETDEFAULTS_FUNC(mod_rrd_set_defaults) { + plugin_data *p = p_d; + size_t i; + int activate = 0; + + config_values_t cv[] = { + { "rrdtool.binary", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "rrdtool.db-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + force_assert(srv->config_context->used > 0); + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->path_rrdtool_bin = buffer_init(); + s->path_rrd = buffer_init(); + s->requests = 0; + s->bytes_written = 0; + s->bytes_read = 0; + + cv[0].destination = s->path_rrdtool_bin; + cv[1].destination = s->path_rrd; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (i > 0 && !buffer_string_is_empty(s->path_rrdtool_bin)) { + /* path_rrdtool_bin is a global option */ + + log_error_write(srv, __FILE__, __LINE__, "s", + "rrdtool.binary can only be set as a global option."); + + return HANDLER_ERROR; + } + + if (!buffer_string_is_empty(s->path_rrd)) activate = 1; + } + + p->conf.path_rrdtool_bin = p->config_storage[0]->path_rrdtool_bin; + p->rrdtool_running = 0; + p->read_fd = -1; + p->write_fd = -1; + + if (!activate) return HANDLER_GO_ON; + + /* check for dir */ + + if (buffer_string_is_empty(p->conf.path_rrdtool_bin)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "rrdtool.binary has to be set"); + return HANDLER_ERROR; + } + + return 0 == mod_rrd_exec(srv, p) ? HANDLER_GO_ON : HANDLER_ERROR; +} + +static void mod_rrd_fatal_error(server *srv, plugin_data *p) { + /* future: might send kill() signal to p->rrdtool_pid to trigger restart */ + p->rrdtool_running = 0; + UNUSED(srv); +} + +TRIGGER_FUNC(mod_rrd_trigger) { + plugin_data *p = p_d; + size_t i; + + if (!p->rrdtool_running) { + /* limit restart to once every 5 sec */ + /*(0 == p->rrdtool_pid if never activated; not used)*/ + if (-1 == p->rrdtool_pid + && p->srv_pid == srv->pid + && p->rrdtool_startup_ts + 5 < srv->cur_ts) { + mod_rrd_exec(srv, p); + } + return HANDLER_GO_ON; + } + + if ((srv->cur_ts % 60) != 0) return HANDLER_GO_ON; + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (buffer_string_is_empty(s->path_rrd)) continue; + + /* write the data down every minute */ + + if (HANDLER_GO_ON != mod_rrdtool_create_rrd(srv, p, s)) return HANDLER_GO_ON; + + buffer_copy_string_len(p->cmd, CONST_STR_LEN("update ")); + buffer_append_string_buffer(p->cmd, s->path_rrd); + buffer_append_string_len(p->cmd, CONST_STR_LEN(" N:")); + buffer_append_int(p->cmd, s->bytes_read); + buffer_append_string_len(p->cmd, CONST_STR_LEN(":")); + buffer_append_int(p->cmd, s->bytes_written); + buffer_append_string_len(p->cmd, CONST_STR_LEN(":")); + buffer_append_int(p->cmd, s->requests); + buffer_append_string_len(p->cmd, CONST_STR_LEN("\n")); + + if (-1 == safe_write(p->write_fd, CONST_BUF_LEN(p->cmd))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "rrdtool-write: failed", strerror(errno)); + + mod_rrd_fatal_error(srv, p); + return HANDLER_GO_ON; + } + + if (-1 == safe_read(p->read_fd, p->resp)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "rrdtool-read: failed", strerror(errno)); + + mod_rrd_fatal_error(srv, p); + return HANDLER_GO_ON; + } + + if (p->resp->ptr[0] != 'O' || + p->resp->ptr[1] != 'K') { + /* don't fail on this error if we just started (graceful restart, the old one might have just updated too) */ + if (!(strstr(p->resp->ptr, "(minimum one second step)") && (srv->cur_ts - srv->startup_ts < 3))) { + log_error_write(srv, __FILE__, __LINE__, "sbb", + "rrdtool-response:", p->cmd, p->resp); + + mod_rrd_fatal_error(srv, p); + return HANDLER_GO_ON; + } + } + s->requests = 0; + s->bytes_written = 0; + s->bytes_read = 0; + } + + return HANDLER_GO_ON; +} + +static handler_t mod_rrd_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) { + plugin_data *p = p_d; + if (pid != p->rrdtool_pid) return HANDLER_GO_ON; + if (srv->pid != p->srv_pid) return HANDLER_GO_ON; + + p->rrdtool_running = 0; + p->rrdtool_pid = -1; + + /* limit restart to once every 5 sec */ + if (p->rrdtool_startup_ts + 5 < srv->cur_ts) + mod_rrd_exec(srv, p); + + UNUSED(status); + return HANDLER_FINISHED; +} + +REQUESTDONE_FUNC(mod_rrd_account) { + plugin_data *p = p_d; + + /*(0 == p->rrdtool_pid if never activated; not used)*/ + if (0 == p->rrdtool_pid) return HANDLER_GO_ON; + mod_rrd_patch_connection(srv, con, p); + + *(p->conf.requests_ptr) += 1; + *(p->conf.bytes_written_ptr) += con->bytes_written; + *(p->conf.bytes_read_ptr) += con->bytes_read; + + return HANDLER_GO_ON; +} + +int mod_rrdtool_plugin_init(plugin *p); +int mod_rrdtool_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("rrd"); + + p->init = mod_rrd_init; + p->cleanup = mod_rrd_free; + p->set_defaults= mod_rrd_set_defaults; + + p->handle_trigger = mod_rrd_trigger; + p->handle_waitpid = mod_rrd_waitpid_cb; + p->handle_request_done = mod_rrd_account; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_scgi.c b/data/lighttpd/lighttpd-1.4.53/src/mod_scgi.c new file mode 100644 index 000000000..785e66a30 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_scgi.c @@ -0,0 +1,327 @@ +#include "first.h" + +#include <sys/types.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include "gw_backend.h" +typedef gw_plugin_config plugin_config; +typedef gw_plugin_data plugin_data; +typedef gw_handler_ctx handler_ctx; + +#include "base.h" +#include "buffer.h" +#include "log.h" +#include "status_counter.h" + +#include "sys-endian.h" + +enum { LI_PROTOCOL_SCGI, LI_PROTOCOL_UWSGI }; + +SETDEFAULTS_FUNC(mod_scgi_set_defaults) { + plugin_data *p = p_d; + data_unset *du; + size_t i = 0; + + config_values_t cv[] = { + { "scgi.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "scgi.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "scgi.protocol", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "scgi.map-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "scgi.balance", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + force_assert(p->config_storage); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + force_assert(s); + s->exts = NULL; + s->exts_auth = NULL; + s->exts_resp = NULL; + s->debug = 0; + s->proto = LI_PROTOCOL_SCGI; + s->ext_mapping = array_init(); + + cv[0].destination = s->exts; /* not used; T_CONFIG_LOCAL */ + cv[1].destination = &(s->debug); + cv[2].destination = NULL; /* not used; T_CONFIG_LOCAL */ + cv[3].destination = s->ext_mapping; + cv[4].destination = NULL; /* not used; T_CONFIG_LOCAL */ + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + du = array_get_element(config->value, "scgi.server"); + if (!gw_set_defaults_backend(srv, p, du, i, 1)) { + return HANDLER_ERROR; + } + + du = array_get_element(config->value, "scgi.balance"); + if (!gw_set_defaults_balance(srv, s, du)) { + return HANDLER_ERROR; + } + + if (NULL != (du = array_get_element(config->value, "scgi.protocol"))) { + data_string *ds = (data_string *)du; + if (du->type == TYPE_STRING + && buffer_is_equal_string(ds->value, CONST_STR_LEN("scgi"))) { + s->proto = LI_PROTOCOL_SCGI; + } else if (du->type == TYPE_STRING + && buffer_is_equal_string(ds->value, CONST_STR_LEN("uwsgi"))) { + s->proto = LI_PROTOCOL_UWSGI; + } else { + log_error_write(srv, __FILE__, __LINE__, "sss", + "unexpected type for key: ", "scgi.protocol", "expected \"scgi\" or \"uwsgi\""); + + return HANDLER_ERROR; + } + } + } + + return HANDLER_GO_ON; +} + +static int scgi_env_add_scgi(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { + buffer *env = venv; + char *dst; + size_t len; + + if (!key || !val) return -1; + + len = key_len + val_len + 2; + + if (buffer_string_space(env) < len) { + size_t extend = env->size * 2 - buffer_string_length(env); + extend = extend > len ? extend : len + 4095; + buffer_string_prepare_append(env, extend); + } + + dst = buffer_string_prepare_append(env, len); + memcpy(dst, key, key_len); + dst[key_len] = '\0'; + memcpy(dst + key_len + 1, val, val_len); + dst[key_len + 1 + val_len] = '\0'; + buffer_commit(env, len); + + return 0; +} + + +#ifdef __LITTLE_ENDIAN__ +#define uwsgi_htole16(x) (x) +#else /* __BIG_ENDIAN__ */ +#define uwsgi_htole16(x) ((uint16_t) (((x) & 0xff) << 8 | ((x) & 0xff00) >> 8)) +#endif + + +static int scgi_env_add_uwsgi(void *venv, const char *key, size_t key_len, const char *val, size_t val_len) { + buffer *env = venv; + char *dst; + size_t len; + uint16_t uwlen; + + if (!key || !val) return -1; + if (key_len > USHRT_MAX || val_len > USHRT_MAX) return -1; + + len = 2 + key_len + 2 + val_len; + + if (buffer_string_space(env) < len) { + size_t extend = env->size * 2 - buffer_string_length(env); + extend = extend > len ? extend : len + 4095; + buffer_string_prepare_append(env, extend); + } + + dst = buffer_string_prepare_append(env, len); + uwlen = uwsgi_htole16((uint16_t)key_len); + memcpy(dst, (char *)&uwlen, 2); + memcpy(dst + 2, key, key_len); + uwlen = uwsgi_htole16((uint16_t)val_len); + memcpy(dst + 2 + key_len, (char *)&uwlen, 2); + memcpy(dst + 2 + key_len + 2, val, val_len); + buffer_commit(env, len); + + return 0; +} + + +static handler_t scgi_create_env(server *srv, handler_ctx *hctx) { + gw_host *host = hctx->host; + connection *con = hctx->remote_conn; + http_cgi_opts opts = { 0, 0, host->docroot, NULL }; + http_cgi_header_append_cb scgi_env_add = hctx->conf.proto == LI_PROTOCOL_SCGI + ? scgi_env_add_scgi + : scgi_env_add_uwsgi; + size_t offset; + size_t rsz = (size_t)(con->read_queue->bytes_out - hctx->wb->bytes_in); + buffer * const b = chunkqueue_prepend_buffer_open_sz(hctx->wb, rsz < 65536 ? rsz : con->header_len); + + /* save space for 9 digits (plus ':'), though incoming HTTP request + * currently limited to 64k (65535, so 5 chars) */ + buffer_copy_string_len(b, CONST_STR_LEN(" ")); + + if (0 != http_cgi_headers(srv, con, &opts, scgi_env_add, b)) { + con->http_status = 400; + con->mode = DIRECT; + buffer_clear(b); + chunkqueue_remove_finished_chunks(hctx->wb); + return HANDLER_FINISHED; + } + + if (hctx->conf.proto == LI_PROTOCOL_SCGI) { + size_t len; + scgi_env_add(b, CONST_STR_LEN("SCGI"), CONST_STR_LEN("1")); + buffer_clear(srv->tmp_buf); + buffer_append_int(srv->tmp_buf, buffer_string_length(b)-10); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(":")); + len = buffer_string_length(srv->tmp_buf); + offset = 10 - len; + memcpy(b->ptr+offset, srv->tmp_buf->ptr, len); + buffer_append_string_len(b, CONST_STR_LEN(",")); + } else { /* LI_PROTOCOL_UWSGI */ + /* http://uwsgi-docs.readthedocs.io/en/latest/Protocol.html */ + size_t len = buffer_string_length(b)-10; + uint32_t uwsgi_header; + if (len > USHRT_MAX) { + con->http_status = 431; /* Request Header Fields Too Large */ + con->mode = DIRECT; + buffer_clear(b); + chunkqueue_remove_finished_chunks(hctx->wb); + return HANDLER_FINISHED; + } + offset = 10 - 4; + uwsgi_header = ((uint32_t)uwsgi_htole16((uint16_t)len)) << 8; + memcpy(b->ptr+offset, (char *)&uwsgi_header, 4); + } + + hctx->wb_reqlen = buffer_string_length(b) - offset; + chunkqueue_prepend_buffer_commit(hctx->wb); + #if 0 + hctx->wb->first->offset += (off_t)offset; + hctx->wb->bytes_in -= (off_t)offset; + #else + chunkqueue_mark_written(hctx->wb, offset); + #endif + + if (con->request.content_length) { + chunkqueue_append_chunkqueue(hctx->wb, con->request_content_queue); + if (con->request.content_length > 0) + hctx->wb_reqlen += con->request.content_length; /* total req size */ + else /* as-yet-unknown total request size (Transfer-Encoding: chunked)*/ + hctx->wb_reqlen = -hctx->wb_reqlen; + } + + status_counter_inc(srv, CONST_STR_LEN("scgi.requests")); + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int scgi_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(exts); + PATCH(exts_auth); + PATCH(exts_resp); + PATCH(proto); + PATCH(debug); + PATCH(balance); + PATCH(ext_mapping); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.server"))) { + PATCH(exts); + PATCH(exts_auth); + PATCH(exts_resp); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.protocol"))) { + PATCH(proto); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.balance"))) { + PATCH(balance); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.debug"))) { + PATCH(debug); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("scgi.map-extensions"))) { + PATCH(ext_mapping); + } + } + } + + return 0; +} +#undef PATCH + + +static handler_t scgi_check_extension(server *srv, connection *con, void *p_d, int uri_path_handler) { + plugin_data *p = p_d; + handler_t rc; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + scgi_patch_connection(srv, con, p); + if (NULL == p->conf.exts) return HANDLER_GO_ON; + + rc = gw_check_extension(srv, con, p, uri_path_handler, 0); + if (HANDLER_GO_ON != rc) return rc; + + if (con->mode == p->id) { + handler_ctx *hctx = con->plugin_ctx[p->id]; + hctx->opts.backend = BACKEND_SCGI; + hctx->create_env = scgi_create_env; + hctx->response = chunk_buffer_acquire(); + } + + return HANDLER_GO_ON; +} + +/* uri-path handler */ +static handler_t scgi_check_extension_1(server *srv, connection *con, void *p_d) { + return scgi_check_extension(srv, con, p_d, 1); +} + +/* start request handler */ +static handler_t scgi_check_extension_2(server *srv, connection *con, void *p_d) { + return scgi_check_extension(srv, con, p_d, 0); +} + + + +int mod_scgi_plugin_init(plugin *p); +int mod_scgi_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("scgi"); + + p->init = gw_init; + p->cleanup = gw_free; + p->set_defaults = mod_scgi_set_defaults; + p->connection_reset = gw_connection_reset; + p->handle_uri_clean = scgi_check_extension_1; + p->handle_subrequest_start = scgi_check_extension_2; + p->handle_subrequest = gw_handle_subrequest; + p->handle_trigger = gw_handle_trigger; + p->handle_waitpid = gw_handle_waitpid_cb; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_secdownload.c b/data/lighttpd/lighttpd-1.4.53/src/mod_secdownload.c new file mode 100644 index 000000000..41d6a2c75 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_secdownload.c @@ -0,0 +1,571 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "base64.h" +#include "http_auth.h" + +#include "plugin.h" + +#include <stdlib.h> +#include <string.h> + +#include "sys-crypto.h" +#ifdef USE_OPENSSL_CRYPTO +#include <openssl/evp.h> +#include <openssl/hmac.h> +#endif + +#include "md5.h" + +/* + * mod_secdownload verifies a checksum associated with a timestamp + * and a path. + * + * It takes an URL of the form: + * securl := <uri-prefix> <mac> <protected-path> + * uri-prefix := '/' any* # whatever was configured: must start with a '/') + * mac := [a-zA-Z0-9_-]{mac_len} # mac length depends on selected algorithm + * protected-path := '/' <timestamp> <rel-path> + * timestamp := [a-f0-9]{8} # timestamp when the checksum was calculated + * # to prevent access after timeout (active requests + * # will finish successfully even after the timeout) + * rel-path := '/' any* # the protected path; changing the path breaks the + * # checksum + * + * The timestamp is the `epoch` timestamp in hex, i.e. time in seconds + * since 00:00:00 UTC on 1 January 1970. + * + * mod_secdownload supports various MAC algorithms: + * + * # md5 + * mac_len := 32 (and hex only) + * mac := md5-hex(<secrect><rel-path><timestamp>) # lowercase hex + * perl example: + use Digest::MD5 qw(md5_hex); + my $secret = "verysecret"; + my $rel_path = "/index.html" + my $xtime = sprintf("%08x", time); + my $url = '/'. md5_hex($secret . $rel_path . $xtime) . '/' . $xtime . $rel_path; + * + * # hmac-sha1 + * mac_len := 27 (no base64 padding) + * mac := base64-url(hmac-sha1(<secret>, <protected-path>)) + * perl example: + use Digest::SHA qw(hmac_sha1); + use MIME::Base64 qw(encode_base64url); + my $secret = "verysecret"; + my $rel_path = "/index.html" + my $protected_path = '/' . sprintf("%08x", time) . $rel_path; + my $url = '/'. encode_base64url(hmac_sha1($protected_path, $secret)) . $protected_path; + * + * # hmac-256 + * mac_len := 43 (no base64 padding) + * mac := base64-url(hmac-256(<secret>, <protected-path>)) + use Digest::SHA qw(hmac_sha256); + use MIME::Base64 qw(encode_base64url); + my $secret = "verysecret"; + my $rel_path = "/index.html" + my $protected_path = '/' . sprintf("%08x", time) . $rel_path; + my $url = '/'. encode_base64url(hmac_sha256($protected_path, $secret)) . $protected_path; + * + */ + +/* plugin config for all request/connections */ + +typedef enum { + SECDL_INVALID = 0, + SECDL_MD5 = 1, + SECDL_HMAC_SHA1 = 2, + SECDL_HMAC_SHA256 = 3, +} secdl_algorithm; + +typedef struct { + buffer *doc_root; + buffer *secret; + buffer *uri_prefix; + secdl_algorithm algorithm; + + unsigned int timeout; + unsigned short path_segments; + unsigned short hash_querystr; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +static int const_time_memeq(const char *a, const char *b, size_t len) { + /* constant time memory compare, unless the compiler figures it out */ + char diff = 0; + size_t i; + for (i = 0; i < len; ++i) { + diff |= (a[i] ^ b[i]); + } + return 0 == diff; +} + +static const char* secdl_algorithm_names[] = { + "invalid", + "md5", + "hmac-sha1", + "hmac-sha256", +}; + +static secdl_algorithm algorithm_from_string(buffer *name) { + size_t ndx; + + if (buffer_string_is_empty(name)) return SECDL_INVALID; + + for (ndx = 1; ndx < sizeof(secdl_algorithm_names)/sizeof(secdl_algorithm_names[0]); ++ndx) { + if (0 == strcmp(secdl_algorithm_names[ndx], name->ptr)) return (secdl_algorithm)ndx; + } + + return SECDL_INVALID; +} + +static size_t secdl_algorithm_mac_length(secdl_algorithm alg) { + switch (alg) { + case SECDL_INVALID: + break; + case SECDL_MD5: + return 32; + case SECDL_HMAC_SHA1: + return 27; + case SECDL_HMAC_SHA256: + return 43; + } + return 0; +} + +static int secdl_verify_mac(server *srv, plugin_config *config, const char* protected_path, const char* mac, size_t maclen) { + UNUSED(srv); + if (0 == maclen || secdl_algorithm_mac_length(config->algorithm) != maclen) return 0; + + switch (config->algorithm) { + case SECDL_INVALID: + break; + case SECDL_MD5: + { + li_MD5_CTX Md5Ctx; + const char *ts_str; + const char *rel_uri; + unsigned char HA1[16]; + unsigned char md5bin[16]; + + if (0 != http_auth_md5_hex2bin(mac, maclen, md5bin)) return 0; + + /* legacy message: + * protected_path := '/' <timestamp-hex> <rel-path> + * timestamp-hex := [0-9a-f]{8} + * rel-path := '/' any* + * (the protected path was already verified) + * message = <secret><rel-path><timestamp-hex> + */ + ts_str = protected_path + 1; + rel_uri = ts_str + 8; + + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(config->secret)); + li_MD5_Update(&Md5Ctx, rel_uri, strlen(rel_uri)); + li_MD5_Update(&Md5Ctx, ts_str, 8); + li_MD5_Final(HA1, &Md5Ctx); + + return const_time_memeq((char *)HA1, (char *)md5bin, sizeof(md5bin)); + } + case SECDL_HMAC_SHA1: +#ifdef USE_OPENSSL_CRYPTO + { + unsigned char digest[20]; + char base64_digest[27]; + + if (NULL == HMAC( + EVP_sha1(), + (unsigned char const*) config->secret->ptr, buffer_string_length(config->secret), + (unsigned char const*) protected_path, strlen(protected_path), + digest, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "hmac-sha1: HMAC() failed"); + return 0; + } + + li_to_base64_no_padding(base64_digest, 27, digest, 20, BASE64_URL); + + return (27 == maclen) && const_time_memeq(mac, base64_digest, 27); + } +#endif + break; + case SECDL_HMAC_SHA256: +#ifdef USE_OPENSSL_CRYPTO + { + unsigned char digest[32]; + char base64_digest[43]; + + if (NULL == HMAC( + EVP_sha256(), + (unsigned char const*) config->secret->ptr, buffer_string_length(config->secret), + (unsigned char const*) protected_path, strlen(protected_path), + digest, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "hmac-sha256: HMAC() failed"); + return 0; + } + + li_to_base64_no_padding(base64_digest, 43, digest, 32, BASE64_URL); + + return (43 == maclen) && const_time_memeq(mac, base64_digest, 43); + } +#endif + break; + } + + return 0; +} + +/* init the plugin data */ +INIT_FUNC(mod_secdownload_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_secdownload_free) { + plugin_data *p = p_d; + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->secret); + buffer_free(s->doc_root); + buffer_free(s->uri_prefix); + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_secdownload_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "secdownload.secret", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "secdownload.document-root", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "secdownload.uri-prefix", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "secdownload.timeout", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "secdownload.algorithm", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "secdownload.path-segments", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { "secdownload.hash-querystr", NULL, T_CONFIG_BOOLEAN,T_CONFIG_SCOPE_CONNECTION }, /* 6 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + buffer *algorithm = buffer_init(); + + s = calloc(1, sizeof(plugin_config)); + s->secret = buffer_init(); + s->doc_root = buffer_init(); + s->uri_prefix = buffer_init(); + s->timeout = 60; + s->path_segments = 0; + s->hash_querystr = 0; + + cv[0].destination = s->secret; + cv[1].destination = s->doc_root; + cv[2].destination = s->uri_prefix; + cv[3].destination = &(s->timeout); + cv[4].destination = algorithm; + cv[5].destination = &(s->path_segments); + cv[6].destination = &(s->hash_querystr); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + buffer_free(algorithm); + return HANDLER_ERROR; + } + + if (!buffer_is_empty(algorithm)) { + s->algorithm = algorithm_from_string(algorithm); + switch (s->algorithm) { + case SECDL_INVALID: + log_error_write(srv, __FILE__, __LINE__, "sb", + "invalid secdownload.algorithm:", + algorithm); + buffer_free(algorithm); + return HANDLER_ERROR; +#ifndef USE_OPENSSL_CRYPTO + case SECDL_HMAC_SHA1: + case SECDL_HMAC_SHA256: + log_error_write(srv, __FILE__, __LINE__, "sb", + "unsupported secdownload.algorithm:", + algorithm); +#endif + default: + break; + } + } + + buffer_free(algorithm); + } + + return HANDLER_GO_ON; +} + +/** + * checks if the supplied string is a hex string + * + * @param str a possible hex string + * @return if the supplied string is a valid hex string 1 is returned otherwise 0 + */ + +static int is_hex_len(const char *str, size_t len) { + size_t i; + + if (NULL == str) return 0; + + for (i = 0; i < len && *str; i++, str++) { + /* illegal characters */ + if (!((*str >= '0' && *str <= '9') || + (*str >= 'a' && *str <= 'f') || + (*str >= 'A' && *str <= 'F')) + ) { + return 0; + } + } + + return i == len; +} + +/** + * checks if the supplied string is a base64 (modified URL) string + * + * @param str a possible base64 (modified URL) string + * @return if the supplied string is a valid base64 (modified URL) string 1 is returned otherwise 0 + */ + +static int is_base64_len(const char *str, size_t len) { + size_t i; + + if (NULL == str) return 0; + + for (i = 0; i < len && *str; i++, str++) { + /* illegal characters */ + if (!((*str >= '0' && *str <= '9') || + (*str >= 'a' && *str <= 'z') || + (*str >= 'A' && *str <= 'Z') || + (*str == '-') || (*str == '_')) + ) { + return 0; + } + } + + return i == len; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_secdownload_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(secret); + PATCH(doc_root); + PATCH(uri_prefix); + PATCH(timeout); + PATCH(algorithm); + PATCH(path_segments); + PATCH(hash_querystr); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.secret"))) { + PATCH(secret); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.document-root"))) { + PATCH(doc_root); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.uri-prefix"))) { + PATCH(uri_prefix); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.timeout"))) { + PATCH(timeout); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.algorithm"))) { + PATCH(algorithm); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.path-segments"))) { + PATCH(path_segments); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.hash-querystr"))) { + PATCH(hash_querystr); + } + } + } + + return 0; +} +#undef PATCH + + +URIHANDLER_FUNC(mod_secdownload_uri_handler) { + plugin_data *p = p_d; + const char *rel_uri, *ts_str, *mac_str, *protected_path; + time_t ts = 0; + size_t i, mac_len; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; + + mod_secdownload_patch_connection(srv, con, p); + + if (buffer_string_is_empty(p->conf.uri_prefix)) return HANDLER_GO_ON; + + if (buffer_string_is_empty(p->conf.secret)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "secdownload.secret has to be set"); + con->http_status = 500; + return HANDLER_FINISHED; + } + + if (buffer_string_is_empty(p->conf.doc_root)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "secdownload.document-root has to be set"); + con->http_status = 500; + return HANDLER_FINISHED; + } + + if (SECDL_INVALID == p->conf.algorithm) { + log_error_write(srv, __FILE__, __LINE__, "s", + "secdownload.algorithm has to be set"); + con->http_status = 500; + return HANDLER_FINISHED; + } + + mac_len = secdl_algorithm_mac_length(p->conf.algorithm); + + if (0 != strncmp(con->uri.path->ptr, p->conf.uri_prefix->ptr, buffer_string_length(p->conf.uri_prefix))) return HANDLER_GO_ON; + + mac_str = con->uri.path->ptr + buffer_string_length(p->conf.uri_prefix); + + if (!is_base64_len(mac_str, mac_len)) return HANDLER_GO_ON; + + protected_path = mac_str + mac_len; + if (*protected_path != '/') return HANDLER_GO_ON; + + ts_str = protected_path + 1; + if (!is_hex_len(ts_str, 8)) return HANDLER_GO_ON; + if (*(ts_str + 8) != '/') return HANDLER_GO_ON; + + for (i = 0; i < 8; i++) { + ts = (ts << 4) + hex2int(ts_str[i]); + } + + /* timed-out */ + if ( (srv->cur_ts > ts && (unsigned int) (srv->cur_ts - ts) > p->conf.timeout) || + (srv->cur_ts < ts && (unsigned int) (ts - srv->cur_ts) > p->conf.timeout) ) { + /* "Gone" as the url will never be valid again instead of "408 - Timeout" where the request may be repeated */ + con->http_status = 410; + + return HANDLER_FINISHED; + } + + rel_uri = ts_str + 8; + + if (p->conf.path_segments) { + const char *rel_uri_end = rel_uri; + unsigned int count = p->conf.path_segments; + do { + rel_uri_end = strchr(rel_uri_end+1, '/'); + } while (rel_uri_end && --count); + if (rel_uri_end) { + buffer_copy_string_len(srv->tmp_buf, protected_path, + rel_uri_end - protected_path); + protected_path = srv->tmp_buf->ptr; + } + } + + if (p->conf.hash_querystr && !buffer_is_empty(con->uri.query)) { + buffer *b = srv->tmp_buf; + if (protected_path != b->ptr) { + buffer_copy_string(b, protected_path); + } + buffer_append_string_len(b, CONST_STR_LEN("?")); + buffer_append_string_buffer(b, con->uri.query); + /* assign last in case b->ptr is reallocated */ + protected_path = b->ptr; + } + + if (!secdl_verify_mac(srv, &p->conf, protected_path, mac_str, mac_len)) { + con->http_status = 403; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "mac invalid:", + con->uri.path); + } + + return HANDLER_FINISHED; + } + + /* starting with the last / we should have relative-path to the docroot + */ + + buffer_copy_buffer(con->physical.doc_root, p->conf.doc_root); + buffer_copy_buffer(con->physical.basedir, p->conf.doc_root); + buffer_copy_string(con->physical.rel_path, rel_uri); + buffer_copy_buffer(con->physical.path, con->physical.doc_root); + buffer_append_string_buffer(con->physical.path, con->physical.rel_path); + + return HANDLER_GO_ON; +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_secdownload_plugin_init(plugin *p); +int mod_secdownload_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("secdownload"); + + p->init = mod_secdownload_init; + p->handle_physical = mod_secdownload_uri_handler; + p->set_defaults = mod_secdownload_set_defaults; + p->cleanup = mod_secdownload_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_setenv.c b/data/lighttpd/lighttpd-1.4.53/src/mod_setenv.c new file mode 100644 index 000000000..43eb7abe3 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_setenv.c @@ -0,0 +1,314 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "http_header.h" + +#include "plugin.h" + +#include <stdlib.h> +#include <string.h> + +/* plugin config for all request/connections */ + +typedef struct { + array *request_header; + array *set_request_header; + array *response_header; + array *set_response_header; + array *environment; + array *set_environment; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +typedef struct { + int handled; /* make sure that we only apply the headers once */ + plugin_config conf; +} handler_ctx; + +static handler_ctx * handler_ctx_init(void) { + handler_ctx * hctx; + + hctx = calloc(1, sizeof(*hctx)); + + hctx->handled = 0; + + return hctx; +} + +static void handler_ctx_free(handler_ctx *hctx) { + free(hctx); +} + + +/* init the plugin data */ +INIT_FUNC(mod_setenv_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_setenv_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->request_header); + array_free(s->response_header); + array_free(s->environment); + array_free(s->set_request_header); + array_free(s->set_response_header); + array_free(s->set_environment); + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_setenv_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "setenv.add-request-header", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "setenv.add-response-header", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "setenv.add-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "setenv.set-request-header", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "setenv.set-response-header", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "setenv.set-environment", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->request_header = array_init(); + s->response_header = array_init(); + s->environment = array_init(); + s->set_request_header = array_init(); + s->set_response_header = array_init(); + s->set_environment = array_init(); + + cv[0].destination = s->request_header; + cv[1].destination = s->response_header; + cv[2].destination = s->environment; + cv[3].destination = s->set_request_header; + cv[4].destination = s->set_response_header; + cv[5].destination = s->set_environment; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if ( !array_is_kvstring(s->request_header) + || !array_is_kvstring(s->response_header) + || !array_is_kvstring(s->environment) + || !array_is_kvstring(s->set_request_header) + || !array_is_kvstring(s->set_response_header) + || !array_is_kvstring(s->set_environment)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for setenv.xxxxxx; expected list of \"envvar\" => \"value\""); + return HANDLER_ERROR; + } + + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_setenv_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(request_header); + PATCH(set_request_header); + PATCH(response_header); + PATCH(set_response_header); + PATCH(environment); + PATCH(set_environment); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-request-header"))) { + PATCH(request_header); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.set-request-header"))) { + PATCH(set_request_header); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-response-header"))) { + PATCH(response_header); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.set-response-header"))) { + PATCH(set_response_header); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-environment"))) { + PATCH(environment); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.set-environment"))) { + PATCH(set_environment); + } + } + } + + return 0; +} +#undef PATCH + +URIHANDLER_FUNC(mod_setenv_uri_handler) { + plugin_data *p = p_d; + size_t k; + handler_ctx *hctx; + + if (con->plugin_ctx[p->id]) { + hctx = con->plugin_ctx[p->id]; + } else { + hctx = handler_ctx_init(); + + con->plugin_ctx[p->id] = hctx; + } + + if (hctx->handled) { + return HANDLER_GO_ON; + } + + hctx->handled = 1; + + mod_setenv_patch_connection(srv, con, p); + memcpy(&hctx->conf, &p->conf, sizeof(plugin_config)); + + for (k = 0; k < p->conf.request_header->used; k++) { + data_string *ds = (data_string *)p->conf.request_header->data[k]; + enum http_header_e id = http_header_hkey_get(CONST_BUF_LEN(ds->key)); + http_header_request_append(con, id, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); + } + + for (k = 0; k < hctx->conf.set_request_header->used; ++k) { + data_string *ds = (data_string *)hctx->conf.set_request_header->data[k]; + enum http_header_e id = http_header_hkey_get(CONST_BUF_LEN(ds->key)); + !buffer_string_is_empty(ds->value) + ? http_header_request_set(con, id, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)) + : http_header_request_unset(con, id, CONST_BUF_LEN(ds->key)); + } + + return HANDLER_GO_ON; +} + +CONNECTION_FUNC(mod_setenv_handle_request_env) { + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (NULL == hctx) return HANDLER_GO_ON; + if (hctx->handled > 1) return HANDLER_GO_ON; + hctx->handled = 2; + UNUSED(srv); + + for (size_t k = 0; k < hctx->conf.environment->used; ++k) { + data_string *ds = (data_string *)hctx->conf.environment->data[k]; + http_header_env_append(con, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); + } + + for (size_t k = 0; k < hctx->conf.set_environment->used; ++k) { + data_string *ds = (data_string *)hctx->conf.set_environment->data[k]; + http_header_env_set(con, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); + } + + return HANDLER_GO_ON; +} + +CONNECTION_FUNC(mod_setenv_handle_response_start) { + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (NULL == hctx) return HANDLER_GO_ON; + UNUSED(srv); + + for (size_t k = 0; k < hctx->conf.response_header->used; ++k) { + data_string *ds = (data_string *)hctx->conf.response_header->data[k]; + enum http_header_e id = http_header_hkey_get(CONST_BUF_LEN(ds->key)); + http_header_response_insert(con, id, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); + } + + for (size_t k = 0; k < hctx->conf.set_response_header->used; ++k) { + data_string *ds = (data_string *)hctx->conf.set_response_header->data[k]; + enum http_header_e id = http_header_hkey_get(CONST_BUF_LEN(ds->key)); + !buffer_string_is_empty(ds->value) + ? http_header_response_set(con, id, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)) + : http_header_response_unset(con, id, CONST_BUF_LEN(ds->key)); + } + + return HANDLER_GO_ON; +} + +CONNECTION_FUNC(mod_setenv_reset) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (con->plugin_ctx[p->id]) { + handler_ctx_free(con->plugin_ctx[p->id]); + con->plugin_ctx[p->id] = NULL; + } + + return HANDLER_GO_ON; +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_setenv_plugin_init(plugin *p); +int mod_setenv_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("setenv"); + + p->init = mod_setenv_init; + p->handle_uri_clean = mod_setenv_uri_handler; + p->handle_request_env = mod_setenv_handle_request_env; + p->handle_response_start = mod_setenv_handle_response_start; + p->set_defaults = mod_setenv_set_defaults; + p->cleanup = mod_setenv_free; + + p->connection_reset = mod_setenv_reset; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_simple_vhost.c b/data/lighttpd/lighttpd-1.4.53/src/mod_simple_vhost.c new file mode 100644 index 000000000..1fae140f1 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_simple_vhost.c @@ -0,0 +1,287 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "stat_cache.h" + +#include "plugin.h" + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +typedef struct { + buffer *server_root; + buffer *default_host; + buffer *document_root; + + buffer *docroot_cache_key; + buffer *docroot_cache_value; + buffer *docroot_cache_servername; + + unsigned short debug; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + buffer *doc_root; + + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +INIT_FUNC(mod_simple_vhost_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->doc_root = buffer_init(); + + return p; +} + +FREE_FUNC(mod_simple_vhost_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + if (NULL == s) continue; + + buffer_free(s->document_root); + buffer_free(s->default_host); + buffer_free(s->server_root); + + buffer_free(s->docroot_cache_key); + buffer_free(s->docroot_cache_value); + buffer_free(s->docroot_cache_servername); + + free(s); + } + + free(p->config_storage); + } + + buffer_free(p->doc_root); + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_simple_vhost_set_defaults) { + plugin_data *p = p_d; + size_t i; + + config_values_t cv[] = { + { "simple-vhost.server-root", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "simple-vhost.default-host", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "simple-vhost.document-root", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "simple-vhost.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + + s->server_root = buffer_init(); + s->default_host = buffer_init(); + s->document_root = buffer_init(); + + s->docroot_cache_key = buffer_init(); + s->docroot_cache_value = buffer_init(); + s->docroot_cache_servername = buffer_init(); + + s->debug = 0; + + cv[0].destination = s->server_root; + cv[1].destination = s->default_host; + cv[2].destination = s->document_root; + cv[3].destination = &(s->debug); + + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!buffer_string_is_empty(s->server_root)) + buffer_append_slash(s->server_root); + if (!buffer_string_is_empty(s->document_root)) + buffer_append_slash(s->document_root); + } + + return HANDLER_GO_ON; +} + +static void build_doc_root_path(buffer *out, buffer *sroot, buffer *host, buffer *droot) { + force_assert(!buffer_string_is_empty(sroot)); + buffer_copy_buffer(out, sroot); + + if (!buffer_string_is_empty(host)) { + /* a hostname has to start with a alpha-numerical character + * and must not contain a slash "/" + */ + char *dp; + if (NULL == (dp = strchr(host->ptr, ':'))) { + buffer_append_string_buffer(out, host); + } else { + buffer_append_string_len(out, host->ptr, dp - host->ptr); + } + } + + if (!buffer_string_is_empty(droot)) { + buffer_append_path_len(out, CONST_BUF_LEN(droot)); + } + else { + buffer_append_slash(out); + } +} + +static int build_doc_root(server *srv, connection *con, plugin_data *p, buffer *out, buffer *host) { + stat_cache_entry *sce = NULL; + + build_doc_root_path(out, p->conf.server_root, host, p->conf.document_root); + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, out, &sce)) { + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "sb", + strerror(errno), out); + } + return -1; + } else if (!S_ISDIR(sce->st.st_mode)) { + return -1; + } + + return 0; +} + + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_simple_vhost_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(server_root); + PATCH(default_host); + PATCH(document_root); + + PATCH(docroot_cache_key); + PATCH(docroot_cache_value); + PATCH(docroot_cache_servername); + + PATCH(debug); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("simple-vhost.server-root"))) { + PATCH(server_root); + PATCH(docroot_cache_key); + PATCH(docroot_cache_value); + PATCH(docroot_cache_servername); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("simple-vhost.default-host"))) { + PATCH(default_host); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("simple-vhost.document-root"))) { + PATCH(document_root); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("simple-vhost.debug"))) { + PATCH(debug); + } + } + } + + return 0; +} +#undef PATCH + +static handler_t mod_simple_vhost_docroot(server *srv, connection *con, void *p_data) { + plugin_data *p = p_data; + + /* + * cache the last successfull translation from hostname (authority) to docroot + * - this saves us a stat() call + * + */ + + mod_simple_vhost_patch_connection(srv, con, p); + + /* build_doc_root() requires a server_root; skip module if simple-vhost.server-root is not set + * or set to an empty string (especially don't cache any results!) + */ + if (buffer_string_is_empty(p->conf.server_root)) return HANDLER_GO_ON; + + if (!buffer_string_is_empty(p->conf.docroot_cache_key) && + !buffer_string_is_empty(con->uri.authority) && + buffer_is_equal(p->conf.docroot_cache_key, con->uri.authority)) { + /* cache hit */ + buffer_copy_buffer(con->server_name, p->conf.docroot_cache_servername); + buffer_copy_buffer(con->physical.doc_root, p->conf.docroot_cache_value); + } else { + /* build document-root */ + if (buffer_string_is_empty(con->uri.authority) || + build_doc_root(srv, con, p, p->doc_root, con->uri.authority)) { + /* not found, fallback the default-host */ + if (0 == build_doc_root(srv, con, p, + p->doc_root, + p->conf.default_host)) { + /* default host worked */ + buffer_copy_buffer(con->server_name, p->conf.default_host); + buffer_copy_buffer(con->physical.doc_root, p->doc_root); + /* do not cache default host */ + } + return HANDLER_GO_ON; + } + + /* found host */ + buffer_copy_buffer(con->server_name, con->uri.authority); + buffer_copy_buffer(con->physical.doc_root, p->doc_root); + + /* copy to cache */ + buffer_copy_buffer(p->conf.docroot_cache_key, con->uri.authority); + buffer_copy_buffer(p->conf.docroot_cache_value, p->doc_root); + buffer_copy_buffer(p->conf.docroot_cache_servername, con->server_name); + } + + return HANDLER_GO_ON; +} + + +int mod_simple_vhost_plugin_init(plugin *p); +int mod_simple_vhost_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("simple_vhost"); + + p->init = mod_simple_vhost_init; + p->set_defaults = mod_simple_vhost_set_defaults; + p->handle_docroot = mod_simple_vhost_docroot; + p->cleanup = mod_simple_vhost_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_skeleton.c b/data/lighttpd/lighttpd-1.4.53/src/mod_skeleton.c new file mode 100644 index 000000000..464f7b8de --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_skeleton.c @@ -0,0 +1,183 @@ +#include "first.h" + +#include <stdlib.h> +#include <string.h> + +#include "plugin.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "array.h" + +/** + * this is a skeleton for a lighttpd plugin + * + * just replaces every occurrence of 'skeleton' by your plugin name + * + * e.g. in vim: + * + * :%s/skeleton/myhandler/ + * + */ + + +/* plugin config for all request/connections */ + +typedef struct { + array *match; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + + +#if 0 /* (needed if module keeps state for request) */ + +typedef struct { + size_t foo; +} handler_ctx; + +static handler_ctx * handler_ctx_init() { + handler_ctx * hctx = calloc(1, sizeof(*hctx)); + force_assert(hctx); + return hctx; +} + +static void handler_ctx_free(handler_ctx *hctx) { + free(hctx); +} + +#endif + + +/* init the plugin data */ +INIT_FUNC(mod_skeleton_init) { + return calloc(1, sizeof(plugin_data)); +} + +/* destroy the plugin data */ +FREE_FUNC(mod_skeleton_free) { + plugin_data *p = p_d; + UNUSED(srv); + if (!p) return HANDLER_GO_ON; + if (p->config_storage) { + for (size_t i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + if (NULL == s) continue; + array_free(s->match); + free(s); + } + free(p->config_storage); + } + free(p); + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ +SETDEFAULTS_FUNC(mod_skeleton_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "skeleton.array", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + force_assert(p->config_storage); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s = calloc(1, sizeof(plugin_config)); + force_assert(s); + s->match = array_init(); + + cv[0].destination = s->match; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_vlist(s->match)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for skeleton.array; expected list of \"urlpath\""); + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_skeleton_patch_connection(server *srv, connection *con, plugin_data *p) { + plugin_config *s = p->config_storage[0]; + + PATCH(match); + + /* skip the first, the global context */ + for (size_t i = 1; i < srv->config_context->used; ++i) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (size_t j = 0; j < dc->value->used; ++j) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("skeleton.array"))) { + PATCH(match); + } + } + } + + return 0; +} +#undef PATCH + +URIHANDLER_FUNC(mod_skeleton_uri_handler) { + plugin_data *p = p_d; + UNUSED(srv); + + /* determine whether or not module participates in request */ + + if (con->mode != DIRECT) return HANDLER_GO_ON; + if (buffer_string_is_empty(con->uri.path)) return HANDLER_GO_ON; + + /* get module config for request */ + mod_skeleton_patch_connection(srv, con, p); + + if (NULL == array_match_value_suffix(p->conf.match, con->uri.path)) { + return HANDLER_GO_ON; + } + + /* module participates in request; business logic here */ + + con->http_status = 403; /* example: reject request with 403 Forbidden */ + return HANDLER_FINISHED; +} + +/* this function is called at dlopen() time and inits the callbacks */ +int mod_skeleton_plugin_init(plugin *p); +int mod_skeleton_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("skeleton"); + p->data = NULL; + p->init = mod_skeleton_init; + p->cleanup = mod_skeleton_free; + p->set_defaults= mod_skeleton_set_defaults; + + p->handle_uri_clean = mod_skeleton_uri_handler; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_sockproxy.c b/data/lighttpd/lighttpd-1.4.53/src/mod_sockproxy.c new file mode 100644 index 000000000..27d69ee8c --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_sockproxy.c @@ -0,0 +1,180 @@ +#include "first.h" + +#include <stdlib.h> +#include <string.h> + +#include "gw_backend.h" +typedef gw_plugin_config plugin_config; +typedef gw_plugin_data plugin_data; +typedef gw_handler_ctx handler_ctx; + +#include "base.h" +#include "array.h" +#include "buffer.h" +#include "log.h" +#include "status_counter.h" + +/** + * + * socket proxy (with optional buffering) + * + */ + +SETDEFAULTS_FUNC(mod_sockproxy_set_defaults) { + plugin_data *p = p_d; + data_unset *du; + size_t i = 0; + + config_values_t cv[] = { + { "sockproxy.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "sockproxy.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "sockproxy.balance", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + force_assert(p->config_storage); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + force_assert(s); + s->exts = NULL; + s->exts_auth = NULL; + s->exts_resp = NULL; + s->debug = 0; + + cv[0].destination = NULL; /* T_CONFIG_LOCAL */ + cv[1].destination = &(s->debug); + cv[2].destination = NULL; /* T_CONFIG_LOCAL */ + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + du = array_get_element(config->value, "sockproxy.server"); + if (!gw_set_defaults_backend(srv, (gw_plugin_data *)p, du, i, 0)) { + return HANDLER_ERROR; + } + + du = array_get_element(config->value, "sockproxy.balance"); + if (!gw_set_defaults_balance(srv, s, du)) { + return HANDLER_ERROR; + } + + /* disable check-local for all exts (default enabled) */ + if (s->exts) { /*(check after gw_set_defaults_backend())*/ + for (size_t j = 0; j < s->exts->used; ++j) { + gw_extension *ex = s->exts->exts[j]; + for (size_t n = 0; n < ex->used; ++n) { + ex->hosts[n]->check_local = 0; + } + } + } + } + + return HANDLER_GO_ON; +} + + +static handler_t sockproxy_create_env_connect(server *srv, handler_ctx *hctx) { + connection *con = hctx->remote_conn; + con->file_started = 1; + gw_set_transparent(srv, hctx); + http_response_upgrade_read_body_unknown(srv, con); + + status_counter_inc(srv, CONST_STR_LEN("sockproxy.requests")); + return HANDLER_GO_ON; +} + + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_sockproxy_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(exts); + PATCH(exts_auth); + PATCH(exts_resp); + PATCH(debug); + PATCH(ext_mapping); + PATCH(balance); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("sockproxy.server"))) { + PATCH(exts); + PATCH(exts_auth); + PATCH(exts_resp); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("sockproxy.debug"))) { + PATCH(debug); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("sockproxy.balance"))) { + PATCH(balance); + } + } + } + + return 0; +} +#undef PATCH + +static handler_t mod_sockproxy_connection_accept(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + handler_t rc; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + mod_sockproxy_patch_connection(srv, con, p); + if (NULL == p->conf.exts) return HANDLER_GO_ON; + + /*(fake con->uri.path for matching purposes in gw_check_extension())*/ + buffer_copy_string_len(con->uri.path, CONST_STR_LEN("/")); + + rc = gw_check_extension(srv, con, p, 1, 0); + if (HANDLER_GO_ON != rc) return rc; + + if (con->mode == p->id) { + handler_ctx *hctx = con->plugin_ctx[p->id]; + hctx->opts.backend = BACKEND_PROXY; + hctx->create_env = sockproxy_create_env_connect; + hctx->response = chunk_buffer_acquire(); + con->http_status = -1; /*(skip HTTP processing)*/ + } + + return HANDLER_GO_ON; +} + + +int mod_sockproxy_plugin_init(plugin *p); +int mod_sockproxy_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("sockproxy"); + + p->init = gw_init; + p->cleanup = gw_free; + p->set_defaults = mod_sockproxy_set_defaults; + p->connection_reset = gw_connection_reset; + p->handle_connection_accept= mod_sockproxy_connection_accept; + p->handle_subrequest = gw_handle_subrequest; + p->handle_trigger = gw_handle_trigger; + p->handle_waitpid = gw_handle_waitpid_cb; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_ssi.c b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi.c new file mode 100644 index 000000000..f05f8a6e8 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi.c @@ -0,0 +1,1365 @@ +#include "first.h" + +#include "base.h" +#include "fdevent.h" +#include "log.h" +#include "buffer.h" +#include "http_header.h" + +#include "plugin.h" + +#include "response.h" + +#include "mod_ssi.h" + +#include "sys-socket.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include "sys-strings.h" +#include <sys/wait.h> + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> + +#ifdef HAVE_PWD_H +# include <pwd.h> +#endif + +#ifdef HAVE_SYS_FILIO_H +# include <sys/filio.h> +#endif + +#include "etag.h" + +static handler_ctx * handler_ctx_init(plugin_data *p) { + handler_ctx *hctx = calloc(1, sizeof(*hctx)); + force_assert(hctx); + hctx->timefmt = p->timefmt; + hctx->stat_fn = p->stat_fn; + hctx->ssi_vars = p->ssi_vars; + hctx->ssi_cgi_env = p->ssi_cgi_env; + memcpy(&hctx->conf, &p->conf, sizeof(plugin_config)); + return hctx; +} + +static void handler_ctx_free(handler_ctx *hctx) { + free(hctx); +} + +/* The newest modified time of included files for include statement */ +static volatile time_t include_file_last_mtime = 0; + +/* init the plugin data */ +INIT_FUNC(mod_ssi_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->timefmt = buffer_init(); + p->stat_fn = buffer_init(); + + p->ssi_vars = array_init(); + p->ssi_cgi_env = array_init(); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_ssi_free) { + plugin_data *p = p_d; + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->ssi_extension); + buffer_free(s->content_type); + + free(s); + } + free(p->config_storage); + } + + array_free(p->ssi_vars); + array_free(p->ssi_cgi_env); + buffer_free(p->timefmt); + buffer_free(p->stat_fn); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_ssi_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "ssi.extension", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "ssi.content-type", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "ssi.conditional-requests", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "ssi.exec", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "ssi.recursion-max", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->ssi_extension = array_init(); + s->content_type = buffer_init(); + s->conditional_requests = 0; + s->ssi_exec = 1; + s->ssi_recursion_max = 0; + + cv[0].destination = s->ssi_extension; + cv[1].destination = s->content_type; + cv[2].destination = &(s->conditional_requests); + cv[3].destination = &(s->ssi_exec); + cv[4].destination = &(s->ssi_recursion_max); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_vlist(s->ssi_extension)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for ssi.extension; expected list of \"ext\""); + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + + +static int ssi_env_add(void *venv, const char *key, size_t klen, const char *val, size_t vlen) { + array_insert_key_value((array *)venv, key, klen, val, vlen); + return 0; +} + +static int build_ssi_cgi_vars(server *srv, connection *con, handler_ctx *p) { + http_cgi_opts opts = { 0, 0, NULL, NULL }; + /* temporarily remove Authorization from request headers + * so that Authorization does not end up in SSI environment */ + buffer *vb_auth = http_header_request_get(con, HTTP_HEADER_AUTHORIZATION, CONST_STR_LEN("Authorization")); + buffer b_auth; + if (vb_auth) { + memcpy(&b_auth, vb_auth, sizeof(buffer)); + memset(vb_auth, 0, sizeof(buffer)); + } + + array_reset_data_strings(p->ssi_cgi_env); + + if (0 != http_cgi_headers(srv, con, &opts, ssi_env_add, p->ssi_cgi_env)) { + con->http_status = 400; + return -1; + } + + if (vb_auth) { + memcpy(vb_auth, &b_auth, sizeof(buffer)); + } + + return 0; +} + +static int mod_ssi_process_file(server *srv, connection *con, handler_ctx *p, struct stat *st); + +static int process_ssi_stmt(server *srv, connection *con, handler_ctx *p, const char **l, size_t n, struct stat *st) { + + /** + * <!--#element attribute=value attribute=value ... --> + * + * config DONE + * errmsg -- missing + * sizefmt DONE + * timefmt DONE + * echo DONE + * var DONE + * encoding -- missing + * exec DONE + * cgi -- never + * cmd DONE + * fsize DONE + * file DONE + * virtual DONE + * flastmod DONE + * file DONE + * virtual DONE + * include DONE + * file DONE + * virtual DONE + * printenv DONE + * set DONE + * var DONE + * value DONE + * + * if DONE + * elif DONE + * else DONE + * endif DONE + * + * + * expressions + * AND, OR DONE + * comp DONE + * ${...} -- missing + * $... DONE + * '...' DONE + * ( ... ) DONE + * + * + * + * ** all DONE ** + * DATE_GMT + * The current date in Greenwich Mean Time. + * DATE_LOCAL + * The current date in the local time zone. + * DOCUMENT_NAME + * The filename (excluding directories) of the document requested by the user. + * DOCUMENT_URI + * The (%-decoded) URL path of the document requested by the user. Note that in the case of nested include files, this is not then URL for the current document. + * LAST_MODIFIED + * The last modification date of the document requested by the user. + * USER_NAME + * Contains the owner of the file which included it. + * + */ + + size_t i, ssicmd = 0; + char buf[255]; + buffer *b = NULL; + + static const struct { + const char *var; + enum { SSI_UNSET, SSI_ECHO, SSI_FSIZE, SSI_INCLUDE, SSI_FLASTMOD, + SSI_CONFIG, SSI_PRINTENV, SSI_SET, SSI_IF, SSI_ELIF, + SSI_ELSE, SSI_ENDIF, SSI_EXEC, SSI_COMMENT } type; + } ssicmds[] = { + { "echo", SSI_ECHO }, + { "include", SSI_INCLUDE }, + { "flastmod", SSI_FLASTMOD }, + { "fsize", SSI_FSIZE }, + { "config", SSI_CONFIG }, + { "printenv", SSI_PRINTENV }, + { "set", SSI_SET }, + { "if", SSI_IF }, + { "elif", SSI_ELIF }, + { "endif", SSI_ENDIF }, + { "else", SSI_ELSE }, + { "exec", SSI_EXEC }, + { "comment", SSI_COMMENT }, + + { NULL, SSI_UNSET } + }; + + for (i = 0; ssicmds[i].var; i++) { + if (0 == strcmp(l[1], ssicmds[i].var)) { + ssicmd = ssicmds[i].type; + break; + } + } + + switch(ssicmd) { + case SSI_ECHO: { + /* echo */ + int var = 0; + /* int enc = 0; */ + const char *var_val = NULL; + + static const struct { + const char *var; + enum { + SSI_ECHO_UNSET, + SSI_ECHO_DATE_GMT, + SSI_ECHO_DATE_LOCAL, + SSI_ECHO_DOCUMENT_NAME, + SSI_ECHO_DOCUMENT_URI, + SSI_ECHO_LAST_MODIFIED, + SSI_ECHO_USER_NAME, + SSI_ECHO_SCRIPT_URI, + SSI_ECHO_SCRIPT_URL, + } type; + } echovars[] = { + { "DATE_GMT", SSI_ECHO_DATE_GMT }, + { "DATE_LOCAL", SSI_ECHO_DATE_LOCAL }, + { "DOCUMENT_NAME", SSI_ECHO_DOCUMENT_NAME }, + { "DOCUMENT_URI", SSI_ECHO_DOCUMENT_URI }, + { "LAST_MODIFIED", SSI_ECHO_LAST_MODIFIED }, + { "USER_NAME", SSI_ECHO_USER_NAME }, + { "SCRIPT_URI", SSI_ECHO_SCRIPT_URI }, + { "SCRIPT_URL", SSI_ECHO_SCRIPT_URL }, + + { NULL, SSI_ECHO_UNSET } + }; + +/* + static const struct { + const char *var; + enum { SSI_ENC_UNSET, SSI_ENC_URL, SSI_ENC_NONE, SSI_ENC_ENTITY } type; + } encvars[] = { + { "url", SSI_ENC_URL }, + { "none", SSI_ENC_NONE }, + { "entity", SSI_ENC_ENTITY }, + + { NULL, SSI_ENC_UNSET } + }; +*/ + + for (i = 2; i < n; i += 2) { + if (0 == strcmp(l[i], "var")) { + int j; + + var_val = l[i+1]; + + for (j = 0; echovars[j].var; j++) { + if (0 == strcmp(l[i+1], echovars[j].var)) { + var = echovars[j].type; + break; + } + } + } else if (0 == strcmp(l[i], "encoding")) { +/* + int j; + + for (j = 0; encvars[j].var; j++) { + if (0 == strcmp(l[i+1], encvars[j].var)) { + enc = encvars[j].type; + break; + } + } +*/ + } else { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ssi: unknown attribute for ", + l[1], l[i]); + } + } + + if (p->if_is_false) break; + + if (!var_val) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ssi: ", + l[1], "var is missing"); + break; + } + + switch(var) { + case SSI_ECHO_USER_NAME: { + struct passwd *pw; + + b = srv->tmp_buf; +#ifdef HAVE_PWD_H + if (NULL == (pw = getpwuid(st->st_uid))) { + buffer_copy_int(b, st->st_uid); + } else { + buffer_copy_string(b, pw->pw_name); + } +#else + buffer_copy_int(b, st->st_uid); +#endif + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(b)); + break; + } + case SSI_ECHO_LAST_MODIFIED: { + time_t t = st->st_mtime; + + if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)")); + } else { + chunkqueue_append_mem(con->write_queue, buf, strlen(buf)); + } + break; + } + case SSI_ECHO_DATE_LOCAL: { + time_t t = time(NULL); + + if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)")); + } else { + chunkqueue_append_mem(con->write_queue, buf, strlen(buf)); + } + break; + } + case SSI_ECHO_DATE_GMT: { + time_t t = time(NULL); + + if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, gmtime(&t))) { + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)")); + } else { + chunkqueue_append_mem(con->write_queue, buf, strlen(buf)); + } + break; + } + case SSI_ECHO_DOCUMENT_NAME: { + char *sl; + + if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) { + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->physical.path)); + } else { + chunkqueue_append_mem(con->write_queue, sl + 1, strlen(sl + 1)); + } + break; + } + case SSI_ECHO_DOCUMENT_URI: { + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.path)); + break; + } + case SSI_ECHO_SCRIPT_URI: { + if (!buffer_string_is_empty(con->uri.scheme) && !buffer_string_is_empty(con->uri.authority)) { + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.scheme)); + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("://")); + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.authority)); + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->request.uri)); + if (!buffer_string_is_empty(con->uri.query)) { + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("?")); + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.query)); + } + } + break; + } + case SSI_ECHO_SCRIPT_URL: { + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->request.uri)); + if (!buffer_string_is_empty(con->uri.query)) { + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("?")); + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(con->uri.query)); + } + break; + } + default: { + data_string *ds; + /* check if it is a cgi-var or a ssi-var */ + + if (NULL != (ds = (data_string *)array_get_element_klen(p->ssi_cgi_env, var_val, strlen(var_val))) || + NULL != (ds = (data_string *)array_get_element_klen(p->ssi_vars, var_val, strlen(var_val)))) { + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(ds->value)); + } else { + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)")); + } + + break; + } + } + break; + } + case SSI_INCLUDE: + case SSI_FLASTMOD: + case SSI_FSIZE: { + const char * file_path = NULL, *virt_path = NULL; + struct stat stb; + char *sl; + + for (i = 2; i < n; i += 2) { + if (0 == strcmp(l[i], "file")) { + file_path = l[i+1]; + } else if (0 == strcmp(l[i], "virtual")) { + virt_path = l[i+1]; + } else { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ssi: unknown attribute for ", + l[1], l[i]); + } + } + + if (!file_path && !virt_path) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ssi: ", + l[1], "file or virtual are missing"); + break; + } + + if (file_path && virt_path) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ssi: ", + l[1], "only one of file and virtual is allowed here"); + break; + } + + + if (p->if_is_false) break; + + if (file_path) { + /* current doc-root */ + if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) { + buffer_copy_string_len(p->stat_fn, CONST_STR_LEN("/")); + } else { + buffer_copy_string_len(p->stat_fn, con->physical.path->ptr, sl - con->physical.path->ptr + 1); + } + + buffer_copy_string(srv->tmp_buf, file_path); + buffer_urldecode_path(srv->tmp_buf); + if (!buffer_is_valid_UTF8(srv->tmp_buf)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "SSI invalid UTF-8 after url-decode:", srv->tmp_buf); + break; + } + buffer_path_simplify(srv->tmp_buf, srv->tmp_buf); + buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); + } else { + /* virtual */ + size_t remain; + + if (virt_path[0] == '/') { + buffer_copy_string(srv->tmp_buf, virt_path); + } else { + /* there is always a / */ + sl = strrchr(con->uri.path->ptr, '/'); + + buffer_copy_string_len(srv->tmp_buf, con->uri.path->ptr, sl - con->uri.path->ptr + 1); + buffer_append_string(srv->tmp_buf, virt_path); + } + + buffer_urldecode_path(srv->tmp_buf); + if (!buffer_is_valid_UTF8(srv->tmp_buf)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "SSI invalid UTF-8 after url-decode:", srv->tmp_buf); + break; + } + buffer_path_simplify(srv->tmp_buf, srv->tmp_buf); + + /* we have an uri */ + + /* Destination physical path (similar to code in mod_webdav.c) + * src con->physical.path might have been remapped with mod_alias, mod_userdir. + * (but neither modifies con->physical.rel_path) + * Find matching prefix to support relative paths to current physical path. + * Aliasing of paths underneath current con->physical.basedir might not work. + * Likewise, mod_rewrite URL rewriting might thwart this comparison. + * Use mod_redirect instead of mod_alias to remap paths *under* this basedir. + * Use mod_redirect instead of mod_rewrite on *any* parts of path to basedir. + * (Related, use mod_auth to protect this basedir, but avoid attempting to + * use mod_auth on paths underneath this basedir, as target path is not + * validated with mod_auth) + */ + + /* find matching URI prefix + * check if remaining con->physical.rel_path matches suffix + * of con->physical.basedir so that we can use it to + * remap Destination physical path */ + { + const char *sep, *sep2; + sep = con->uri.path->ptr; + sep2 = srv->tmp_buf->ptr; + for (i = 0; sep[i] && sep[i] == sep2[i]; ++i) ; + while (i != 0 && sep[--i] != '/') ; /* find matching directory path */ + } + if (con->conf.force_lowercase_filenames) { + buffer_to_lower(srv->tmp_buf); + } + remain = buffer_string_length(con->uri.path) - i; + if (!con->conf.force_lowercase_filenames + ? buffer_is_equal_right_len(con->physical.path, con->physical.rel_path, remain) + :(buffer_string_length(con->physical.path) >= remain + && 0 == strncasecmp(con->physical.path->ptr+buffer_string_length(con->physical.path)-remain, con->physical.rel_path->ptr+i, remain))) { + buffer_copy_string_len(p->stat_fn, con->physical.path->ptr, buffer_string_length(con->physical.path)-remain); + buffer_append_string_len(p->stat_fn, srv->tmp_buf->ptr+i, buffer_string_length(srv->tmp_buf)-i); + } else { + /* unable to perform physical path remap here; + * assume doc_root/rel_path and no remapping */ + buffer_copy_buffer(p->stat_fn, con->physical.doc_root); + buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); + } + } + + if (0 == stat(p->stat_fn->ptr, &stb)) { + time_t t = stb.st_mtime; + + switch (ssicmd) { + case SSI_FSIZE: + b = srv->tmp_buf; + if (p->sizefmt) { + int j = 0; + const char *abr[] = { " B", " kB", " MB", " GB", " TB", NULL }; + + off_t s = stb.st_size; + + for (j = 0; s > 1024 && abr[j+1]; s /= 1024, j++); + + buffer_copy_int(b, s); + buffer_append_string(b, abr[j]); + } else { + buffer_copy_int(b, stb.st_size); + } + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(b)); + break; + case SSI_FLASTMOD: + if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(none)")); + } else { + chunkqueue_append_mem(con->write_queue, buf, strlen(buf)); + } + break; + case SSI_INCLUDE: + /* Keep the newest mtime of included files */ + if (stb.st_mtime > include_file_last_mtime) + include_file_last_mtime = stb.st_mtime; + + if (file_path || 0 == p->conf.ssi_recursion_max) { + /* don't process if #include file="..." is used */ + chunkqueue_append_file(con->write_queue, p->stat_fn, 0, stb.st_size); + } else { + buffer *upsave, *ppsave, *prpsave; + + /* only allow predefined recursion depth */ + if (p->ssi_recursion_depth >= p->conf.ssi_recursion_max) { + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(error: include directives recurse deeper than pre-defined ssi.recursion-max)")); + break; + } + + /* prevents simple infinite loop */ + if (buffer_is_equal(con->physical.path, p->stat_fn)) { + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("(error: include directives create an infinite loop)")); + break; + } + + /* save and restore con->physical.path, con->physical.rel_path, and con->uri.path around include + * + * srv->tmp_buf contains url-decoded, path-simplified, and lowercased (if con->conf.force_lowercase) uri path of target. + * con->uri.path and con->physical.rel_path are set to the same since we only operate on filenames here, + * not full re-run of all modules for subrequest */ + upsave = con->uri.path; + ppsave = con->physical.path; + prpsave = con->physical.rel_path; + + con->physical.path = p->stat_fn; + p->stat_fn = buffer_init(); + + con->uri.path = con->physical.rel_path = buffer_init_buffer(srv->tmp_buf); + + /*(ignore return value; muddle along as best we can if error occurs)*/ + ++p->ssi_recursion_depth; + mod_ssi_process_file(srv, con, p, &stb); + --p->ssi_recursion_depth; + + buffer_free(con->uri.path); + con->uri.path = upsave; + con->physical.rel_path = prpsave; + + buffer_free(p->stat_fn); + p->stat_fn = con->physical.path; + con->physical.path = ppsave; + } + + break; + } + } else { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "ssi: stating failed ", + p->stat_fn, strerror(errno)); + } + break; + } + case SSI_SET: { + const char *key = NULL, *val = NULL; + for (i = 2; i < n; i += 2) { + if (0 == strcmp(l[i], "var")) { + key = l[i+1]; + } else if (0 == strcmp(l[i], "value")) { + val = l[i+1]; + } else { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ssi: unknown attribute for ", + l[1], l[i]); + } + } + + if (p->if_is_false) break; + + if (key && val) { + array_insert_key_value(p->ssi_vars, key, strlen(key), val, strlen(val)); + } else if (key || val) { + log_error_write(srv, __FILE__, __LINE__, "sSSss", + "ssi: var and value have to be set in <!--#set", l[1], "=", l[2], "-->"); + } else { + log_error_write(srv, __FILE__, __LINE__, "s", + "ssi: var and value have to be set in <!--#set var=... value=... -->"); + } + break; + } + case SSI_CONFIG: + if (p->if_is_false) break; + + for (i = 2; i < n; i += 2) { + if (0 == strcmp(l[i], "timefmt")) { + buffer_copy_string(p->timefmt, l[i+1]); + } else if (0 == strcmp(l[i], "sizefmt")) { + if (0 == strcmp(l[i+1], "abbrev")) { + p->sizefmt = 1; + } else if (0 == strcmp(l[i+1], "bytes")) { + p->sizefmt = 0; + } else { + log_error_write(srv, __FILE__, __LINE__, "sssss", + "ssi: unknown value for attribute '", + l[i], + "' for ", + l[1], l[i+1]); + } + } else { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ssi: unknown attribute for ", + l[1], l[i]); + } + } + break; + case SSI_PRINTENV: + if (p->if_is_false) break; + + b = srv->tmp_buf; + buffer_clear(b); + for (i = 0; i < p->ssi_vars->used; i++) { + data_string *ds = (data_string *)p->ssi_vars->data[p->ssi_vars->sorted[i]]; + + buffer_append_string_buffer(b, ds->key); + buffer_append_string_len(b, CONST_STR_LEN("=")); + buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + } + for (i = 0; i < p->ssi_cgi_env->used; i++) { + data_string *ds = (data_string *)p->ssi_cgi_env->data[p->ssi_cgi_env->sorted[i]]; + + buffer_append_string_buffer(b, ds->key); + buffer_append_string_len(b, CONST_STR_LEN("=")); + buffer_append_string_encoded(b, CONST_BUF_LEN(ds->value), ENCODING_MINIMAL_XML); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + } + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(b)); + break; + case SSI_EXEC: { + const char *cmd = NULL; + pid_t pid; + chunk *c; + char *args[4]; + + if (!p->conf.ssi_exec) { /* <!--#exec ... --> disabled by config */ + break; + } + + for (i = 2; i < n; i += 2) { + if (0 == strcmp(l[i], "cmd")) { + cmd = l[i+1]; + } else { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ssi: unknown attribute for ", + l[1], l[i]); + } + } + + if (p->if_is_false) break; + + /* + * as exec is assumed evil it is implemented synchronously + */ + + if (!cmd) break; + + /* send cmd output to a temporary file */ + if (0 != chunkqueue_append_mem_to_tempfile(srv, con->write_queue, "", 0)) break; + c = con->write_queue->last; + + *(const char **)&args[0] = "/bin/sh"; + *(const char **)&args[1] = "-c"; + *(const char **)&args[2] = cmd; + args[3] = NULL; + + /*(expects STDIN_FILENO open to /dev/null)*/ + pid = fdevent_fork_execve(args[0], args, NULL, -1, c->file.fd, -1, -1); + if (-1 == pid) { + log_error_write(srv, __FILE__, __LINE__, "sss", "spawning exec failed:", strerror(errno), cmd); + } else { + struct stat stb; + int status; + + /* wait for the client to end */ + /* NOTE: synchronous; blocks entire lighttpd server */ + + /* + * OpenBSD and Solaris send a EINTR on SIGCHILD even if we ignore it + */ + while (-1 == waitpid(pid, &status, 0)) { + if (errno != EINTR) { + log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed:", strerror(errno)); + break; + } + } + if (!WIFEXITED(status)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "process exited abnormally:", cmd); + } + if (0 == fstat(c->file.fd, &stb)) { + c->file.length = stb.st_size; + } + } + + break; + } + case SSI_IF: { + const char *expr = NULL; + + for (i = 2; i < n; i += 2) { + if (0 == strcmp(l[i], "expr")) { + expr = l[i+1]; + } else { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ssi: unknown attribute for ", + l[1], l[i]); + } + } + + if (!expr) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ssi: ", + l[1], "expr missing"); + break; + } + + if ((!p->if_is_false) && + ((p->if_is_false_level == 0) || + (p->if_level < p->if_is_false_level))) { + switch (ssi_eval_expr(srv, con, p, expr)) { + case -1: + case 0: + p->if_is_false = 1; + p->if_is_false_level = p->if_level; + break; + case 1: + p->if_is_false = 0; + break; + } + } + + p->if_level++; + + break; + } + case SSI_ELSE: + p->if_level--; + + if (p->if_is_false) { + if ((p->if_level == p->if_is_false_level) && + (p->if_is_false_endif == 0)) { + p->if_is_false = 0; + } + } else { + p->if_is_false = 1; + + p->if_is_false_level = p->if_level; + } + p->if_level++; + + break; + case SSI_ELIF: { + const char *expr = NULL; + for (i = 2; i < n; i += 2) { + if (0 == strcmp(l[i], "expr")) { + expr = l[i+1]; + } else { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ssi: unknown attribute for ", + l[1], l[i]); + } + } + + if (!expr) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "ssi: ", + l[1], "expr missing"); + break; + } + + p->if_level--; + + if (p->if_level == p->if_is_false_level) { + if ((p->if_is_false) && + (p->if_is_false_endif == 0)) { + switch (ssi_eval_expr(srv, con, p, expr)) { + case -1: + case 0: + p->if_is_false = 1; + p->if_is_false_level = p->if_level; + break; + case 1: + p->if_is_false = 0; + break; + } + } else { + p->if_is_false = 1; + p->if_is_false_level = p->if_level; + p->if_is_false_endif = 1; + } + } + + p->if_level++; + + break; + } + case SSI_ENDIF: + p->if_level--; + + if (p->if_level == p->if_is_false_level) { + p->if_is_false = 0; + p->if_is_false_endif = 0; + } + + break; + case SSI_COMMENT: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "ss", + "ssi: unknown ssi-command:", + l[1]); + break; + } + + return 0; + +} + +static int mod_ssi_parse_ssi_stmt_value(const char * const s, const int len) { + int n; + const int c = (s[0] == '"' ? '"' : s[0] == '\'' ? '\'' : 0); + if (0 != c) { + for (n = 1; n < len; ++n) { + if (s[n] == c) return n+1; + if (s[n] == '\\') { + if (n+1 == len) return 0; /* invalid */ + ++n; + } + } + return 0; /* invalid */ + } else { + for (n = 0; n < len; ++n) { + if (isspace(s[n])) return n; + if (s[n] == '\\') { + if (n+1 == len) return 0; /* invalid */ + ++n; + } + } + return n; + } +} + +static int mod_ssi_parse_ssi_stmt_offlen(int o[10], const char * const s, const int len) { + + /** + * <!--#element attribute=value attribute=value ... --> + */ + + /* s must begin "<!--#" and must end with "-->" */ + int n = 5; + o[0] = n; + for (; light_isalpha(s[n]); ++n) ; /*(n = 5 to begin after "<!--#")*/ + o[1] = n - o[0]; + if (0 == o[1]) return -1; /* empty token */ + + if (n+3 == len) return 2; /* token only; no params */ + if (!isspace(s[n])) return -1; + do { ++n; } while (isspace(s[n])); /* string ends "-->", so n < len */ + if (n+3 == len) return 2; /* token only; no params */ + + o[2] = n; + for (; light_isalpha(s[n]); ++n) ; + o[3] = n - o[2]; + if (0 == o[3] || s[n++] != '=') return -1; + + o[4] = n; + o[5] = mod_ssi_parse_ssi_stmt_value(s+n, len-n-3); + if (0 == o[5]) return -1; /* empty or invalid token */ + n += o[5]; + + if (n+3 == len) return 6; /* token and one param */ + if (!isspace(s[n])) return -1; + do { ++n; } while (isspace(s[n])); /* string ends "-->", so n < len */ + if (n+3 == len) return 6; /* token and one param */ + + o[6] = n; + for (; light_isalpha(s[n]); ++n) ; + o[7] = n - o[6]; + if (0 == o[7] || s[n++] != '=') return -1; + + o[8] = n; + o[9] = mod_ssi_parse_ssi_stmt_value(s+n, len-n-3); + if (0 == o[9]) return -1; /* empty or invalid token */ + n += o[9]; + + if (n+3 == len) return 10; /* token and two params */ + if (!isspace(s[n])) return -1; + do { ++n; } while (isspace(s[n])); /* string ends "-->", so n < len */ + if (n+3 == len) return 10; /* token and two params */ + return -1; +} + +static void mod_ssi_parse_ssi_stmt(server *srv, connection *con, handler_ctx *p, char *s, int len, struct stat *st) { + + /** + * <!--#element attribute=value attribute=value ... --> + */ + + int o[10]; + int m; + const int n = mod_ssi_parse_ssi_stmt_offlen(o, s, len); + char *l[6] = { s, NULL, NULL, NULL, NULL, NULL }; + if (-1 == n) { + /* ignore <!--#comment ... --> */ + if (len >= 16 + && 0 == memcmp(s+5, "comment", sizeof("comment")-1) + && (s[12] == ' ' || s[12] == '\t')) + return; + /* XXX: perhaps emit error comment instead of invalid <!--#...--> code to client */ + chunkqueue_append_mem(con->write_queue, s, len); /* append stmt as-is */ + return; + } + + #if 0 + /* dup s and then modify s */ + /*(l[0] is no longer used; was previously used in only one place for error reporting)*/ + l[0] = malloc((size_t)(len+1)); + memcpy(l[0], s, (size_t)len); + (l[0])[len] = '\0'; + #endif + + /* modify s in-place to split string into arg tokens */ + for (m = 0; m < n; m += 2) { + char *ptr = s+o[m]; + switch (*ptr) { + case '"': + case '\'': (++ptr)[o[m+1]-2] = '\0'; break; + default: ptr[o[m+1]] = '\0'; break; + } + l[1+(m>>1)] = ptr; + if (m == 4 || m == 8) { + /* XXX: removing '\\' escapes from param value would be + * the right thing to do, but would potentially change + * current behavior, e.g. <!--#exec cmd=... --> */ + } + } + + process_ssi_stmt(srv, con, p, (const char **)l, 1+(n>>1), st); + + #if 0 + free(l[0]); + #endif +} + +static int mod_ssi_stmt_len(const char *s, const int len) { + /* s must begin "<!--#" */ + int n, sq = 0, dq = 0, bs = 0; + for (n = 5; n < len; ++n) { /*(n = 5 to begin after "<!--#")*/ + switch (s[n]) { + default: + break; + case '-': + if (!sq && !dq && n+2 < len && s[n+1] == '-' && s[n+2] == '>') return n+3; /* found end of stmt */ + break; + case '"': + if (!sq && (!dq || !bs)) dq = !dq; + break; + case '\'': + if (!dq && (!sq || !bs)) sq = !sq; + break; + case '\\': + if (sq || dq) bs = !bs; + break; + } + } + return 0; /* incomplete directive "<!--#...-->" */ +} + +static void mod_ssi_read_fd(server *srv, connection *con, handler_ctx *p, struct stat *st, int fd) { + ssize_t rd; + size_t offset, pretag; + size_t bufsz = 8192; + char *buf = malloc(bufsz); /* allocate to reduce chance of stack exhaustion upon deep recursion */ + force_assert(buf); + + offset = 0; + pretag = 0; + while (0 < (rd = read(fd, buf+offset, bufsz-offset))) { + char *s; + size_t prelen = 0, len; + offset += (size_t)rd; + for (; (s = memchr(buf+prelen, '<', offset-prelen)); ++prelen) { + prelen = s - buf; + if (prelen + 5 <= offset) { /*("<!--#" is 5 chars)*/ + if (0 != memcmp(s+1, CONST_STR_LEN("!--#"))) continue; /* loop to loop for next '<' */ + + if (prelen - pretag && !p->if_is_false) { + chunkqueue_append_mem(con->write_queue, buf+pretag, prelen-pretag); + } + + len = mod_ssi_stmt_len(buf+prelen, offset-prelen); + if (len) { /* num of chars to be consumed */ + mod_ssi_parse_ssi_stmt(srv, con, p, buf+prelen, len, st); + prelen += (len - 1); /* offset to '>' at end of SSI directive; incremented at top of loop */ + pretag = prelen + 1; + if (pretag == offset) { + offset = pretag = 0; + break; + } + } else if (0 == prelen && offset == bufsz) { /*(full buf)*/ + /* SSI statement is way too long + * NOTE: skipping this buf will expose *the rest* of this SSI statement */ + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("<!-- [an error occurred: directive too long] ")); + /* check if buf ends with "-" or "--" which might be part of "-->" + * (buf contains at least 5 chars for "<!--#") */ + if (buf[offset-2] == '-' && buf[offset-1] == '-') { + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("--")); + } else if (buf[offset-1] == '-') { + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("-")); + } + offset = pretag = 0; + break; + } else { /* incomplete directive "<!--#...-->" */ + memmove(buf, buf+prelen, (offset -= prelen)); + pretag = 0; + break; + } + } else if (prelen + 1 == offset || 0 == memcmp(s+1, "!--", offset - prelen - 1)) { + if (prelen - pretag && !p->if_is_false) { + chunkqueue_append_mem(con->write_queue, buf+pretag, prelen-pretag); + } + memcpy(buf, buf+prelen, (offset -= prelen)); + pretag = 0; + break; + } + /* loop to look for next '<' */ + } + if (offset == bufsz) { + if (!p->if_is_false) { + chunkqueue_append_mem(con->write_queue, buf+pretag, offset-pretag); + } + offset = pretag = 0; + } + } + + if (0 != rd) { + log_error_write(srv, __FILE__, __LINE__, "SsB", "read(): ", strerror(errno), con->physical.path); + } + + if (offset - pretag) { + /* copy remaining data in buf */ + if (!p->if_is_false) { + chunkqueue_append_mem(con->write_queue, buf+pretag, offset-pretag); + } + } + + free(buf); +} + + +/* don't want to block when open()ing a fifo */ +#if defined(O_NONBLOCK) +# define FIFO_NONBLOCK O_NONBLOCK +#else +# define FIFO_NONBLOCK 0 +#endif + +static int mod_ssi_process_file(server *srv, connection *con, handler_ctx *p, struct stat *st) { + int fd = open(con->physical.path->ptr, O_RDONLY | FIFO_NONBLOCK); + if (-1 == fd) { + log_error_write(srv, __FILE__, __LINE__, "SsB", "open(): ", + strerror(errno), con->physical.path); + return -1; + } + + if (0 != fstat(fd, st)) { + log_error_write(srv, __FILE__, __LINE__, "SsB", "fstat(): ", + strerror(errno), con->physical.path); + close(fd); + return -1; + } + + mod_ssi_read_fd(srv, con, p, st, fd); + + close(fd); + return 0; +} + + +static int mod_ssi_handle_request(server *srv, connection *con, handler_ctx *p) { + struct stat st; + + /* get a stream to the file */ + + array_reset_data_strings(p->ssi_vars); + array_reset_data_strings(p->ssi_cgi_env); + buffer_copy_string_len(p->timefmt, CONST_STR_LEN("%a, %d %b %Y %H:%M:%S %Z")); + build_ssi_cgi_vars(srv, con, p); + + /* Reset the modified time of included files */ + include_file_last_mtime = 0; + + if (mod_ssi_process_file(srv, con, p, &st)) return -1; + + con->file_started = 1; + con->file_finished = 1; + + if (buffer_string_is_empty(p->conf.content_type)) { + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); + } else { + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->conf.content_type)); + } + + if (p->conf.conditional_requests) { + /* Generate "ETag" & "Last-Modified" headers */ + buffer *mtime = NULL; + + /* use most recently modified include file for ETag and Last-Modified */ + if (st.st_mtime < include_file_last_mtime) + st.st_mtime = include_file_last_mtime; + + etag_create(con->physical.etag, &st, con->etag_flags); + etag_mutate(con->physical.etag, con->physical.etag); + http_header_response_set(con, HTTP_HEADER_ETAG, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); + + mtime = strftime_cache_get(srv, st.st_mtime); + http_header_response_set(con, HTTP_HEADER_LAST_MODIFIED, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); + + if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { + /* ok, the client already has our content, + * no need to send it again */ + + chunkqueue_reset(con->write_queue); + } + } + + /* Reset the modified time of included files */ + include_file_last_mtime = 0; + + /* reset physical.path */ + buffer_reset(con->physical.path); + + return 0; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_ssi_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(ssi_extension); + PATCH(content_type); + PATCH(conditional_requests); + PATCH(ssi_exec); + PATCH(ssi_recursion_max); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.extension"))) { + PATCH(ssi_extension); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.content-type"))) { + PATCH(content_type); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.conditional-requests"))) { + PATCH(conditional_requests); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.exec"))) { + PATCH(ssi_exec); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.recursion-max"))) { + PATCH(ssi_recursion_max); + } + } + } + + return 0; +} +#undef PATCH + +URIHANDLER_FUNC(mod_ssi_physical_path) { + plugin_data *p = p_d; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; + + mod_ssi_patch_connection(srv, con, p); + + if (array_match_value_suffix(p->conf.ssi_extension, con->physical.path)) { + con->plugin_ctx[p->id] = handler_ctx_init(p); + con->mode = p->id; + } + + return HANDLER_GO_ON; +} + +SUBREQUEST_FUNC(mod_ssi_handle_subrequest) { + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (NULL == hctx) return HANDLER_GO_ON; + if (con->mode != p->id) return HANDLER_GO_ON; /* not my job */ + /* + * NOTE: if mod_ssi modified to use fdevents, HANDLER_WAIT_FOR_EVENT, + * instead of blocking to completion, then hctx->timefmt, hctx->ssi_vars, + * and hctx->ssi_cgi_env should be allocated and cleaned up per request. + */ + + /* handle ssi-request */ + + if (mod_ssi_handle_request(srv, con, hctx)) { + /* on error */ + con->http_status = 500; + con->mode = DIRECT; + } + + return HANDLER_FINISHED; +} + +static handler_t mod_ssi_connection_reset(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (hctx) { + handler_ctx_free(hctx); + con->plugin_ctx[p->id] = NULL; + } + + UNUSED(srv); + return HANDLER_GO_ON; +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_ssi_plugin_init(plugin *p); +int mod_ssi_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("ssi"); + + p->init = mod_ssi_init; + p->handle_subrequest_start = mod_ssi_physical_path; + p->handle_subrequest = mod_ssi_handle_subrequest; + p->connection_reset = mod_ssi_connection_reset; + p->set_defaults = mod_ssi_set_defaults; + p->cleanup = mod_ssi_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_ssi.h b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi.h new file mode 100644 index 000000000..b4722b29f --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi.h @@ -0,0 +1,53 @@ +#ifndef _MOD_SSI_H_ +#define _MOD_SSI_H_ +#include "first.h" + +#include "base_decls.h" +#include "buffer.h" +#include "array.h" + +#include "plugin.h" + +/* plugin config for all request/connections */ + +typedef struct { + array *ssi_extension; + buffer *content_type; + unsigned short conditional_requests; + unsigned short ssi_exec; + unsigned short ssi_recursion_max; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + buffer *timefmt; + + buffer *stat_fn; + + array *ssi_vars; + array *ssi_cgi_env; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +typedef struct { + buffer *timefmt; + int sizefmt; + + buffer *stat_fn; + + array *ssi_vars; + array *ssi_cgi_env; + + int if_level, if_is_false_level, if_is_false, if_is_false_endif; + unsigned short ssi_recursion_depth; + + plugin_config conf; +} handler_ctx; + +int ssi_eval_expr(server *srv, connection *con, handler_ctx *p, const char *expr); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_expr.c b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_expr.c new file mode 100644 index 000000000..04e388988 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_expr.c @@ -0,0 +1,330 @@ +#include "first.h" + +#include "buffer.h" +#include "log.h" +#include "mod_ssi.h" +#include "mod_ssi_expr.h" +#include "mod_ssi_exprparser.h" + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +typedef struct { + const char *input; + size_t offset; + size_t size; + + int line_pos; + + int in_key; + int in_brace; + int in_cond; +} ssi_tokenizer_t; + +ssi_val_t *ssi_val_init(void) { + ssi_val_t *s; + + s = calloc(1, sizeof(*s)); + + return s; +} + +void ssi_val_free(ssi_val_t *s) { + if (s->str) buffer_free(s->str); + + free(s); +} + +int ssi_val_tobool(ssi_val_t *B) { + if (B->type == SSI_TYPE_STRING) { + return !buffer_string_is_empty(B->str); + } else { + return B->bo; + } +} + +static int ssi_expr_tokenizer(server *srv, connection *con, handler_ctx *p, + ssi_tokenizer_t *t, int *token_id, buffer *token) { + int tid = 0; + size_t i; + + UNUSED(con); + + for (tid = 0; tid == 0 && t->offset < t->size && t->input[t->offset] ; ) { + char c = t->input[t->offset]; + data_string *ds; + + switch (c) { + case '=': + tid = TK_EQ; + + t->offset++; + t->line_pos++; + + buffer_copy_string_len(token, CONST_STR_LEN("(=)")); + + break; + case '>': + if (t->input[t->offset + 1] == '=') { + t->offset += 2; + t->line_pos += 2; + + tid = TK_GE; + + buffer_copy_string_len(token, CONST_STR_LEN("(>=)")); + } else { + t->offset += 1; + t->line_pos += 1; + + tid = TK_GT; + + buffer_copy_string_len(token, CONST_STR_LEN("(>)")); + } + + break; + case '<': + if (t->input[t->offset + 1] == '=') { + t->offset += 2; + t->line_pos += 2; + + tid = TK_LE; + + buffer_copy_string_len(token, CONST_STR_LEN("(<=)")); + } else { + t->offset += 1; + t->line_pos += 1; + + tid = TK_LT; + + buffer_copy_string_len(token, CONST_STR_LEN("(<)")); + } + + break; + + case '!': + if (t->input[t->offset + 1] == '=') { + t->offset += 2; + t->line_pos += 2; + + tid = TK_NE; + + buffer_copy_string_len(token, CONST_STR_LEN("(!=)")); + } else { + t->offset += 1; + t->line_pos += 1; + + tid = TK_NOT; + + buffer_copy_string_len(token, CONST_STR_LEN("(!)")); + } + + break; + case '&': + if (t->input[t->offset + 1] == '&') { + t->offset += 2; + t->line_pos += 2; + + tid = TK_AND; + + buffer_copy_string_len(token, CONST_STR_LEN("(&&)")); + } else { + log_error_write(srv, __FILE__, __LINE__, "sds", + "pos:", t->line_pos, + "missing second &"); + return -1; + } + + break; + case '|': + if (t->input[t->offset + 1] == '|') { + t->offset += 2; + t->line_pos += 2; + + tid = TK_OR; + + buffer_copy_string_len(token, CONST_STR_LEN("(||)")); + } else { + log_error_write(srv, __FILE__, __LINE__, "sds", + "pos:", t->line_pos, + "missing second |"); + return -1; + } + + break; + case '\t': + case ' ': + t->offset++; + t->line_pos++; + break; + + case '\'': + /* search for the terminating " */ + for (i = 1; t->input[t->offset + i] && t->input[t->offset + i] != '\''; i++); + + if (t->input[t->offset + i]) { + tid = TK_VALUE; + + buffer_copy_string_len(token, t->input + t->offset + 1, i-1); + + t->offset += i + 1; + t->line_pos += i + 1; + } else { + /* ERROR */ + + log_error_write(srv, __FILE__, __LINE__, "sds", + "pos:", t->line_pos, + "missing closing quote"); + + return -1; + } + + break; + case '(': + t->offset++; + t->in_brace++; + + tid = TK_LPARAN; + + buffer_copy_string_len(token, CONST_STR_LEN("(")); + break; + case ')': + t->offset++; + t->in_brace--; + + tid = TK_RPARAN; + + buffer_copy_string_len(token, CONST_STR_LEN(")")); + break; + case '$': + if (t->input[t->offset + 1] == '{') { + for (i = 2; t->input[t->offset + i] && t->input[t->offset + i] != '}'; i++); + + if (t->input[t->offset + i] != '}') { + log_error_write(srv, __FILE__, __LINE__, "sds", + "pos:", t->line_pos, + "missing closing quote"); + + return -1; + } + + buffer_copy_string_len(token, t->input + t->offset + 2, i-3); + } else { + for (i = 1; isalpha(t->input[t->offset + i]) || + t->input[t->offset + i] == '_' || + ((i > 1) && isdigit(t->input[t->offset + i])); i++); + + buffer_copy_string_len(token, t->input + t->offset + 1, i-1); + } + + tid = TK_VALUE; + + if (NULL != (ds = (data_string *)array_get_element_klen(p->ssi_cgi_env, CONST_BUF_LEN(token)))) { + buffer_copy_buffer(token, ds->value); + } else if (NULL != (ds = (data_string *)array_get_element_klen(p->ssi_vars, CONST_BUF_LEN(token)))) { + buffer_copy_buffer(token, ds->value); + } else { + buffer_copy_string_len(token, CONST_STR_LEN("")); + } + + t->offset += i; + t->line_pos += i; + + break; + default: + for (i = 0; isgraph(t->input[t->offset + i]); i++) { + char d = t->input[t->offset + i]; + switch(d) { + case ' ': + case '\t': + case ')': + case '(': + case '\'': + case '=': + case '!': + case '<': + case '>': + case '&': + case '|': + break; + } + } + + tid = TK_VALUE; + + buffer_copy_string_len(token, t->input + t->offset, i); + + t->offset += i; + t->line_pos += i; + + break; + } + } + + if (tid) { + *token_id = tid; + + return 1; + } else if (t->offset < t->size) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "pos:", t->line_pos, + "foobar"); + } + return 0; +} + +int ssi_eval_expr(server *srv, connection *con, handler_ctx *p, const char *expr) { + ssi_tokenizer_t t; + void *pParser; + int token_id; + buffer *token; + ssi_ctx_t context; + int ret; + + t.input = expr; + t.offset = 0; + t.size = strlen(expr); + t.line_pos = 1; + + t.in_key = 1; + t.in_brace = 0; + t.in_cond = 0; + + context.ok = 1; + context.srv = srv; + + /* default context */ + + pParser = ssiexprparserAlloc( malloc ); + force_assert(pParser); + token = buffer_init(); + while((1 == (ret = ssi_expr_tokenizer(srv, con, p, &t, &token_id, token))) && context.ok) { + ssiexprparser(pParser, token_id, token, &context); + + token = buffer_init(); + } + ssiexprparser(pParser, 0, token, &context); + ssiexprparserFree(pParser, free ); + + buffer_free(token); + + if (ret == -1) { + log_error_write(srv, __FILE__, __LINE__, "s", + "expr parser failed"); + return -1; + } + + if (context.ok == 0) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "pos:", t.line_pos, + "parser failed somehow near here"); + return -1; + } +#if 0 + log_error_write(srv, __FILE__, __LINE__, "ssd", + "expr: ", + expr, + context.val.bo); +#endif + return context.val.bo; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_expr.h b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_expr.h new file mode 100644 index 000000000..17cd73ecc --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_expr.h @@ -0,0 +1,32 @@ +#ifndef _MOD_SSI_EXPR_H_ +#define _MOD_SSI_EXPR_H_ +#include "first.h" + +#include "buffer.h" + +typedef struct { + enum { SSI_TYPE_UNSET, SSI_TYPE_BOOL, SSI_TYPE_STRING } type; + + buffer *str; + int bo; +} ssi_val_t; + +typedef struct { + int ok; + + ssi_val_t val; + + void *srv; +} ssi_ctx_t; + +typedef enum { SSI_COND_UNSET, SSI_COND_LE, SSI_COND_GE, SSI_COND_EQ, SSI_COND_NE, SSI_COND_LT, SSI_COND_GT } ssi_expr_cond; + +void *ssiexprparserAlloc(void *(*mallocProc)(size_t)); +void ssiexprparserFree(void *p, void (*freeProc)(void*)); +void ssiexprparser(void *yyp, int yymajor, buffer *yyminor, ssi_ctx_t *ctx); + +int ssi_val_tobool(ssi_val_t *B); +ssi_val_t *ssi_val_init(void); +void ssi_val_free(ssi_val_t *s); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_exprparser.c b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_exprparser.c new file mode 100644 index 000000000..fdf15de03 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_exprparser.c @@ -0,0 +1,962 @@ +/* Driver template for the LEMON parser generator. +** The author disclaims copyright to this source code. +*/ +/* First off, code is include which follows the "include" declaration +** in the input file. */ +#include "first.h" +#include <stdio.h> +#line 6 "../../src/mod_ssi_exprparser.y" + +#include "first.h" +#include "mod_ssi_expr.h" +#include "buffer.h" + +#include <string.h> + +#line 17 "./mod_ssi_exprparser.c" +/* Next is all token values, in a form suitable for use by makeheaders. +** This section will be null unless lemon is run with the -m switch. +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ +/* Make sure the INTERFACE macro is defined. +*/ +#ifndef INTERFACE +# define INTERFACE 1 +#endif +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** YYCODETYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 terminals +** and nonterminals. "int" is used otherwise. +** YYNOCODE is a number of type YYCODETYPE which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** YYACTIONTYPE is the data type used for storing terminal +** and nonterminal numbers. "unsigned char" is +** used if there are fewer than 250 rules and +** states combined. "int" is used otherwise. +** ssiexprparserTOKENTYPE is the data type used for minor tokens given +** directly to the parser from the tokenizer. +** YYMINORTYPE is the data type used for all minor tokens. +** This is typically a union of many types, one of +** which is ssiexprparserTOKENTYPE. The entry in the union +** for base tokens is called "yy0". +** YYSTACKDEPTH is the maximum depth of the parser's stack. +** ssiexprparserARG_SDECL A static variable declaration for the %extra_argument +** ssiexprparserARG_PDECL A parameter declaration for the %extra_argument +** ssiexprparserARG_STORE Code to store %extra_argument into yypParser +** ssiexprparserARG_FETCH Code to extract %extra_argument from yypParser +** YYNSTATE the combined number of states. +** YYNRULE the number of rules in the grammar +** YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ +/* */ +#define YYCODETYPE unsigned char +#define YYNOCODE 20 +#define YYACTIONTYPE unsigned char +#define ssiexprparserTOKENTYPE buffer * +typedef union { + ssiexprparserTOKENTYPE yy0; + int yy8; + buffer * yy19; + ssi_val_t * yy29; + int yy39; +} YYMINORTYPE; +#define YYSTACKDEPTH 100 +#define ssiexprparserARG_SDECL ssi_ctx_t *ctx; +#define ssiexprparserARG_PDECL ,ssi_ctx_t *ctx +#define ssiexprparserARG_FETCH ssi_ctx_t *ctx = yypParser->ctx +#define ssiexprparserARG_STORE yypParser->ctx = ctx +#define YYNSTATE 23 +#define YYNRULE 16 +#define YYERRORSYMBOL 13 +#define YYERRSYMDT yy39 +#define YY_NO_ACTION (YYNSTATE+YYNRULE+2) +#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) +#define YY_ERROR_ACTION (YYNSTATE+YYNRULE) + +/* Next are that tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < YYNSTATE Shift N. That is, push the lookahead +** token onto the stack and goto state N. +** +** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE. +** +** N == YYNSTATE+YYNRULE A syntax error has occurred. +** +** N == YYNSTATE+YYNRULE+1 The parser accepts its input. +** +** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large table named yy_action[]. +** Given state S and lookahead X, the action is computed as +** +** yy_action[ yy_shift_ofst[S] + X ] +** +** If the index value yy_shift_ofst[S]+X is out of range or if the value +** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S] +** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table +** and that yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the yy_reduce_ofst[] array is used in place of +** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of +** YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** yy_action[] A single table containing all actions. +** yy_lookahead[] A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** yy_shift_ofst[] For each state, the offset into yy_action for +** shifting terminals. +** yy_reduce_ofst[] For each state, the offset into yy_action for +** shifting non-terminals after a reduce. +** yy_default[] Default action for each state. +*/ +static YYACTIONTYPE yy_action[] = { + /* 0 */ 5, 7, 17, 18, 22, 20, 21, 19, 2, 14, + /* 10 */ 1, 23, 40, 9, 11, 3, 16, 2, 14, 12, + /* 20 */ 4, 14, 5, 7, 6, 14, 7, 8, 14, 10, + /* 30 */ 14, 13, 37, 37, 15, +}; +static YYCODETYPE yy_lookahead[] = { + /* 0 */ 1, 2, 3, 4, 5, 6, 7, 8, 14, 15, + /* 10 */ 16, 0, 18, 9, 10, 17, 12, 14, 15, 16, + /* 20 */ 14, 15, 1, 2, 14, 15, 2, 14, 15, 14, + /* 30 */ 15, 11, 19, 19, 12, +}; +#define YY_SHIFT_USE_DFLT (-2) +static signed char yy_shift_ofst[] = { + /* 0 */ 4, 11, -1, 4, 21, 4, 24, 4, -2, 4, + /* 10 */ -2, 4, 20, -2, 22, -2, -2, -2, -2, -2, + /* 20 */ -2, -2, -2, +}; +#define YY_REDUCE_USE_DFLT (-7) +static signed char yy_reduce_ofst[] = { + /* 0 */ -6, -7, -2, 6, -7, 10, -7, 13, -7, 15, + /* 10 */ -7, 3, -7, -7, -7, -7, -7, -7, -7, -7, + /* 20 */ -7, -7, -7, +}; +static YYACTIONTYPE yy_default[] = { + /* 0 */ 39, 39, 25, 39, 24, 39, 26, 39, 27, 39, + /* 10 */ 28, 39, 39, 29, 30, 32, 31, 33, 34, 35, + /* 20 */ 36, 37, 38, +}; +#define YY_SZ_ACTTAB (sizeof(yy_action)/sizeof(yy_action[0])) + +/* The next table maps tokens into fallback tokens. If a construct +** like the following: +** +** %fallback ID X Y Z. +** +** appears in the grammer, then ID becomes a fallback token for X, Y, +** and Z. Whenever one of the tokens X, Y, or Z is input to the parser +** but it does not parse, the type of the token is changed to ID and +** the parse is retried before an error is thrown. +*/ +#ifdef YYFALLBACK +static const YYCODETYPE yyFallback[] = { +}; +#endif /* YYFALLBACK */ + +/* The following structure represents a single element of the +** parser's stack. Information stored includes: +** +** + The state number for the parser at this level of the stack. +** +** + The value of the token stored at this level of the stack. +** (In other words, the "major" token.) +** +** + The semantic value stored at this level of the stack. This is +** the information used by the action routines in the grammar. +** It is sometimes called the "minor" token. +*/ +struct yyStackEntry { + int stateno; /* The state-number */ + int major; /* The major token value. This is the code + ** number for the token at this stack level */ + YYMINORTYPE minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; +typedef struct yyStackEntry yyStackEntry; + +/* The state of the parser is completely contained in an instance of +** the following structure */ +struct yyParser { + int yyidx; /* Index of top element in stack */ + int yyerrcnt; /* Shifts left before out of the error */ + ssiexprparserARG_SDECL /* A place to hold %extra_argument */ + yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */ +}; +typedef struct yyParser yyParser; + +#ifndef NDEBUG +#include <stdio.h> +static FILE *yyTraceFILE = NULL; +static char *yyTracePrompt = NULL; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* +** Turn parser tracing on by giving a stream to which to write the trace +** and a prompt to preface each trace message. Tracing is turned off +** by making either argument NULL +** +** Inputs: +** <ul> +** <li> A FILE* to which trace output should be written. +** If NULL, then tracing is turned off. +** <li> A prefix string written at the beginning of every +** line of trace output. If NULL, then tracing is +** turned off. +** </ul> +** +** Outputs: +** None. +*/ +#if 0 +void ssiexprparserTrace(FILE *TraceFILE, char *zTracePrompt){ + yyTraceFILE = TraceFILE; + yyTracePrompt = zTracePrompt; + if( yyTraceFILE==0 ) yyTracePrompt = 0; + else if( yyTracePrompt==0 ) yyTraceFILE = 0; +} +#endif +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing shifts, the names of all terminals and nonterminals +** are required. The following table supplies these names */ +static const char *yyTokenName[] = { + "$", "AND", "OR", "EQ", + "NE", "GT", "GE", "LT", + "LE", "NOT", "LPARAN", "RPARAN", + "VALUE", "error", "expr", "value", + "exprline", "cond", "input", +}; +#endif /* NDEBUG */ + +#ifndef NDEBUG +/* For tracing reduce actions, the names of all rules are required. +*/ +static const char *yyRuleName[] = { + /* 0 */ "input ::= exprline", + /* 1 */ "exprline ::= expr cond expr", + /* 2 */ "exprline ::= expr", + /* 3 */ "expr ::= expr AND expr", + /* 4 */ "expr ::= expr OR expr", + /* 5 */ "expr ::= NOT expr", + /* 6 */ "expr ::= LPARAN exprline RPARAN", + /* 7 */ "expr ::= value", + /* 8 */ "value ::= VALUE", + /* 9 */ "value ::= value VALUE", + /* 10 */ "cond ::= EQ", + /* 11 */ "cond ::= NE", + /* 12 */ "cond ::= LE", + /* 13 */ "cond ::= GE", + /* 14 */ "cond ::= LT", + /* 15 */ "cond ::= GT", +}; +#endif /* NDEBUG */ + +/* +** This function returns the symbolic name associated with a token +** value. +*/ +#if 0 +const char *ssiexprparserTokenName(int tokenType){ +#ifndef NDEBUG + if( tokenType>0 && (size_t)tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){ + return yyTokenName[tokenType]; + }else{ + return "Unknown"; + } +#else + return ""; +#endif +} +#endif + +/* +** This function allocates a new parser. +** The only argument is a pointer to a function which works like +** malloc. +** +** Inputs: +** A pointer to the function used to allocate memory. +** +** Outputs: +** A pointer to a parser. This pointer is used in subsequent calls +** to ssiexprparser and ssiexprparserFree. +*/ +void *ssiexprparserAlloc(void *(*mallocProc)(size_t)){ + yyParser *pParser; + pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) ); + if( pParser ){ + pParser->yyidx = -1; + } + return pParser; +} + +/* The following function deletes the value associated with a +** symbol. The symbol can be either a terminal or nonterminal. +** "yymajor" is the symbol code, and "yypminor" is a pointer to +** the value. +*/ +static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){ + switch( yymajor ){ + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: +#line 22 "../../src/mod_ssi_exprparser.y" +{ buffer_free((yypminor->yy0)); } +#line 353 "./mod_ssi_exprparser.c" + break; + default: break; /* If no destructor action specified: do nothing */ + } +} + +/* +** Pop the parser's stack once. +** +** If there is a destructor routine associated with the token which +** is popped from the stack, then call it. +** +** Return the major token number for the symbol popped. +*/ +static int yy_pop_parser_stack(yyParser *pParser){ + YYCODETYPE yymajor; + yyStackEntry *yytos; + + if( pParser->yyidx<0 ) return 0; + yytos = &pParser->yystack[pParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE && pParser->yyidx>=0 ){ + fprintf(yyTraceFILE,"%sPopping %s\n", + yyTracePrompt, + yyTokenName[yytos->major]); + } +#endif + yymajor = yytos->major; + yy_destructor( yymajor, &yytos->minor); + pParser->yyidx--; + return yymajor; +} + +/* +** Deallocate and destroy a parser. Destructors are all called for +** all stack elements before shutting the parser down. +** +** Inputs: +** <ul> +** <li> A pointer to the parser. This should be a pointer +** obtained from ssiexprparserAlloc. +** <li> A pointer to a function used to reclaim memory obtained +** from malloc. +** </ul> +*/ +void ssiexprparserFree( + void *p, /* The parser to be deleted */ + void (*freeProc)(void*) /* Function used to reclaim memory */ +){ + yyParser *pParser = (yyParser*)p; + if( pParser==NULL ) return; + while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); + (*freeProc)((void*)pParser); +} + +/* +** Find the appropriate action for a parser given the terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_shift_action( + yyParser *pParser, /* The parser */ + int iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + /* if( pParser->yyidx<0 ) return YY_NO_ACTION; */ + i = yy_shift_ofst[stateno]; + if( i==YY_SHIFT_USE_DFLT ){ + return yy_default[stateno]; + } + if( iLookAhead==YYNOCODE ){ + return YY_NO_ACTION; + } + i += iLookAhead; + if( i<0 || (size_t)i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ +#ifdef YYFALLBACK + int iFallback; /* Fallback token */ + if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) + && (iFallback = yyFallback[iLookAhead])!=0 ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n", + yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]); + } +#endif + return yy_find_shift_action(pParser, iFallback); + } +#endif + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Find the appropriate action for a parser given the non-terminal +** look-ahead token iLookAhead. +** +** If the look-ahead token is YYNOCODE, then check to see if the action is +** independent of the look-ahead. If it is, return the action, otherwise +** return YY_NO_ACTION. +*/ +static int yy_find_reduce_action( + yyParser *pParser, /* The parser */ + int iLookAhead /* The look-ahead token */ +){ + int i; + int stateno = pParser->yystack[pParser->yyidx].stateno; + + i = yy_reduce_ofst[stateno]; + if( i==YY_REDUCE_USE_DFLT ){ + return yy_default[stateno]; + } + if( iLookAhead==YYNOCODE ){ + return YY_NO_ACTION; + } + i += iLookAhead; + if( i<0 || (size_t)i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + return yy_default[stateno]; + }else{ + return yy_action[i]; + } +} + +/* +** Perform a shift action. +*/ +static void yy_shift( + yyParser *yypParser, /* The parser to be shifted */ + int yyNewState, /* The new state to shift in */ + int yyMajor, /* The major token to shift in */ + YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */ +){ + yyStackEntry *yytos; + yypParser->yyidx++; + if( yypParser->yyidx>=YYSTACKDEPTH ){ + ssiexprparserARG_FETCH; + yypParser->yyidx--; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will execute if the parser + ** stack every overflows */ + ssiexprparserARG_STORE; /* Suppress warning about unused %extra_argument var */ + return; + } + yytos = &yypParser->yystack[yypParser->yyidx]; + yytos->stateno = yyNewState; + yytos->major = yyMajor; + yytos->minor = *yypMinor; +#ifndef NDEBUG + if( yyTraceFILE && yypParser->yyidx>0 ){ + int i; + fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState); + fprintf(yyTraceFILE,"%sStack:",yyTracePrompt); + for(i=1; i<=yypParser->yyidx; i++) + fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]); + fprintf(yyTraceFILE,"\n"); + } +#endif +} + +/* The following table contains information about every rule that +** is used during the reduce. +*/ +static struct { + YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */ + unsigned char nrhs; /* Number of right-hand side symbols in the rule */ +} yyRuleInfo[] = { + { 18, 1 }, + { 16, 3 }, + { 16, 1 }, + { 14, 3 }, + { 14, 3 }, + { 14, 2 }, + { 14, 3 }, + { 14, 1 }, + { 15, 1 }, + { 15, 2 }, + { 17, 1 }, + { 17, 1 }, + { 17, 1 }, + { 17, 1 }, + { 17, 1 }, + { 17, 1 }, +}; + +static void yy_accept(yyParser*); /* Forward Declaration */ + +/* +** Perform a reduce action and the shift that must immediately +** follow the reduce. +*/ +static void yy_reduce( + yyParser *yypParser, /* The parser */ + int yyruleno /* Number of the rule by which to reduce */ +){ + int yygoto; /* The next state */ + int yyact; /* The next action */ + YYMINORTYPE yygotominor; /* The LHS of the rule reduced */ + yyStackEntry *yymsp; /* The top of the parser's stack */ + int yysize; /* Amount to pop the stack */ + ssiexprparserARG_FETCH; + yymsp = &yypParser->yystack[yypParser->yyidx]; +#ifndef NDEBUG + if( yyTraceFILE ) { + if (yyruleno>=0 + && (size_t)yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){ + fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, + yyRuleName[yyruleno]); + } else { + return; /*(should not happen)*/ + } + } +#endif /* NDEBUG */ + + switch( yyruleno ){ + /* Beginning here are the reduction cases. A typical example + ** follows: + ** case 0: + ** #line <lineno> <grammarfile> + ** { ... } // User supplied code + ** #line <lineno> <thisfile> + ** break; + */ + case 0: +#line 29 "../../src/mod_ssi_exprparser.y" +{ + ctx->val.bo = ssi_val_tobool(yymsp[0].minor.yy29); + ctx->val.type = SSI_TYPE_BOOL; + + ssi_val_free(yymsp[0].minor.yy29); +} +#line 594 "./mod_ssi_exprparser.c" + break; + case 1: +#line 36 "../../src/mod_ssi_exprparser.y" +{ + int cmp; + + if (yymsp[-2].minor.yy29->type == SSI_TYPE_STRING && + yymsp[0].minor.yy29->type == SSI_TYPE_STRING) { + cmp = strcmp(yymsp[-2].minor.yy29->str->ptr, yymsp[0].minor.yy29->str->ptr); + } else { + cmp = ssi_val_tobool(yymsp[-2].minor.yy29) - ssi_val_tobool(yymsp[0].minor.yy29); + } + + yygotominor.yy29 = yymsp[-2].minor.yy29; + + switch(yymsp[-1].minor.yy8) { + case SSI_COND_EQ: yygotominor.yy29->bo = (cmp == 0) ? 1 : 0; break; + case SSI_COND_NE: yygotominor.yy29->bo = (cmp != 0) ? 1 : 0; break; + case SSI_COND_GE: yygotominor.yy29->bo = (cmp >= 0) ? 1 : 0; break; + case SSI_COND_GT: yygotominor.yy29->bo = (cmp > 0) ? 1 : 0; break; + case SSI_COND_LE: yygotominor.yy29->bo = (cmp <= 0) ? 1 : 0; break; + case SSI_COND_LT: yygotominor.yy29->bo = (cmp < 0) ? 1 : 0; break; + } + + yygotominor.yy29->type = SSI_TYPE_BOOL; + + ssi_val_free(yymsp[0].minor.yy29); +} +#line 623 "./mod_ssi_exprparser.c" + break; + case 2: +#line 61 "../../src/mod_ssi_exprparser.y" +{ + yygotominor.yy29 = yymsp[0].minor.yy29; +} +#line 630 "./mod_ssi_exprparser.c" + break; + case 3: +#line 64 "../../src/mod_ssi_exprparser.y" +{ + int e; + + e = ssi_val_tobool(yymsp[-2].minor.yy29) && ssi_val_tobool(yymsp[0].minor.yy29); + + yygotominor.yy29 = yymsp[-2].minor.yy29; + yygotominor.yy29->bo = e; + yygotominor.yy29->type = SSI_TYPE_BOOL; + ssi_val_free(yymsp[0].minor.yy29); +} +#line 644 "./mod_ssi_exprparser.c" + yy_destructor(1,&yymsp[-1].minor); + break; + case 4: +#line 75 "../../src/mod_ssi_exprparser.y" +{ + int e; + + e = ssi_val_tobool(yymsp[-2].minor.yy29) || ssi_val_tobool(yymsp[0].minor.yy29); + + yygotominor.yy29 = yymsp[-2].minor.yy29; + yygotominor.yy29->bo = e; + yygotominor.yy29->type = SSI_TYPE_BOOL; + ssi_val_free(yymsp[0].minor.yy29); +} +#line 659 "./mod_ssi_exprparser.c" + yy_destructor(2,&yymsp[-1].minor); + break; + case 5: +#line 86 "../../src/mod_ssi_exprparser.y" +{ + int e; + + e = !ssi_val_tobool(yymsp[0].minor.yy29); + + yygotominor.yy29 = yymsp[0].minor.yy29; + yygotominor.yy29->bo = e; + yygotominor.yy29->type = SSI_TYPE_BOOL; +} +#line 673 "./mod_ssi_exprparser.c" + yy_destructor(9,&yymsp[-1].minor); + break; + case 6: +#line 95 "../../src/mod_ssi_exprparser.y" +{ + yygotominor.yy29 = yymsp[-1].minor.yy29; +} +#line 681 "./mod_ssi_exprparser.c" + yy_destructor(10,&yymsp[-2].minor); + yy_destructor(11,&yymsp[0].minor); + break; + case 7: +#line 99 "../../src/mod_ssi_exprparser.y" +{ + yygotominor.yy29 = ssi_val_init(); + yygotominor.yy29->str = yymsp[0].minor.yy19; + yygotominor.yy29->type = SSI_TYPE_STRING; +} +#line 692 "./mod_ssi_exprparser.c" + break; + case 8: +#line 105 "../../src/mod_ssi_exprparser.y" +{ + yygotominor.yy19 = yymsp[0].minor.yy0; +} +#line 699 "./mod_ssi_exprparser.c" + break; + case 9: +#line 109 "../../src/mod_ssi_exprparser.y" +{ + yygotominor.yy19 = yymsp[-1].minor.yy19; + buffer_append_string_buffer(yygotominor.yy19, yymsp[0].minor.yy0); + buffer_free(yymsp[0].minor.yy0); +} +#line 708 "./mod_ssi_exprparser.c" + break; + case 10: +#line 115 "../../src/mod_ssi_exprparser.y" +{ yygotominor.yy8 = SSI_COND_EQ; } +#line 713 "./mod_ssi_exprparser.c" + yy_destructor(3,&yymsp[0].minor); + break; + case 11: +#line 116 "../../src/mod_ssi_exprparser.y" +{ yygotominor.yy8 = SSI_COND_NE; } +#line 719 "./mod_ssi_exprparser.c" + yy_destructor(4,&yymsp[0].minor); + break; + case 12: +#line 117 "../../src/mod_ssi_exprparser.y" +{ yygotominor.yy8 = SSI_COND_LE; } +#line 725 "./mod_ssi_exprparser.c" + yy_destructor(8,&yymsp[0].minor); + break; + case 13: +#line 118 "../../src/mod_ssi_exprparser.y" +{ yygotominor.yy8 = SSI_COND_GE; } +#line 731 "./mod_ssi_exprparser.c" + yy_destructor(6,&yymsp[0].minor); + break; + case 14: +#line 119 "../../src/mod_ssi_exprparser.y" +{ yygotominor.yy8 = SSI_COND_LT; } +#line 737 "./mod_ssi_exprparser.c" + yy_destructor(7,&yymsp[0].minor); + break; + case 15: +#line 120 "../../src/mod_ssi_exprparser.y" +{ yygotominor.yy8 = SSI_COND_GT; } +#line 743 "./mod_ssi_exprparser.c" + yy_destructor(5,&yymsp[0].minor); + break; + }; + yygoto = yyRuleInfo[yyruleno].lhs; + yysize = yyRuleInfo[yyruleno].nrhs; + yypParser->yyidx -= yysize; + yyact = yy_find_reduce_action(yypParser,yygoto); + if( yyact < YYNSTATE ){ + yy_shift(yypParser,yyact,yygoto,&yygotominor); + }else if( yyact == YYNSTATE + YYNRULE + 1 ){ + yy_accept(yypParser); + } +} + +/* +** The following code executes when the parse fails +*/ +static void yy_parse_failed( + yyParser *yypParser /* The parser */ +){ + ssiexprparserARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser fails */ +#line 14 "../../src/mod_ssi_exprparser.y" + + ctx->ok = 0; + +#line 777 "./mod_ssi_exprparser.c" + ssiexprparserARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following code executes when a syntax error first occurs. +*/ +static void yy_syntax_error( + yyParser *yypParser, /* The parser */ + int yymajor, /* The major type of the error token */ + YYMINORTYPE yyminor /* The minor type of the error token */ +){ + ssiexprparserARG_FETCH; + UNUSED(yymajor); + UNUSED(yyminor); +#define TOKEN (yyminor.yy0) + ssiexprparserARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* +** The following is executed when the parser accepts +*/ +static void yy_accept( + yyParser *yypParser /* The parser */ +){ + ssiexprparserARG_FETCH; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt); + } +#endif + while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser); + /* Here code is inserted which will be executed whenever the + ** parser accepts */ + ssiexprparserARG_STORE; /* Suppress warning about unused %extra_argument variable */ +} + +/* The main parser program. +** The first argument is a pointer to a structure obtained from +** "ssiexprparserAlloc" which describes the current state of the parser. +** The second argument is the major token number. The third is +** the minor token. The fourth optional argument is whatever the +** user wants (and specified in the grammar) and is available for +** use by the action routines. +** +** Inputs: +** <ul> +** <li> A pointer to the parser (an opaque structure.) +** <li> The major token number. +** <li> The minor token number. +** <li> An option argument of a grammar-specified type. +** </ul> +** +** Outputs: +** None. +*/ +void ssiexprparser( + void *yyp, /* The parser */ + int yymajor, /* The major token code number */ + ssiexprparserTOKENTYPE yyminor /* The value for the token */ + ssiexprparserARG_PDECL /* Optional %extra_argument parameter */ +){ + YYMINORTYPE yyminorunion; + int yyact; /* The parser action. */ + int yyendofinput; /* True if we are at the end of input */ + int yyerrorhit = 0; /* True if yymajor has invoked an error */ + yyParser *yypParser; /* The parser */ + + /* (re)initialize the parser, if necessary */ + yypParser = (yyParser*)yyp; + if( yypParser->yyidx<0 ){ + if( yymajor==0 ) return; + yypParser->yyidx = 0; + yypParser->yyerrcnt = -1; + yypParser->yystack[0].stateno = 0; + yypParser->yystack[0].major = 0; + } + yyminorunion.yy0 = yyminor; + yyendofinput = (yymajor==0); + ssiexprparserARG_STORE; + +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]); + } +#endif + + do{ + yyact = yy_find_shift_action(yypParser,yymajor); + if( yyact<YYNSTATE ){ + yy_shift(yypParser,yyact,yymajor,&yyminorunion); + yypParser->yyerrcnt--; + if( yyendofinput && yypParser->yyidx>=0 ){ + yymajor = 0; + }else{ + yymajor = YYNOCODE; + } + }else if( yyact < YYNSTATE + YYNRULE ){ + yy_reduce(yypParser,yyact-YYNSTATE); + }else if( yyact == YY_ERROR_ACTION ){ + int yymx; +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt); + } +#endif +#ifdef YYERRORSYMBOL + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if( yypParser->yyerrcnt<0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yymx = yypParser->yystack[yypParser->yyidx].major; + if( yymx==YYERRORSYMBOL || yyerrorhit ){ +#ifndef NDEBUG + if( yyTraceFILE ){ + fprintf(yyTraceFILE,"%sDiscard input token %s\n", + yyTracePrompt,yyTokenName[yymajor]); + } +#endif + yy_destructor(yymajor,&yyminorunion); + yymajor = YYNOCODE; + }else{ + while( + yypParser->yyidx >= 0 && + yymx != YYERRORSYMBOL && + (yyact = yy_find_shift_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE + ){ + yy_pop_parser_stack(yypParser); + } + if( yypParser->yyidx < 0 || yymajor==0 ){ + yy_destructor(yymajor,&yyminorunion); + yy_parse_failed(yypParser); + yymajor = YYNOCODE; + }else if( yymx!=YYERRORSYMBOL ){ + YYMINORTYPE u2; + u2.YYERRSYMDT = 0; + yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2); + } + } + yypParser->yyerrcnt = 3; + yyerrorhit = 1; +#else /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if( yypParser->yyerrcnt<=0 ){ + yy_syntax_error(yypParser,yymajor,yyminorunion); + } + yypParser->yyerrcnt = 3; + yy_destructor(yymajor,&yyminorunion); + if( yyendofinput ){ + yy_parse_failed(yypParser); + } + yymajor = YYNOCODE; +#endif + }else{ + yy_accept(yypParser); + yymajor = YYNOCODE; + } + }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 ); + return; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_exprparser.h b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_exprparser.h new file mode 100644 index 000000000..eb55ea5ff --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_exprparser.h @@ -0,0 +1,12 @@ +#define TK_AND 1 +#define TK_OR 2 +#define TK_EQ 3 +#define TK_NE 4 +#define TK_GT 5 +#define TK_GE 6 +#define TK_LT 7 +#define TK_LE 8 +#define TK_NOT 9 +#define TK_LPARAN 10 +#define TK_RPARAN 11 +#define TK_VALUE 12 diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_exprparser.y b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_exprparser.y new file mode 100644 index 000000000..0b18edc14 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_ssi_exprparser.y @@ -0,0 +1,120 @@ +%token_prefix TK_ +%token_type {buffer *} +%extra_argument {ssi_ctx_t *ctx} +%name ssiexprparser + +%include { +#include "first.h" +#include "mod_ssi_expr.h" +#include "buffer.h" + +#include <string.h> +} + +%parse_failure { + ctx->ok = 0; +} + +%type expr { ssi_val_t * } +%type value { buffer * } +%type exprline { ssi_val_t * } +%type cond { int } +%token_destructor { buffer_free($$); } + +%left AND. +%left OR. +%nonassoc EQ NE GT GE LT LE. +%right NOT. + +input ::= exprline(B). { + ctx->val.bo = ssi_val_tobool(B); + ctx->val.type = SSI_TYPE_BOOL; + + ssi_val_free(B); +} + +exprline(A) ::= expr(B) cond(C) expr(D). { + int cmp; + + if (B->type == SSI_TYPE_STRING && + D->type == SSI_TYPE_STRING) { + cmp = strcmp(B->str->ptr, D->str->ptr); + } else { + cmp = ssi_val_tobool(B) - ssi_val_tobool(D); + } + + A = B; + + switch(C) { + case SSI_COND_EQ: A->bo = (cmp == 0) ? 1 : 0; break; + case SSI_COND_NE: A->bo = (cmp != 0) ? 1 : 0; break; + case SSI_COND_GE: A->bo = (cmp >= 0) ? 1 : 0; break; + case SSI_COND_GT: A->bo = (cmp > 0) ? 1 : 0; break; + case SSI_COND_LE: A->bo = (cmp <= 0) ? 1 : 0; break; + case SSI_COND_LT: A->bo = (cmp < 0) ? 1 : 0; break; + } + + A->type = SSI_TYPE_BOOL; + + ssi_val_free(D); +} +exprline(A) ::= expr(B). { + A = B; +} +expr(A) ::= expr(B) AND expr(C). { + int e; + + e = ssi_val_tobool(B) && ssi_val_tobool(C); + + A = B; + A->bo = e; + A->type = SSI_TYPE_BOOL; + ssi_val_free(C); +} + +expr(A) ::= expr(B) OR expr(C). { + int e; + + e = ssi_val_tobool(B) || ssi_val_tobool(C); + + A = B; + A->bo = e; + A->type = SSI_TYPE_BOOL; + ssi_val_free(C); +} + +expr(A) ::= NOT expr(B). { + int e; + + e = !ssi_val_tobool(B); + + A = B; + A->bo = e; + A->type = SSI_TYPE_BOOL; +} +expr(A) ::= LPARAN exprline(B) RPARAN. { + A = B; +} + +expr(A) ::= value(B). { + A = ssi_val_init(); + A->str = B; + A->type = SSI_TYPE_STRING; +} + +value(A) ::= VALUE(B). { + A = B; +} + +value(A) ::= value(B) VALUE(C). { + A = B; + buffer_append_string_buffer(A, C); + buffer_free(C); +} + +cond(A) ::= EQ. { A = SSI_COND_EQ; } +cond(A) ::= NE. { A = SSI_COND_NE; } +cond(A) ::= LE. { A = SSI_COND_LE; } +cond(A) ::= GE. { A = SSI_COND_GE; } +cond(A) ::= LT. { A = SSI_COND_LT; } +cond(A) ::= GT. { A = SSI_COND_GT; } diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_staticfile.c b/data/lighttpd/lighttpd-1.4.53/src/mod_staticfile.c new file mode 100644 index 000000000..bf783a21f --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_staticfile.c @@ -0,0 +1,221 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" + +#include "plugin.h" + +#include "etag.h" +#include "http_chunk.h" +#include "response.h" + +#include <stdlib.h> +#include <string.h> + +/** + * this is a staticfile for a lighttpd plugin + * + */ + + + +/* plugin config for all request/connections */ + +typedef struct { + array *exclude_ext; + unsigned short etags_used; + unsigned short disable_pathinfo; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_staticfile_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_staticfile_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->exclude_ext); + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_staticfile_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "static-file.exclude-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "static-file.etags", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "static-file.disable-pathinfo", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->exclude_ext = array_init(); + s->etags_used = 1; + s->disable_pathinfo = 0; + + cv[0].destination = s->exclude_ext; + cv[1].destination = &(s->etags_used); + cv[2].destination = &(s->disable_pathinfo); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_vlist(s->exclude_ext)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for static-file.exclude-extensions; expected list of \"ext\""); + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_staticfile_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(exclude_ext); + PATCH(etags_used); + PATCH(disable_pathinfo); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.exclude-extensions"))) { + PATCH(exclude_ext); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.etags"))) { + PATCH(etags_used); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.disable-pathinfo"))) { + PATCH(disable_pathinfo); + } + } + } + + return 0; +} +#undef PATCH + +URIHANDLER_FUNC(mod_staticfile_subrequest) { + plugin_data *p = p_d; + + /* someone else has done a decision for us */ + if (con->http_status != 0) return HANDLER_GO_ON; + if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; + if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; + + /* someone else has handled this request */ + if (con->mode != DIRECT) return HANDLER_GO_ON; + + /* we only handle GET, POST and HEAD */ + switch(con->request.http_method) { + case HTTP_METHOD_GET: + case HTTP_METHOD_POST: + case HTTP_METHOD_HEAD: + break; + default: + return HANDLER_GO_ON; + } + + mod_staticfile_patch_connection(srv, con, p); + + if (p->conf.disable_pathinfo && !buffer_string_is_empty(con->request.pathinfo)) { + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- NOT handling file as static file, pathinfo forbidden"); + } + return HANDLER_GO_ON; + } + + /* ignore certain extensions */ + if (array_match_value_suffix(p->conf.exclude_ext, con->physical.path)) { + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- NOT handling file as static file, extension forbidden"); + } + return HANDLER_GO_ON; + } + + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- handling file as static file"); + } + + if (!p->conf.etags_used) con->etag_flags = 0; + http_response_send_file(srv, con, con->physical.path); + + return HANDLER_FINISHED; +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_staticfile_plugin_init(plugin *p); +int mod_staticfile_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("staticfile"); + + p->init = mod_staticfile_init; + p->handle_subrequest_start = mod_staticfile_subrequest; + p->set_defaults = mod_staticfile_set_defaults; + p->cleanup = mod_staticfile_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_status.c b/data/lighttpd/lighttpd-1.4.53/src/mod_status.c new file mode 100644 index 000000000..70e3d3a01 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_status.c @@ -0,0 +1,977 @@ +#include "first.h" + +#include "base.h" +#include "connections.h" +#include "fdevent.h" +#include "http_header.h" +#include "log.h" + +#include "plugin.h" + +#include <sys/types.h> + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> + +typedef struct { + buffer *config_url; + buffer *status_url; + buffer *statistics_url; + + int sort; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + double traffic_out; + double requests; + + double mod_5s_traffic_out[5]; + double mod_5s_requests[5]; + size_t mod_5s_ndx; + + double rel_traffic_out; + double rel_requests; + + double abs_traffic_out; + double abs_requests; + + double bytes_written; + + buffer *module_list; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +INIT_FUNC(mod_status_init) { + plugin_data *p; + size_t i; + + p = calloc(1, sizeof(*p)); + + p->traffic_out = p->requests = 0; + p->rel_traffic_out = p->rel_requests = 0; + p->abs_traffic_out = p->abs_requests = 0; + p->bytes_written = 0; + p->module_list = buffer_init(); + + for (i = 0; i < 5; i++) { + p->mod_5s_traffic_out[i] = p->mod_5s_requests[i] = 0; + } + + return p; +} + +FREE_FUNC(mod_status_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + buffer_free(p->module_list); + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + if (NULL == s) continue; + + buffer_free(s->status_url); + buffer_free(s->statistics_url); + buffer_free(s->config_url); + + free(s); + } + free(p->config_storage); + } + + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_status_set_defaults) { + plugin_data *p = p_d; + size_t i; + + config_values_t cv[] = { + { "status.status-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "status.config-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { "status.enable-sort", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, + { "status.statistics-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->config_url = buffer_init(); + s->status_url = buffer_init(); + s->sort = 1; + s->statistics_url = buffer_init(); + + cv[0].destination = s->status_url; + cv[1].destination = s->config_url; + cv[2].destination = &(s->sort); + cv[3].destination = s->statistics_url; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + + + +static int mod_status_row_append(buffer *b, const char *key, const char *value) { + buffer_append_string_len(b, CONST_STR_LEN(" <tr>\n")); + buffer_append_string_len(b, CONST_STR_LEN(" <td><b>")); + buffer_append_string(b, key); + buffer_append_string_len(b, CONST_STR_LEN("</b></td>\n")); + buffer_append_string_len(b, CONST_STR_LEN(" <td>")); + buffer_append_string(b, value); + buffer_append_string_len(b, CONST_STR_LEN("</td>\n")); + buffer_append_string_len(b, CONST_STR_LEN(" </tr>\n")); + + return 0; +} + +static int mod_status_header_append(buffer *b, const char *key) { + buffer_append_string_len(b, CONST_STR_LEN(" <tr>\n")); + buffer_append_string_len(b, CONST_STR_LEN(" <th colspan=\"2\">")); + buffer_append_string(b, key); + buffer_append_string_len(b, CONST_STR_LEN("</th>\n")); + buffer_append_string_len(b, CONST_STR_LEN(" </tr>\n")); + + return 0; +} + +static int mod_status_header_append_sort(buffer *b, void *p_d, const char* key) { + plugin_data *p = p_d; + + if (p->conf.sort) { + buffer_append_string_len(b, CONST_STR_LEN("<th class=\"status\"><a href=\"#\" class=\"sortheader\" onclick=\"resort(this);return false;\">")); + buffer_append_string(b, key); + buffer_append_string_len(b, CONST_STR_LEN("<span class=\"sortarrow\">:</span></a></th>\n")); + } else { + buffer_append_string_len(b, CONST_STR_LEN("<th class=\"status\">")); + buffer_append_string(b, key); + buffer_append_string_len(b, CONST_STR_LEN("</th>\n")); + } + + return 0; +} + +static int mod_status_get_multiplier(double *avg, char *multiplier, int size) { + *multiplier = ' '; + + if (*avg > size) { *avg /= size; *multiplier = 'k'; } + if (*avg > size) { *avg /= size; *multiplier = 'M'; } + if (*avg > size) { *avg /= size; *multiplier = 'G'; } + if (*avg > size) { *avg /= size; *multiplier = 'T'; } + if (*avg > size) { *avg /= size; *multiplier = 'P'; } + if (*avg > size) { *avg /= size; *multiplier = 'E'; } + if (*avg > size) { *avg /= size; *multiplier = 'Z'; } + if (*avg > size) { *avg /= size; *multiplier = 'Y'; } + + return 0; +} + +static handler_t mod_status_handle_server_status_html(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + buffer *b = chunkqueue_append_buffer_open(con->write_queue); + size_t j; + double avg; + char multiplier = '\0'; + char buf[32]; + time_t ts; + + int days, hours, mins, seconds; + + /*(CON_STATE_CLOSE must be last state in enum connection_state_t)*/ + int cstates[CON_STATE_CLOSE+3]; + memset(cstates, 0, sizeof(cstates)); + + buffer_copy_string_len(b, CONST_STR_LEN( + "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" + " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" + " <head>\n" + " <title>Status</title>\n" + + " <style type=\"text/css\">\n" + " table.status { border: black solid thin; }\n" + " td { white-space: nowrap; }\n" + " td.int { background-color: #f0f0f0; text-align: right }\n" + " td.string { background-color: #f0f0f0; text-align: left }\n" + " th.status { background-color: black; color: white; font-weight: bold; }\n" + " a.sortheader { background-color: black; color: white; font-weight: bold; text-decoration: none; display: block; }\n" + " span.sortarrow { color: white; text-decoration: none; }\n" + " </style>\n")); + + if (!buffer_string_is_empty(con->uri.query) && 0 == memcmp(con->uri.query->ptr, CONST_STR_LEN("refresh="))) { + /* Note: Refresh is an historical, but non-standard HTTP header + * References (meta http-equiv="refresh" use is deprecated): + * https://www.w3.org/TR/WCAG10-HTML-TECHS/#meta-element + * https://www.w3.org/TR/WCAG10-CORE-TECHS/#auto-page-refresh + * https://www.w3.org/QA/Tips/reback + */ + const long refresh = strtol(con->uri.query->ptr+sizeof("refresh=")-1, NULL, 10); + if (refresh > 0) { + buffer_append_string_len(b, CONST_STR_LEN("<meta http-equiv=\"refresh\" content=\"")); + buffer_append_int(b, refresh < 604800 ? refresh : 604800); + buffer_append_string_len(b, CONST_STR_LEN("\">\n")); + } + } + + if (p->conf.sort) { + buffer_append_string_len(b, CONST_STR_LEN( + "<script type=\"text/javascript\">\n" + "// <!--\n" + "var sort_column;\n" + "var prev_span = null;\n" + + "function get_inner_text(el) {\n" + " if((typeof el == 'string')||(typeof el == 'undefined'))\n" + " return el;\n" + " if(el.innerText)\n" + " return el.innerText;\n" + " else {\n" + " var str = \"\";\n" + " var cs = el.childNodes;\n" + " var l = cs.length;\n" + " for (i=0;i<l;i++) {\n" + " if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n" + " else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n" + " }\n" + " }\n" + " return str;\n" + "}\n" + + "function sortfn(a,b) {\n" + " var at = get_inner_text(a.cells[sort_column]);\n" + " var bt = get_inner_text(b.cells[sort_column]);\n" + " if (a.cells[sort_column].className == 'int') {\n" + " return parseInt(at)-parseInt(bt);\n" + " } else {\n" + " aa = at.toLowerCase();\n" + " bb = bt.toLowerCase();\n" + " if (aa==bb) return 0;\n" + " else if (aa<bb) return -1;\n" + " else return 1;\n" + " }\n" + "}\n" + + "function resort(lnk) {\n" + " var span = lnk.childNodes[1];\n" + " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n" + " var rows = new Array();\n" + " for (j=1;j<table.rows.length;j++)\n" + " rows[j-1] = table.rows[j];\n" + " sort_column = lnk.parentNode.cellIndex;\n" + " rows.sort(sortfn);\n" + + " if (prev_span != null) prev_span.innerHTML = '';\n" + " if (span.getAttribute('sortdir')=='down') {\n" + " span.innerHTML = '↑';\n" + " span.setAttribute('sortdir','up');\n" + " rows.reverse();\n" + " } else {\n" + " span.innerHTML = '↓';\n" + " span.setAttribute('sortdir','down');\n" + " }\n" + " for (i=0;i<rows.length;i++)\n" + " table.tBodies[0].appendChild(rows[i]);\n" + " prev_span = span;\n" + "}\n" + "// -->\n" + "</script>\n")); + } + + buffer_append_string_len(b, CONST_STR_LEN( + " </head>\n" + " <body>\n")); + + + + /* connection listing */ + buffer_append_string_len(b, CONST_STR_LEN("<h1>Server-Status (")); + buffer_append_string_buffer(b, con->conf.server_tag); + buffer_append_string_len(b, CONST_STR_LEN(")</h1>")); + + buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">")); + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Hostname</td><td class=\"string\">")); + buffer_append_string_buffer(b, con->uri.authority); + buffer_append_string_len(b, CONST_STR_LEN(" (")); + buffer_append_string_buffer(b, con->server_name); + buffer_append_string_len(b, CONST_STR_LEN(")</td></tr>\n")); + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Uptime</td><td class=\"string\">")); + + ts = srv->cur_ts - srv->startup_ts; + + days = ts / (60 * 60 * 24); + ts %= (60 * 60 * 24); + + hours = ts / (60 * 60); + ts %= (60 * 60); + + mins = ts / (60); + ts %= (60); + + seconds = ts; + + if (days) { + buffer_append_int(b, days); + buffer_append_string_len(b, CONST_STR_LEN(" days ")); + } + + if (hours) { + buffer_append_int(b, hours); + buffer_append_string_len(b, CONST_STR_LEN(" hours ")); + } + + if (mins) { + buffer_append_int(b, mins); + buffer_append_string_len(b, CONST_STR_LEN(" min ")); + } + + buffer_append_int(b, seconds); + buffer_append_string_len(b, CONST_STR_LEN(" s")); + + buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Started at</td><td class=\"string\">")); + + ts = srv->startup_ts; + + strftime(buf, sizeof(buf) - 1, "%Y-%m-%d %H:%M:%S", localtime(&ts)); + buffer_append_string(b, buf); + buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); + + + buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">absolute (since start)</th></tr>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">")); + avg = p->abs_requests; + + mod_status_get_multiplier(&avg, &multiplier, 1000); + + buffer_append_int(b, avg); + buffer_append_string_len(b, CONST_STR_LEN(" ")); + if (multiplier) buffer_append_string_len(b, &multiplier, 1); + buffer_append_string_len(b, CONST_STR_LEN("req</td></tr>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">")); + avg = p->abs_traffic_out; + + mod_status_get_multiplier(&avg, &multiplier, 1024); + + snprintf(buf, sizeof(buf), "%.2f", avg); + buffer_append_string(b, buf); + buffer_append_string_len(b, CONST_STR_LEN(" ")); + if (multiplier) buffer_append_string_len(b, &multiplier, 1); + buffer_append_string_len(b, CONST_STR_LEN("byte</td></tr>\n")); + + + + buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (since start)</th></tr>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">")); + avg = p->abs_requests / (srv->cur_ts - srv->startup_ts); + + mod_status_get_multiplier(&avg, &multiplier, 1000); + + buffer_append_int(b, avg); + buffer_append_string_len(b, CONST_STR_LEN(" ")); + if (multiplier) buffer_append_string_len(b, &multiplier, 1); + buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">")); + avg = p->abs_traffic_out / (srv->cur_ts - srv->startup_ts); + + mod_status_get_multiplier(&avg, &multiplier, 1024); + + snprintf(buf, sizeof(buf), "%.2f", avg); + buffer_append_string(b, buf); + buffer_append_string_len(b, CONST_STR_LEN(" ")); + if (multiplier) buffer_append_string_len(b, &multiplier, 1); + buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n")); + + + + buffer_append_string_len(b, CONST_STR_LEN("<tr><th colspan=\"2\">average (5s sliding average)</th></tr>\n")); + for (j = 0, avg = 0; j < 5; j++) { + avg += p->mod_5s_requests[j]; + } + + avg /= 5; + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Requests</td><td class=\"string\">")); + + mod_status_get_multiplier(&avg, &multiplier, 1000); + + buffer_append_int(b, avg); + buffer_append_string_len(b, CONST_STR_LEN(" ")); + if (multiplier) buffer_append_string_len(b, &multiplier, 1); + + buffer_append_string_len(b, CONST_STR_LEN("req/s</td></tr>\n")); + + for (j = 0, avg = 0; j < 5; j++) { + avg += p->mod_5s_traffic_out[j]; + } + + avg /= 5; + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\">")); + + mod_status_get_multiplier(&avg, &multiplier, 1024); + + snprintf(buf, sizeof(buf), "%.2f", avg); + buffer_append_string(b, buf); + buffer_append_string_len(b, CONST_STR_LEN(" ")); + if (multiplier) buffer_append_string_len(b, &multiplier, 1); + buffer_append_string_len(b, CONST_STR_LEN("byte/s</td></tr>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("</table>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<hr />\n<pre>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<b>")); + buffer_append_int(b, srv->conns->used); + buffer_append_string_len(b, CONST_STR_LEN(" connections</b>\n")); + + for (j = 0; j < srv->conns->used; j++) { + connection *c = srv->conns->ptr[j]; + const char *state; + + if (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) { + state = "k"; + ++cstates[CON_STATE_CLOSE+2]; + } else { + state = connection_get_short_state(c->state); + ++cstates[(c->state <= CON_STATE_CLOSE ? c->state : CON_STATE_CLOSE+1)]; + } + + buffer_append_string_len(b, state, 1); + + if (((j + 1) % 50) == 0) { + buffer_append_string_len(b, CONST_STR_LEN("\n")); + } + } + buffer_append_string_len(b, CONST_STR_LEN("\n\n<table>\n")); + buffer_append_string_len(b, CONST_STR_LEN("<tr><td style=\"text-align:right\">")); + buffer_append_int(b, cstates[CON_STATE_CLOSE+2]); + buffer_append_string_len(b, CONST_STR_LEN("<td> k = keep-alive</td></tr>\n")); + for (j = 0; j < CON_STATE_CLOSE+2; ++j) { + /*(skip "unknown" state if there are none; there should not be any unknown)*/ + if (0 == cstates[j] && j == CON_STATE_CLOSE+1) continue; + buffer_append_string_len(b, CONST_STR_LEN("<tr><td style=\"text-align:right\">")); + buffer_append_int(b, cstates[j]); + buffer_append_string_len(b, CONST_STR_LEN("</td><td> ")); + buffer_append_string_len(b, connection_get_short_state(j), 1); + buffer_append_string_len(b, CONST_STR_LEN(" = ")); + buffer_append_string(b, connection_get_state(j)); + buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); + } + buffer_append_string_len(b, CONST_STR_LEN("</table>")); + + buffer_append_string_len(b, CONST_STR_LEN("\n</pre><hr />\n<h2>Connections</h2>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<table summary=\"status\" class=\"status\">\n")); + buffer_append_string_len(b, CONST_STR_LEN("<tr>")); + mod_status_header_append_sort(b, p_d, "Client IP"); + mod_status_header_append_sort(b, p_d, "Read"); + mod_status_header_append_sort(b, p_d, "Written"); + mod_status_header_append_sort(b, p_d, "State"); + mod_status_header_append_sort(b, p_d, "Time"); + mod_status_header_append_sort(b, p_d, "Host"); + mod_status_header_append_sort(b, p_d, "URI"); + mod_status_header_append_sort(b, p_d, "File"); + buffer_append_string_len(b, CONST_STR_LEN("</tr>\n")); + + for (j = 0; j < srv->conns->used; j++) { + connection *c = srv->conns->ptr[j]; + + buffer_append_string_len(b, CONST_STR_LEN("<tr><td class=\"string\">")); + + buffer_append_string_buffer(b, c->dst_addr_buf); + + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">")); + + if (c->request.content_length) { + buffer_append_int(b, c->request_content_queue->bytes_in); + buffer_append_string_len(b, CONST_STR_LEN("/")); + buffer_append_int(b, c->request.content_length); + } else { + buffer_append_string_len(b, CONST_STR_LEN("0/0")); + } + + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">")); + + buffer_append_int(b, c->write_queue->bytes_out); + buffer_append_string_len(b, CONST_STR_LEN("/")); + buffer_append_int(b, c->write_queue->bytes_out + chunkqueue_length(c->write_queue)); + + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); + + if (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) { + buffer_append_string_len(b, CONST_STR_LEN("keep-alive")); + } else { + buffer_append_string(b, connection_get_state(c->state)); + } + + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int\">")); + + buffer_append_int(b, srv->cur_ts - c->request_start); + + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); + + if (buffer_string_is_empty(c->server_name)) { + buffer_append_string_buffer(b, c->uri.authority); + } + else { + buffer_append_string_buffer(b, c->server_name); + } + + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); + + if (!buffer_string_is_empty(c->uri.path)) { + buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.path), ENCODING_HTML); + } + + if (!buffer_string_is_empty(c->uri.query)) { + buffer_append_string_len(b, CONST_STR_LEN("?")); + buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.query), ENCODING_HTML); + } + + if (!buffer_string_is_empty(c->request.orig_uri)) { + buffer_append_string_len(b, CONST_STR_LEN(" (")); + buffer_append_string_encoded(b, CONST_BUF_LEN(c->request.orig_uri), ENCODING_HTML); + buffer_append_string_len(b, CONST_STR_LEN(")")); + } + buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string\">")); + + buffer_append_string_buffer(b, c->physical.path); + + buffer_append_string_len(b, CONST_STR_LEN("</td></tr>\n")); + } + + + buffer_append_string_len(b, CONST_STR_LEN( + "</table>\n")); + + + buffer_append_string_len(b, CONST_STR_LEN( + " </body>\n" + "</html>\n" + )); + + chunkqueue_append_buffer_commit(con->write_queue); + + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); + + return 0; +} + + +static handler_t mod_status_handle_server_status_text(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + buffer *b = chunkqueue_append_buffer_open(con->write_queue); + double avg; + time_t ts; + char buf[32]; + unsigned int k; + unsigned int l; + + /* output total number of requests */ + buffer_append_string_len(b, CONST_STR_LEN("Total Accesses: ")); + avg = p->abs_requests; + snprintf(buf, sizeof(buf) - 1, "%.0f", avg); + buffer_append_string(b, buf); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + + /* output total traffic out in kbytes */ + buffer_append_string_len(b, CONST_STR_LEN("Total kBytes: ")); + avg = p->abs_traffic_out / 1024; + snprintf(buf, sizeof(buf) - 1, "%.0f", avg); + buffer_append_string(b, buf); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + + /* output uptime */ + buffer_append_string_len(b, CONST_STR_LEN("Uptime: ")); + ts = srv->cur_ts - srv->startup_ts; + buffer_append_int(b, ts); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + + /* output busy servers */ + buffer_append_string_len(b, CONST_STR_LEN("BusyServers: ")); + buffer_append_int(b, srv->conns->used); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + + buffer_append_string_len(b, CONST_STR_LEN("IdleServers: ")); + buffer_append_int(b, srv->conns->size - srv->conns->used); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + + /* output scoreboard */ + buffer_append_string_len(b, CONST_STR_LEN("Scoreboard: ")); + for (k = 0; k < srv->conns->used; k++) { + connection *c = srv->conns->ptr[k]; + const char *state = + (CON_STATE_READ == c->state && !buffer_string_is_empty(c->request.orig_uri)) + ? "k" + : connection_get_short_state(c->state); + buffer_append_string_len(b, state, 1); + } + for (l = 0; l < srv->conns->size - srv->conns->used; l++) { + buffer_append_string_len(b, CONST_STR_LEN("_")); + } + buffer_append_string_len(b, CONST_STR_LEN("\n")); + + chunkqueue_append_buffer_commit(con->write_queue); + + /* set text/plain output */ + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain")); + + return 0; +} + + +static handler_t mod_status_handle_server_status_json(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + buffer *b = chunkqueue_append_buffer_open(con->write_queue); + double avg; + time_t ts; + char buf[32]; + size_t j; + unsigned int jsonp = 0; + + if (buffer_string_length(con->uri.query) >= sizeof("jsonp=")-1 + && 0 == memcmp(con->uri.query->ptr, CONST_STR_LEN("jsonp="))) { + /* not a full parse of query string for multiple parameters, + * not URL-decoding param and not XML-encoding (XSS protection), + * so simply ensure that json function name isalnum() or '_' */ + const char *f = con->uri.query->ptr + sizeof("jsonp=")-1; + int len = 0; + while (light_isalnum(f[len]) || f[len] == '_') ++len; + if (0 != len && light_isalpha(f[0]) && f[len] == '\0') { + buffer_append_string_len(b, f, len); + buffer_append_string_len(b, CONST_STR_LEN("(")); + jsonp = 1; + } + } + + /* output total number of requests */ + buffer_append_string_len(b, CONST_STR_LEN("{\n\t\"RequestsTotal\": ")); + avg = p->abs_requests; + snprintf(buf, sizeof(buf) - 1, "%.0f", avg); + buffer_append_string(b, buf); + buffer_append_string_len(b, CONST_STR_LEN(",\n")); + + /* output total traffic out in kbytes */ + buffer_append_string_len(b, CONST_STR_LEN("\t\"TrafficTotal\": ")); + avg = p->abs_traffic_out / 1024; + snprintf(buf, sizeof(buf) - 1, "%.0f", avg); + buffer_append_string(b, buf); + buffer_append_string_len(b, CONST_STR_LEN(",\n")); + + /* output uptime */ + buffer_append_string_len(b, CONST_STR_LEN("\t\"Uptime\": ")); + ts = srv->cur_ts - srv->startup_ts; + buffer_append_int(b, ts); + buffer_append_string_len(b, CONST_STR_LEN(",\n")); + + /* output busy servers */ + buffer_append_string_len(b, CONST_STR_LEN("\t\"BusyServers\": ")); + buffer_append_int(b, srv->conns->used); + buffer_append_string_len(b, CONST_STR_LEN(",\n")); + + buffer_append_string_len(b, CONST_STR_LEN("\t\"IdleServers\": ")); + buffer_append_int(b, srv->conns->size - srv->conns->used); + buffer_append_string_len(b, CONST_STR_LEN(",\n")); + + for (j = 0, avg = 0; j < 5; j++) { + avg += p->mod_5s_requests[j]; + } + + avg /= 5; + + buffer_append_string_len(b, CONST_STR_LEN("\t\"RequestAverage5s\":")); + buffer_append_int(b, avg); + buffer_append_string_len(b, CONST_STR_LEN(",\n")); + + for (j = 0, avg = 0; j < 5; j++) { + avg += p->mod_5s_traffic_out[j]; + } + + avg /= 5; + + buffer_append_string_len(b, CONST_STR_LEN("\t\"TrafficAverage5s\":")); + buffer_append_int(b, avg / 1024); /* kbps */ + buffer_append_string_len(b, CONST_STR_LEN("\n}")); + + if (jsonp) buffer_append_string_len(b, CONST_STR_LEN(");")); + + chunkqueue_append_buffer_commit(con->write_queue); + + /* set text/plain output */ + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/javascript")); + + return 0; +} + + +static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) { + buffer *b; + size_t i; + array *st = srv->status; + UNUSED(p_d); + + if (0 == st->used) { + /* we have nothing to send */ + con->http_status = 204; + con->file_finished = 1; + + return HANDLER_FINISHED; + } + + b = chunkqueue_append_buffer_open(con->write_queue); + for (i = 0; i < st->used; i++) { + size_t ndx = st->sorted[i]; + + buffer_append_string_buffer(b, st->data[ndx]->key); + buffer_append_string_len(b, CONST_STR_LEN(": ")); + buffer_append_int(b, ((data_integer *)(st->data[ndx]))->value); + buffer_append_string_len(b, CONST_STR_LEN("\n")); + } + chunkqueue_append_buffer_commit(con->write_queue); + + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain")); + + con->http_status = 200; + con->file_finished = 1; + + return HANDLER_FINISHED; +} + + +static handler_t mod_status_handle_server_status(server *srv, connection *con, void *p_d) { + + if (buffer_is_equal_string(con->uri.query, CONST_STR_LEN("auto"))) { + mod_status_handle_server_status_text(srv, con, p_d); + } else if (buffer_string_length(con->uri.query) >= sizeof("json")-1 + && 0 == memcmp(con->uri.query->ptr, CONST_STR_LEN("json"))) { + mod_status_handle_server_status_json(srv, con, p_d); + } else { + mod_status_handle_server_status_html(srv, con, p_d); + } + + con->http_status = 200; + con->file_finished = 1; + + return HANDLER_FINISHED; +} + + +static handler_t mod_status_handle_server_config(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + buffer *b = chunkqueue_append_buffer_open(con->write_queue); + buffer *m = p->module_list; + size_t i; + + buffer_copy_string_len(b, CONST_STR_LEN( + "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" + "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" + " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n" + " <head>\n" + " <title>Status</title>\n" + " </head>\n" + " <body>\n" + " <h1>")); + buffer_append_string_buffer(b, con->conf.server_tag); + buffer_append_string_len(b, CONST_STR_LEN( + "</h1>\n" + " <table summary=\"status\" border=\"1\">\n")); + + mod_status_header_append(b, "Server-Features"); +#ifdef HAVE_PCRE_H + mod_status_row_append(b, "RegEx Conditionals", "enabled"); +#else + mod_status_row_append(b, "RegEx Conditionals", "disabled - pcre missing"); +#endif + mod_status_header_append(b, "Network Engine"); + + mod_status_row_append(b, "fd-Event-Handler", srv->srvconf.event_handler->ptr); + + mod_status_header_append(b, "Config-File-Settings"); + + for (i = 0; i < srv->plugins.used; i++) { + plugin **ps = srv->plugins.ptr; + + plugin *pl = ps[i]; + + if (i == 0) { + buffer_copy_buffer(m, pl->name); + } else { + buffer_append_string_len(m, CONST_STR_LEN("<br />")); + buffer_append_string_buffer(m, pl->name); + } + } + + mod_status_row_append(b, "Loaded Modules", m->ptr); + + buffer_append_string_len(b, CONST_STR_LEN(" </table>\n")); + + buffer_append_string_len(b, CONST_STR_LEN( + " </body>\n" + "</html>\n" + )); + + chunkqueue_append_buffer_commit(con->write_queue); + + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); + + con->http_status = 200; + con->file_finished = 1; + + return HANDLER_FINISHED; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_status_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(status_url); + PATCH(config_url); + PATCH(sort); + PATCH(statistics_url); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.status-url"))) { + PATCH(status_url); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.config-url"))) { + PATCH(config_url); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.enable-sort"))) { + PATCH(sort); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.statistics-url"))) { + PATCH(statistics_url); + } + } + } + + return 0; +} + +static handler_t mod_status_handler(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + mod_status_patch_connection(srv, con, p); + + if (!buffer_string_is_empty(p->conf.status_url) && + buffer_is_equal(p->conf.status_url, con->uri.path)) { + return mod_status_handle_server_status(srv, con, p_d); + } else if (!buffer_string_is_empty(p->conf.config_url) && + buffer_is_equal(p->conf.config_url, con->uri.path)) { + return mod_status_handle_server_config(srv, con, p_d); + } else if (!buffer_string_is_empty(p->conf.statistics_url) && + buffer_is_equal(p->conf.statistics_url, con->uri.path)) { + return mod_status_handle_server_statistics(srv, con, p_d); + } + + return HANDLER_GO_ON; +} + +TRIGGER_FUNC(mod_status_trigger) { + plugin_data *p = p_d; + size_t i; + + /* check all connections */ + for (i = 0; i < srv->conns->used; i++) { + connection *c = srv->conns->ptr[i]; + + p->bytes_written += c->bytes_written_cur_second; + } + + /* a sliding average */ + p->mod_5s_traffic_out[p->mod_5s_ndx] = p->bytes_written; + p->mod_5s_requests [p->mod_5s_ndx] = p->requests; + + p->mod_5s_ndx = (p->mod_5s_ndx+1) % 5; + + p->abs_traffic_out += p->bytes_written; + p->rel_traffic_out += p->bytes_written; + + p->bytes_written = 0; + + /* reset storage - second */ + p->traffic_out = 0; + p->requests = 0; + + return HANDLER_GO_ON; +} + +REQUESTDONE_FUNC(mod_status_account) { + plugin_data *p = p_d; + + UNUSED(srv); + + p->requests++; + p->rel_requests++; + p->abs_requests++; + + p->bytes_written += con->bytes_written_cur_second; + + return HANDLER_GO_ON; +} + +int mod_status_plugin_init(plugin *p); +int mod_status_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("status"); + + p->init = mod_status_init; + p->cleanup = mod_status_free; + p->set_defaults= mod_status_set_defaults; + + p->handle_uri_clean = mod_status_handler; + p->handle_trigger = mod_status_trigger; + p->handle_request_done = mod_status_account; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_trigger_b4_dl.c b/data/lighttpd/lighttpd-1.4.53/src/mod_trigger_b4_dl.c new file mode 100644 index 000000000..6a43b25f8 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_trigger_b4_dl.c @@ -0,0 +1,607 @@ +#include "first.h" + +#include "base.h" +#include "fdevent.h" +#include "log.h" +#include "buffer.h" +#include "http_header.h" + +#include "plugin.h" + +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> + +#if defined(HAVE_GDBM_H) +# include <gdbm.h> +#endif + +#if defined(HAVE_PCRE_H) +# include <pcre.h> +#endif + +#if defined(USE_MEMCACHED) +# include <libmemcached/memcached.h> +#endif + +/** + * this is a trigger_b4_dl for a lighttpd plugin + * + */ + +/* plugin config for all request/connections */ + +typedef struct { + buffer *db_filename; + + buffer *trigger_url; + buffer *download_url; + buffer *deny_url; + + array *mc_hosts; + buffer *mc_namespace; +#if defined(HAVE_PCRE_H) + pcre *trigger_regex; + pcre *download_regex; +#endif +#if defined(HAVE_GDBM_H) + GDBM_FILE db; +#endif + +#if defined(USE_MEMCACHED) + memcached_st *memc; +#endif + + unsigned short trigger_timeout; + unsigned short debug; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + buffer *tmp_buf; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_trigger_b4_dl_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->tmp_buf = buffer_init(); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_trigger_b4_dl_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->db_filename); + buffer_free(s->download_url); + buffer_free(s->trigger_url); + buffer_free(s->deny_url); + + buffer_free(s->mc_namespace); + array_free(s->mc_hosts); + +#if defined(HAVE_PCRE_H) + if (s->trigger_regex) pcre_free(s->trigger_regex); + if (s->download_regex) pcre_free(s->download_regex); +#endif +#if defined(HAVE_GDBM_H) + if (s->db) gdbm_close(s->db); +#endif +#if defined(USE_MEMCACHED) + if (s->memc) memcached_free(s->memc); +#endif + + free(s); + } + free(p->config_storage); + } + + buffer_free(p->tmp_buf); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_trigger_b4_dl_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + + config_values_t cv[] = { + { "trigger-before-download.gdbm-filename", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "trigger-before-download.trigger-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "trigger-before-download.download-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "trigger-before-download.deny-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "trigger-before-download.trigger-timeout", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "trigger-before-download.memcache-hosts", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { "trigger-before-download.memcache-namespace", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ + { "trigger-before-download.debug", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; +#if defined(HAVE_PCRE_H) + const char *errptr; + int erroff; +#endif + + s = calloc(1, sizeof(plugin_config)); + s->db_filename = buffer_init(); + s->download_url = buffer_init(); + s->trigger_url = buffer_init(); + s->deny_url = buffer_init(); + s->mc_hosts = array_init(); + s->mc_namespace = buffer_init(); + + cv[0].destination = s->db_filename; + cv[1].destination = s->trigger_url; + cv[2].destination = s->download_url; + cv[3].destination = s->deny_url; + cv[4].destination = &(s->trigger_timeout); + cv[5].destination = s->mc_hosts; + cv[6].destination = s->mc_namespace; + cv[7].destination = &(s->debug); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } +#if defined(HAVE_GDBM_H) + if (!buffer_string_is_empty(s->db_filename)) { + if (NULL == (s->db = gdbm_open(s->db_filename->ptr, 4096, GDBM_WRCREAT | GDBM_NOLOCK, S_IRUSR | S_IWUSR, 0))) { + log_error_write(srv, __FILE__, __LINE__, "s", + "gdbm-open failed"); + return HANDLER_ERROR; + } + fdevent_setfd_cloexec(gdbm_fdesc(s->db)); + } +#endif +#if defined(HAVE_PCRE_H) + if (!buffer_string_is_empty(s->download_url)) { + if (NULL == (s->download_regex = pcre_compile(s->download_url->ptr, + 0, &errptr, &erroff, NULL))) { + + log_error_write(srv, __FILE__, __LINE__, "sbss", + "compiling regex for download-url failed:", + s->download_url, "pos:", erroff); + return HANDLER_ERROR; + } + } + + if (!buffer_string_is_empty(s->trigger_url)) { + if (NULL == (s->trigger_regex = pcre_compile(s->trigger_url->ptr, + 0, &errptr, &erroff, NULL))) { + + log_error_write(srv, __FILE__, __LINE__, "sbss", + "compiling regex for trigger-url failed:", + s->trigger_url, "pos:", erroff); + + return HANDLER_ERROR; + } + } +#endif + + if (!array_is_vlist(s->mc_hosts)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for trigger-before-download.memcache-hosts; expected list of \"host\""); + return HANDLER_ERROR; + } + + if (s->mc_hosts->used) { +#if defined(USE_MEMCACHED) + buffer *option_string = buffer_init(); + size_t k; + + { + data_string *ds = (data_string *)s->mc_hosts->data[0]; + + buffer_append_string_len(option_string, CONST_STR_LEN("--SERVER=")); + buffer_append_string_buffer(option_string, ds->value); + } + + for (k = 1; k < s->mc_hosts->used; k++) { + data_string *ds = (data_string *)s->mc_hosts->data[k]; + + buffer_append_string_len(option_string, CONST_STR_LEN(" --SERVER=")); + buffer_append_string_buffer(option_string, ds->value); + } + + s->memc = memcached(CONST_BUF_LEN(option_string)); + + if (NULL == s->memc) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "configuring memcached failed for option string:", + option_string); + } + buffer_free(option_string); + + if (NULL == s->memc) return HANDLER_ERROR; +#else + log_error_write(srv, __FILE__, __LINE__, "s", + "memcache support is not compiled in but trigger-before-download.memcache-hosts is set, aborting"); + return HANDLER_ERROR; +#endif + } + } + + return HANDLER_GO_ON; +} + +#if defined(HAVE_PCRE_H) + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_trigger_b4_dl_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + +#if defined(HAVE_GDBM) + PATCH(db); +#endif +#if defined(HAVE_PCRE_H) + PATCH(download_regex); + PATCH(trigger_regex); +#endif + PATCH(trigger_timeout); + PATCH(deny_url); + PATCH(mc_namespace); + PATCH(debug); +#if defined(USE_MEMCACHED) + PATCH(memc); +#endif + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.download-url"))) { +#if defined(HAVE_PCRE_H) + PATCH(download_regex); +#endif + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.trigger-url"))) { +# if defined(HAVE_PCRE_H) + PATCH(trigger_regex); +# endif + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.gdbm-filename"))) { +#if defined(HAVE_GDBM_H) + PATCH(db); +#endif + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.trigger-timeout"))) { + PATCH(trigger_timeout); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.debug"))) { + PATCH(debug); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.deny-url"))) { + PATCH(deny_url); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.memcache-namespace"))) { + PATCH(mc_namespace); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.memcache-hosts"))) { +#if defined(USE_MEMCACHED) + PATCH(memc); +#endif + } + } + } + + return 0; +} +#undef PATCH + +#endif + +URIHANDLER_FUNC(mod_trigger_b4_dl_uri_handler) { +#if defined(HAVE_PCRE_H) + plugin_data *p = p_d; + const char *remote_ip; + buffer *vb; + + int n; +# define N 10 + int ovec[N * 3]; + + if (con->mode != DIRECT) return HANDLER_GO_ON; + + if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; + + mod_trigger_b4_dl_patch_connection(srv, con, p); + + if (!p->conf.trigger_regex || !p->conf.download_regex) return HANDLER_GO_ON; + +# if !defined(HAVE_GDBM_H) && !defined(USE_MEMCACHED) + return HANDLER_GO_ON; +# elif defined(HAVE_GDBM_H) && defined(USE_MEMCACHED) + if (!p->conf.db && !p->conf.memc) return HANDLER_GO_ON; + if (p->conf.db && p->conf.memc) { + /* can't decide which one */ + + return HANDLER_GO_ON; + } +# elif defined(HAVE_GDBM_H) + if (!p->conf.db) return HANDLER_GO_ON; +# else + if (!p->conf.memc) return HANDLER_GO_ON; +# endif + + if (NULL != (vb = http_header_request_get(con, HTTP_HEADER_X_FORWARDED_FOR, CONST_STR_LEN("X-Forwarded-For")))) { + /* X-Forwarded-For contains the ip behind the proxy */ + + remote_ip = vb->ptr; + + /* memcache can't handle spaces */ + } else { + remote_ip = con->dst_addr_buf->ptr; + } + + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "ss", "(debug) remote-ip:", remote_ip); + } + + /* check if URL is a trigger -> insert IP into DB */ + if ((n = pcre_exec(p->conf.trigger_regex, NULL, CONST_BUF_LEN(con->uri.path), 0, 0, ovec, 3 * N)) < 0) { + if (n != PCRE_ERROR_NOMATCH) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "execution error while matching:", n); + + return HANDLER_ERROR; + } + } else { +# if defined(HAVE_GDBM_H) + if (p->conf.db) { + /* the trigger matched */ + datum key, val; + + key.dptr = (char *)remote_ip; + key.dsize = strlen(remote_ip); + + val.dptr = (char *)&(srv->cur_ts); + val.dsize = sizeof(srv->cur_ts); + + if (0 != gdbm_store(p->conf.db, key, val, GDBM_REPLACE)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "insert failed"); + } + } +# endif +# if defined(USE_MEMCACHED) + if (p->conf.memc) { + size_t i, len; + buffer_copy_buffer(p->tmp_buf, p->conf.mc_namespace); + buffer_append_string(p->tmp_buf, remote_ip); + + len = buffer_string_length(p->tmp_buf); + for (i = 0; i < len; i++) { + if (p->tmp_buf->ptr[i] == ' ') p->tmp_buf->ptr[i] = '-'; + } + + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "sb", "(debug) triggered IP:", p->tmp_buf); + } + + if (MEMCACHED_SUCCESS != memcached_set(p->conf.memc, + CONST_BUF_LEN(p->tmp_buf), + (const char *)&(srv->cur_ts), sizeof(srv->cur_ts), + p->conf.trigger_timeout, 0)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "insert failed"); + } + } +# endif + } + + /* check if URL is a download -> check IP in DB, update timestamp */ + if ((n = pcre_exec(p->conf.download_regex, NULL, CONST_BUF_LEN(con->uri.path), 0, 0, ovec, 3 * N)) < 0) { + if (n != PCRE_ERROR_NOMATCH) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "execution error while matching: ", n); + return HANDLER_ERROR; + } + } else { + /* the download uri matched */ +# if defined(HAVE_GDBM_H) + if (p->conf.db) { + datum key, val; + time_t last_hit; + + key.dptr = (char *)remote_ip; + key.dsize = strlen(remote_ip); + + val = gdbm_fetch(p->conf.db, key); + + if (val.dptr == NULL) { + /* not found, redirect */ + + http_header_response_set(con, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url)); + con->http_status = 307; + con->file_finished = 1; + + return HANDLER_FINISHED; + } + + memcpy(&last_hit, val.dptr, sizeof(time_t)); + + free(val.dptr); + + if (srv->cur_ts - last_hit > p->conf.trigger_timeout) { + /* found, but timeout, redirect */ + + http_header_response_set(con, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url)); + con->http_status = 307; + con->file_finished = 1; + + if (p->conf.db) { + if (0 != gdbm_delete(p->conf.db, key)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "delete failed"); + } + } + + return HANDLER_FINISHED; + } + + val.dptr = (char *)&(srv->cur_ts); + val.dsize = sizeof(srv->cur_ts); + + if (0 != gdbm_store(p->conf.db, key, val, GDBM_REPLACE)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "insert failed"); + } + } +# endif + +# if defined(USE_MEMCACHED) + if (p->conf.memc) { + size_t i, len; + + buffer_copy_buffer(p->tmp_buf, p->conf.mc_namespace); + buffer_append_string(p->tmp_buf, remote_ip); + + len = buffer_string_length(p->tmp_buf); + for (i = 0; i < len; i++) { + if (p->tmp_buf->ptr[i] == ' ') p->tmp_buf->ptr[i] = '-'; + } + + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "sb", "(debug) checking IP:", p->tmp_buf); + } + + /** + * + * memcached is do expiration for us, as long as we can fetch it every thing is ok + * and the timestamp is updated + * + */ + if (MEMCACHED_SUCCESS != memcached_exist(p->conf.memc, CONST_BUF_LEN(p->tmp_buf))) { + http_header_response_set(con, HTTP_HEADER_LOCATION, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url)); + + con->http_status = 307; + con->file_finished = 1; + + return HANDLER_FINISHED; + } + + /* set a new timeout */ + if (MEMCACHED_SUCCESS != memcached_set(p->conf.memc, + CONST_BUF_LEN(p->tmp_buf), + (const char *)&(srv->cur_ts), sizeof(srv->cur_ts), + p->conf.trigger_timeout, 0)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "insert failed"); + } + } +# endif + } + +#else + UNUSED(srv); + UNUSED(con); + UNUSED(p_d); +#endif + + return HANDLER_GO_ON; +} + +#if defined(HAVE_GDBM_H) +TRIGGER_FUNC(mod_trigger_b4_dl_handle_trigger) { + plugin_data *p = p_d; + size_t i; + + /* check DB each minute */ + if (srv->cur_ts % 60 != 0) return HANDLER_GO_ON; + + /* cleanup */ + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + datum key, val, okey; + + if (!s->db) continue; + + okey.dptr = NULL; + + /* according to the manual this loop + delete does delete all entries on its way + * + * we don't care as the next round will remove them. We don't have to perfect here. + */ + for (key = gdbm_firstkey(s->db); key.dptr; key = gdbm_nextkey(s->db, okey)) { + time_t last_hit; + if (okey.dptr) { + free(okey.dptr); + okey.dptr = NULL; + } + + val = gdbm_fetch(s->db, key); + + memcpy(&last_hit, val.dptr, sizeof(time_t)); + + free(val.dptr); + + if (srv->cur_ts - last_hit > s->trigger_timeout) { + gdbm_delete(s->db, key); + } + + okey = key; + } + if (okey.dptr) free(okey.dptr); + + /* reorg once a day */ + if ((srv->cur_ts % (60 * 60 * 24) != 0)) gdbm_reorganize(s->db); + } + return HANDLER_GO_ON; +} +#endif + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_trigger_b4_dl_plugin_init(plugin *p); +int mod_trigger_b4_dl_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("trigger_b4_dl"); + + p->init = mod_trigger_b4_dl_init; + p->handle_uri_clean = mod_trigger_b4_dl_uri_handler; + p->set_defaults = mod_trigger_b4_dl_set_defaults; +#if defined(HAVE_GDBM_H) + p->handle_trigger = mod_trigger_b4_dl_handle_trigger; +#endif + p->cleanup = mod_trigger_b4_dl_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_uploadprogress.c b/data/lighttpd/lighttpd-1.4.53/src/mod_uploadprogress.c new file mode 100644 index 000000000..3dbd61e1b --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_uploadprogress.c @@ -0,0 +1,413 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "http_header.h" + +#include "plugin.h" + +#include <stdlib.h> +#include <string.h> + +/** + * this is a uploadprogress for a lighttpd plugin + * + */ + +typedef struct { + buffer *con_id; + connection *con; +} connection_map_entry; + +typedef struct { + connection_map_entry **ptr; + + size_t used; + size_t size; +} connection_map; + +/* plugin config for all request/connections */ + +typedef struct { + buffer *progress_url; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + connection_map *con_map; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/** + * + * connection maps + * + */ + +/* init the plugin data */ +static connection_map *connection_map_init() { + connection_map *cm; + + cm = calloc(1, sizeof(*cm)); + + return cm; +} + +static void connection_map_free(connection_map *cm) { + size_t i; + for (i = 0; i < cm->size; i++) { + connection_map_entry *cme = cm->ptr[i]; + + if (!cme) break; + + if (cme->con_id) { + buffer_free(cme->con_id); + } + free(cme); + } + + free(cm); +} + +static int connection_map_insert(connection_map *cm, connection *con, const char *con_id, size_t idlen) { + connection_map_entry *cme; + size_t i; + + if (cm->size == 0) { + cm->size = 16; + cm->ptr = malloc(cm->size * sizeof(*(cm->ptr))); + for (i = 0; i < cm->size; i++) { + cm->ptr[i] = NULL; + } + } else if (cm->used == cm->size) { + cm->size += 16; + cm->ptr = realloc(cm->ptr, cm->size * sizeof(*(cm->ptr))); + for (i = cm->used; i < cm->size; i++) { + cm->ptr[i] = NULL; + } + } + + if (cm->ptr[cm->used]) { + /* is already alloced, just reuse it */ + cme = cm->ptr[cm->used]; + } else { + cme = malloc(sizeof(*cme)); + cme->con_id = buffer_init(); + } + buffer_copy_string_len(cme->con_id, con_id, idlen); + cme->con = con; + + cm->ptr[cm->used++] = cme; + + return 0; +} + +static connection *connection_map_get_connection(connection_map *cm, const char *con_id, size_t idlen) { + size_t i; + + for (i = 0; i < cm->used; i++) { + connection_map_entry *cme = cm->ptr[i]; + + if (buffer_is_equal_string(cme->con_id, con_id, idlen)) { + /* found connection */ + + return cme->con; + } + } + return NULL; +} + +static int connection_map_remove_connection(connection_map *cm, connection *con) { + size_t i; + + for (i = 0; i < cm->used; i++) { + connection_map_entry *cme = cm->ptr[i]; + + if (cme->con == con) { + /* found connection */ + + buffer_clear(cme->con_id); + cme->con = NULL; + + cm->used--; + + /* swap positions with the last entry */ + if (cm->used) { + cm->ptr[i] = cm->ptr[cm->used]; + cm->ptr[cm->used] = cme; + } + + return 1; + } + } + + return 0; +} + +/* init the plugin data */ +INIT_FUNC(mod_uploadprogress_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->con_map = connection_map_init(); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_uploadprogress_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->progress_url); + + free(s); + } + free(p->config_storage); + } + + connection_map_free(p->con_map); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_uploadprogress_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "upload-progress.progress-url", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->progress_url = buffer_init(); + + cv[0].destination = s->progress_url; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_uploadprogress_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(progress_url); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("upload-progress.progress-url"))) { + PATCH(progress_url); + } + } + } + + return 0; +} +#undef PATCH + +/** + * + * the idea: + * + * for the first request we check if it is a post-request + * + * if no, move out, don't care about them + * + * if yes, take the connection structure and register it locally + * in the progress-struct together with an session-id (md5 ... ) + * + * if the connections closes, cleanup the entry in the progress-struct + * + * a second request can now get the info about the size of the upload, + * the received bytes + * + */ + +URIHANDLER_FUNC(mod_uploadprogress_uri_handler) { + plugin_data *p = p_d; + size_t len; + char *id; + buffer *b; + connection *post_con = NULL; + int pathinfo = 0; + + if (buffer_string_is_empty(con->uri.path)) return HANDLER_GO_ON; + switch(con->request.http_method) { + case HTTP_METHOD_GET: + case HTTP_METHOD_POST: break; + default: return HANDLER_GO_ON; + } + + mod_uploadprogress_patch_connection(srv, con, p); + if (buffer_string_is_empty(p->conf.progress_url)) return HANDLER_GO_ON; + + if (con->request.http_method == HTTP_METHOD_GET) { + if (!buffer_is_equal(con->uri.path, p->conf.progress_url)) { + return HANDLER_GO_ON; + } + } + + if (NULL != (b = http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("X-Progress-ID")))) { + id = b->ptr; + } else if (!buffer_string_is_empty(con->uri.query) + && (id = strstr(con->uri.query->ptr, "X-Progress-ID="))) { + /* perhaps the POST request is using the query-string to pass the X-Progress-ID */ + id += sizeof("X-Progress-ID=")-1; + } else { + /*(path-info is not known at this point in request)*/ + id = con->uri.path->ptr; + len = buffer_string_length(con->uri.path); + if (len >= 33 && id[len-33] == '/') { + id += len - 32; + pathinfo = 1; + } else { + return HANDLER_GO_ON; + } + } + + /* the request has to contain a 32byte ID */ + for (len = 0; light_isxdigit(id[len]); ++len) ; + if (len != 32) { + if (!pathinfo) { /*(reduce false positive noise in error log)*/ + log_error_write(srv, __FILE__, __LINE__, "ss", + "invalid progress-id; non-xdigit or len != 32:", id); + } + return HANDLER_GO_ON; + } + + /* check if this is a POST request */ + switch(con->request.http_method) { + case HTTP_METHOD_POST: + + connection_map_insert(p->con_map, con, id, len); + + return HANDLER_GO_ON; + case HTTP_METHOD_GET: + buffer_reset(con->physical.path); + + con->file_started = 1; + con->file_finished = 1; + + con->http_status = 200; + con->mode = DIRECT; + + /* get the connection */ + if (NULL == (post_con = connection_map_get_connection(p->con_map, id, len))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "ID not known:", id); + + chunkqueue_append_mem(con->write_queue, CONST_STR_LEN("not in progress")); + + return HANDLER_FINISHED; + } + + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml")); + + /* just an attempt the force the IE/proxies to NOT cache the request ... doesn't help :( */ + http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Pragma"), CONST_STR_LEN("no-cache")); + http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Expires"), CONST_STR_LEN("Thu, 19 Nov 1981 08:52:00 GMT")); + http_header_response_set(con, HTTP_HEADER_CACHE_CONTROL, CONST_STR_LEN("Cache-Control"), CONST_STR_LEN("no-store, no-cache, must-revalidate, post-check=0, pre-check=0")); + + /* prepare XML */ + b = srv->tmp_buf; + buffer_copy_string_len(b, CONST_STR_LEN( + "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>" + "<upload>" + "<size>")); + buffer_append_int(b, post_con->request.content_length); + buffer_append_string_len(b, CONST_STR_LEN( + "</size>" + "<received>")); + buffer_append_int(b, post_con->request_content_queue->bytes_in); + buffer_append_string_len(b, CONST_STR_LEN( + "</received>" + "</upload>")); + chunkqueue_append_mem(con->write_queue, CONST_BUF_LEN(b)); + return HANDLER_FINISHED; + default: + break; + } + + return HANDLER_GO_ON; +} + +REQUESTDONE_FUNC(mod_uploadprogress_request_done) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (con->request.http_method != HTTP_METHOD_POST) return HANDLER_GO_ON; + if (buffer_string_is_empty(con->uri.path)) return HANDLER_GO_ON; + + if (connection_map_remove_connection(p->con_map, con)) { + /* removed */ + } + + return HANDLER_GO_ON; +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_uploadprogress_plugin_init(plugin *p); +int mod_uploadprogress_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("uploadprogress"); + + p->init = mod_uploadprogress_init; + p->handle_uri_clean = mod_uploadprogress_uri_handler; + p->connection_reset = mod_uploadprogress_request_done; + p->set_defaults = mod_uploadprogress_set_defaults; + p->cleanup = mod_uploadprogress_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_userdir.c b/data/lighttpd/lighttpd-1.4.53/src/mod_userdir.c new file mode 100644 index 000000000..6389d9fab --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_userdir.c @@ -0,0 +1,349 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" + +#include "response.h" + +#include "plugin.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_PWD_H +# include <pwd.h> +#endif + +/* plugin config for all request/connections */ +typedef struct { + array *exclude_user; + array *include_user; + buffer *path; + buffer *basepath; + unsigned short letterhomes; + unsigned short active; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + buffer *username; + buffer *temp_path; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_userdir_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->username = buffer_init(); + p->temp_path = buffer_init(); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_userdir_free) { + plugin_data *p = p_d; + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + array_free(s->include_user); + array_free(s->exclude_user); + buffer_free(s->path); + buffer_free(s->basepath); + + free(s); + } + free(p->config_storage); + } + + buffer_free(p->username); + buffer_free(p->temp_path); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_userdir_set_defaults) { + plugin_data *p = p_d; + size_t i; + + config_values_t cv[] = { + { "userdir.path", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "userdir.exclude-user", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "userdir.include-user", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "userdir.basepath", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { "userdir.letterhomes", NULL, T_CONFIG_BOOLEAN,T_CONFIG_SCOPE_CONNECTION }, /* 4 */ + { "userdir.active", NULL, T_CONFIG_BOOLEAN,T_CONFIG_SCOPE_CONNECTION }, /* 5 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->exclude_user = array_init(); + s->include_user = array_init(); + s->path = buffer_init(); + s->basepath = buffer_init(); + s->letterhomes = 0; + /* enabled by default for backward compatibility; if userdir.path isn't set userdir is disabled too, + * but you can't disable it by setting it to an empty string. */ + s->active = 1; + + cv[0].destination = s->path; + cv[1].destination = s->exclude_user; + cv[2].destination = s->include_user; + cv[3].destination = s->basepath; + cv[4].destination = &(s->letterhomes); + cv[5].destination = &(s->active); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_vlist(s->exclude_user)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for userdir.exclude-user; expected list of \"suffix\""); + return HANDLER_ERROR; + } + + if (!array_is_vlist(s->include_user)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for userdir.include-user; expected list of \"suffix\""); + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_userdir_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(path); + PATCH(exclude_user); + PATCH(include_user); + PATCH(basepath); + PATCH(letterhomes); + PATCH(active); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.path"))) { + PATCH(path); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.exclude-user"))) { + PATCH(exclude_user); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.include-user"))) { + PATCH(include_user); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.basepath"))) { + PATCH(basepath); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.letterhomes"))) { + PATCH(letterhomes); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.active"))) { + PATCH(active); + } + } + } + + return 0; +} +#undef PATCH + +URIHANDLER_FUNC(mod_userdir_docroot_handler) { + plugin_data *p = p_d; + size_t k; + char *rel_url; +#ifdef HAVE_PWD_H + struct passwd *pwd = NULL; +#endif + + if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; + + mod_userdir_patch_connection(srv, con, p); + + /* enforce the userdir.path to be set in the config, ugly fix for #1587; + * should be replaced with a clean .enabled option in 1.5 + */ + if (!p->conf.active || buffer_is_empty(p->conf.path)) return HANDLER_GO_ON; + + /* /~user/foo.html -> /home/user/public_html/foo.html */ + + if (con->uri.path->ptr[0] != '/' || + con->uri.path->ptr[1] != '~') return HANDLER_GO_ON; + + if (NULL == (rel_url = strchr(con->uri.path->ptr + 2, '/'))) { + /* / is missing -> redirect to .../ as we are a user - DIRECTORY ! :) */ + http_response_redirect_to_directory(srv, con); + + return HANDLER_FINISHED; + } + + /* /~/ is a empty username, catch it directly */ + if (0 == rel_url - (con->uri.path->ptr + 2)) { + return HANDLER_GO_ON; + } + + buffer_copy_string_len(p->username, con->uri.path->ptr + 2, rel_url - (con->uri.path->ptr + 2)); + + if (buffer_string_is_empty(p->conf.basepath) +#ifdef HAVE_PWD_H + && NULL == (pwd = getpwnam(p->username->ptr)) +#endif + ) { + /* user not found */ + return HANDLER_GO_ON; + } + + + for (k = 0; k < p->conf.exclude_user->used; k++) { + data_string *ds = (data_string *)p->conf.exclude_user->data[k]; + + if (buffer_is_equal(ds->value, p->username)) { + /* user in exclude list */ + return HANDLER_GO_ON; + } + } + + if (p->conf.include_user->used) { + int found_user = 0; + for (k = 0; k < p->conf.include_user->used; k++) { + data_string *ds = (data_string *)p->conf.include_user->data[k]; + + if (buffer_is_equal(ds->value, p->username)) { + /* user in include list */ + found_user = 1; + break; + } + } + + if (!found_user) return HANDLER_GO_ON; + } + + /* we build the physical path */ + buffer_clear(p->temp_path); + + if (buffer_string_is_empty(p->conf.basepath)) { +#ifdef HAVE_PWD_H + buffer_copy_string(p->temp_path, pwd->pw_dir); +#endif + } else { + char *cp = p->username->ptr; + /* check if the username is valid + * a request for /~../ should lead to a directory traversal + * limiting to [-_a-z0-9.] should fix it */ + if (cp[0] == '.' && (cp[1] == '\0' || (cp[1] == '.' && cp[2] == '\0'))) { + return HANDLER_GO_ON; + } + + for (; *cp; cp++) { + char c = *cp; + if (!(light_isalnum(c) || c == '-' || c == '_' || c == '.')) { + return HANDLER_GO_ON; + } + } + if (con->conf.force_lowercase_filenames) { + buffer_to_lower(p->username); + } + + buffer_copy_buffer(p->temp_path, p->conf.basepath); + if (p->conf.letterhomes) { + if (p->username->ptr[0] == '.') return HANDLER_GO_ON; + buffer_append_path_len(p->temp_path, p->username->ptr, 1); + } + buffer_append_path_len(p->temp_path, CONST_BUF_LEN(p->username)); + } + buffer_append_path_len(p->temp_path, CONST_BUF_LEN(p->conf.path)); + + if (buffer_string_is_empty(p->conf.basepath)) { + struct stat st; + int ret; + + ret = stat(p->temp_path->ptr, &st); + if (ret < 0 || S_ISDIR(st.st_mode) != 1) { + return HANDLER_GO_ON; + } + } + + buffer_copy_buffer(con->physical.basedir, p->temp_path); + + /* the physical rel_path is basically the same as uri.path; + * but it is converted to lowercase in case of force_lowercase_filenames and some special handling + * for trailing '.', ' ' and '/' on windows + * we assume that no docroot/physical handler changed this + * (docroot should only set the docroot/server name, phyiscal should only change the phyiscal.path; + * the exception mod_secdownload doesn't work with userdir anyway) + */ + buffer_append_slash(p->temp_path); + /* if no second '/' is found, we assume that it was stripped from the uri.path for the special handling + * on windows. + * we do not care about the trailing slash here on windows, as we already ensured it is a directory + * + * TODO: what to do with trailing dots in usernames on windows? they may result in the same directory + * as a username without them. + */ + if (NULL != (rel_url = strchr(con->physical.rel_path->ptr + 2, '/'))) { + buffer_append_string(p->temp_path, rel_url + 1); /* skip the / */ + } + buffer_copy_buffer(con->physical.path, p->temp_path); + + return HANDLER_GO_ON; +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_userdir_plugin_init(plugin *p); +int mod_userdir_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("userdir"); + + p->init = mod_userdir_init; + p->handle_physical = mod_userdir_docroot_handler; + p->set_defaults = mod_userdir_set_defaults; + p->cleanup = mod_userdir_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_usertrack.c b/data/lighttpd/lighttpd-1.4.53/src/mod_usertrack.c new file mode 100644 index 000000000..701bb1e5b --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_usertrack.c @@ -0,0 +1,285 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "rand.h" +#include "http_header.h" + +#include "plugin.h" + +#include <stdlib.h> +#include <string.h> + +#include "md5.h" + +/* plugin config for all request/connections */ + +typedef struct { + buffer *cookie_name; + buffer *cookie_attrs; + buffer *cookie_domain; + unsigned int cookie_max_age; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +INIT_FUNC(mod_usertrack_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_usertrack_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->cookie_name); + buffer_free(s->cookie_attrs); + buffer_free(s->cookie_domain); + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_usertrack_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "usertrack.cookie-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "usertrack.cookie-max-age", NULL, T_CONFIG_INT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "usertrack.cookie-domain", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "usertrack.cookie-attrs", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->cookie_name = buffer_init(); + s->cookie_attrs = buffer_init(); + s->cookie_domain = buffer_init(); + s->cookie_max_age = 0; + + cv[0].destination = s->cookie_name; + cv[1].destination = &(s->cookie_max_age); + cv[2].destination = s->cookie_domain; + cv[3].destination = s->cookie_attrs; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (buffer_string_is_empty(s->cookie_name)) { + buffer_copy_string_len(s->cookie_name, CONST_STR_LEN("TRACKID")); + } else { + size_t j, len = buffer_string_length(s->cookie_name); + for (j = 0; j < len; j++) { + char c = s->cookie_name->ptr[j] | 32; + if (c < 'a' || c > 'z') { + log_error_write(srv, __FILE__, __LINE__, "sb", + "invalid character in usertrack.cookie-name:", + s->cookie_name); + + return HANDLER_ERROR; + } + } + } + + if (!buffer_string_is_empty(s->cookie_domain)) { + size_t j, len = buffer_string_length(s->cookie_domain); + for (j = 0; j < len; j++) { + char c = s->cookie_domain->ptr[j]; + if (c <= 32 || c >= 127 || c == '"' || c == '\\') { + log_error_write(srv, __FILE__, __LINE__, "sb", + "invalid character in usertrack.cookie-domain:", + s->cookie_domain); + + return HANDLER_ERROR; + } + } + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_usertrack_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(cookie_name); + PATCH(cookie_attrs); + PATCH(cookie_domain); + PATCH(cookie_max_age); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-name"))) { + PATCH(cookie_name); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-attrs"))) { + PATCH(cookie_attrs); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-max-age"))) { + PATCH(cookie_max_age); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-domain"))) { + PATCH(cookie_domain); + } + } + } + + return 0; +} +#undef PATCH + +URIHANDLER_FUNC(mod_usertrack_uri_handler) { + plugin_data *p = p_d; + buffer *cookie; + buffer *b; + unsigned char h[16]; + li_MD5_CTX Md5Ctx; + char hh[LI_ITOSTRING_LENGTH]; + + if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; + + mod_usertrack_patch_connection(srv, con, p); + + if (NULL != (b = http_header_request_get(con, HTTP_HEADER_COOKIE, CONST_STR_LEN("Cookie")))) { + char *g; + /* we have a cookie, does it contain a valid name ? */ + + /* parse the cookie + * + * check for cookiename + (WS | '=') + * + */ + + if (NULL != (g = strstr(b->ptr, p->conf.cookie_name->ptr))) { + char *nc; + + /* skip WS */ + for (nc = g + buffer_string_length(p->conf.cookie_name); *nc == ' ' || *nc == '\t'; nc++); + + if (*nc == '=') { + /* ok, found the key of our own cookie */ + + if (strlen(nc) > 32) { + /* i'm lazy */ + return HANDLER_GO_ON; + } + } + } + } + + /* set a cookie */ + cookie = srv->tmp_buf; + buffer_copy_buffer(cookie, p->conf.cookie_name); + buffer_append_string_len(cookie, CONST_STR_LEN("=")); + + + /* taken from mod_auth.c */ + + /* generate shared-secret */ + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(con->uri.path)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN("+")); + + li_itostrn(hh, sizeof(hh), srv->cur_ts); + li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); + li_itostrn(hh, sizeof(hh), li_rand_pseudo()); + li_MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); + + li_MD5_Final(h, &Md5Ctx); + + buffer_append_string_encoded_hex_lc(cookie, (char *)h, 16); + + /* usertrack.cookie-attrs, if set, replaces all other attrs */ + if (!buffer_string_is_empty(p->conf.cookie_attrs)) { + buffer_append_string_buffer(cookie, p->conf.cookie_attrs); + http_header_response_insert(con, HTTP_HEADER_SET_COOKIE, CONST_STR_LEN("Set-Cookie"), CONST_BUF_LEN(cookie)); + return HANDLER_GO_ON; + } + + buffer_append_string_len(cookie, CONST_STR_LEN("; Path=/")); + buffer_append_string_len(cookie, CONST_STR_LEN("; Version=1")); + + if (!buffer_string_is_empty(p->conf.cookie_domain)) { + buffer_append_string_len(cookie, CONST_STR_LEN("; Domain=")); + buffer_append_string_encoded(cookie, CONST_BUF_LEN(p->conf.cookie_domain), ENCODING_REL_URI); + } + + if (p->conf.cookie_max_age) { + buffer_append_string_len(cookie, CONST_STR_LEN("; max-age=")); + buffer_append_int(cookie, p->conf.cookie_max_age); + } + + http_header_response_insert(con, HTTP_HEADER_SET_COOKIE, CONST_STR_LEN("Set-Cookie"), CONST_BUF_LEN(cookie)); + + return HANDLER_GO_ON; +} + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_usertrack_plugin_init(plugin *p); +int mod_usertrack_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("usertrack"); + + p->init = mod_usertrack_init; + p->handle_uri_clean = mod_usertrack_uri_handler; + p->set_defaults = mod_usertrack_set_defaults; + p->cleanup = mod_usertrack_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb.c b/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb.c new file mode 100644 index 000000000..ca75183d2 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb.c @@ -0,0 +1,239 @@ +#include "first.h" + +#include "base.h" +#include "plugin.h" +#include "http_vhostdb.h" +#include "log.h" +#include "stat_cache.h" + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +/** + * vhostdb framework + */ + +typedef struct { + buffer *vhostdb_backend_conf; + + /* generated */ + const http_vhostdb_backend_t *vhostdb_backend; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; + + buffer *tmp_buf; +} plugin_data; + +INIT_FUNC(mod_vhostdb_init) { + plugin_data *p = calloc(1, sizeof(*p)); + p->tmp_buf = buffer_init(); + return p; +} + +FREE_FUNC(mod_vhostdb_free) { + plugin_data *p = p_d; + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + if (NULL == s) continue; + buffer_free(s->vhostdb_backend_conf); + free(s); + } + free(p->config_storage); + } + + free(p->tmp_buf); + free(p); + + UNUSED(srv); + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) { + plugin_data *p = p_d; + config_values_t cv[] = { + { "vhostdb.backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (size_t i = 0; i < srv->config_context->used; ++i) { + data_config const *config = (data_config const*)srv->config_context->data[i]; + plugin_config *s = calloc(1, sizeof(plugin_config)); + s->vhostdb_backend_conf = buffer_init(); + + cv[0].destination = s->vhostdb_backend_conf; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!buffer_string_is_empty(s->vhostdb_backend_conf)) { + s->vhostdb_backend = + http_vhostdb_backend_get(s->vhostdb_backend_conf); + if (NULL == s->vhostdb_backend) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "vhostdb.backend not supported:", + s->vhostdb_backend_conf); + return HANDLER_ERROR; + } + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_vhostdb_patch_connection(server *srv, connection *con, plugin_data *p) { + plugin_config *s = p->config_storage[0]; + PATCH(vhostdb_backend); + + /* skip the first, the global context */ + for (size_t i = 1; i < srv->config_context->used; ++i) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (size_t j = 0; j < dc->value->used; ++j) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("vhostdb.backend"))) { + PATCH(vhostdb_backend); + } + } + } + + return 0; +} +#undef PATCH + +typedef struct { + buffer *server_name; + buffer *document_root; +} vhostdb_entry; + +static vhostdb_entry * vhostdb_entry_init (void) +{ + vhostdb_entry *ve = calloc(1, sizeof(*ve)); + ve->server_name = buffer_init(); + ve->document_root = buffer_init(); + return ve; +} + +static void vhostdb_entry_free (vhostdb_entry *ve) +{ + buffer_free(ve->server_name); + buffer_free(ve->document_root); + free(ve); +} + +CONNECTION_FUNC(mod_vhostdb_handle_connection_close) { + plugin_data *p = p_d; + vhostdb_entry *ve; + + if ((ve = con->plugin_ctx[p->id])) { + con->plugin_ctx[p->id] = NULL; + vhostdb_entry_free(ve); + } + + UNUSED(srv); + return HANDLER_GO_ON; +} + +static handler_t mod_vhostdb_error_500 (connection *con) +{ + con->http_status = 500; /* Internal Server Error */ + con->mode = DIRECT; + return HANDLER_FINISHED; +} + +static handler_t mod_vhostdb_found (connection *con, vhostdb_entry *ve) +{ + /* fix virtual server and docroot */ + buffer_copy_buffer(con->server_name, ve->server_name); + buffer_copy_buffer(con->physical.doc_root, ve->document_root); + return HANDLER_GO_ON; +} + +CONNECTION_FUNC(mod_vhostdb_handle_docroot) { + plugin_data *p = p_d; + vhostdb_entry *ve; + const http_vhostdb_backend_t *backend; + buffer *b; + stat_cache_entry *sce; + + /* no host specified? */ + if (buffer_string_is_empty(con->uri.authority)) return HANDLER_GO_ON; + + /* XXX: future: implement larger, managed cache + * of database responses (positive and negative) */ + + /* check if cached this connection */ + ve = con->plugin_ctx[p->id]; + if (ve && buffer_is_equal(ve->server_name, con->uri.authority)) { + return mod_vhostdb_found(con, ve); /* HANDLER_GO_ON */ + } + + mod_vhostdb_patch_connection(srv, con, p); + if (!p->conf.vhostdb_backend) return HANDLER_GO_ON; + + b = p->tmp_buf; + backend = p->conf.vhostdb_backend; + if (0 != backend->query(srv, con, backend->p_d, b)) { + return mod_vhostdb_error_500(con); /* HANDLER_FINISHED */ + } + + if (buffer_string_is_empty(b)) { + /* no such virtual host */ + return HANDLER_GO_ON; + } + + /* sanity check that really is a directory */ + buffer_append_slash(b); + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, b, &sce)) { + log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), b); + return mod_vhostdb_error_500(con); /* HANDLER_FINISHED */ + } + if (!S_ISDIR(sce->st.st_mode)) { + log_error_write(srv, __FILE__, __LINE__, "sb", "Not a directory", b); + return mod_vhostdb_error_500(con); /* HANDLER_FINISHED */ + } + + /* cache the data */ + if (!ve) con->plugin_ctx[p->id] = ve = vhostdb_entry_init(); + buffer_copy_buffer(ve->server_name, con->uri.authority); + buffer_copy_buffer(ve->document_root, b); + + return mod_vhostdb_found(con, ve); /* HANDLER_GO_ON */ +} + +int mod_vhostdb_plugin_init(plugin *p); +int mod_vhostdb_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("vhostdb"); + p->init = mod_vhostdb_init; + p->cleanup = mod_vhostdb_free; + p->set_defaults = mod_vhostdb_set_defaults; + p->handle_docroot = mod_vhostdb_handle_docroot; + p->connection_reset = mod_vhostdb_handle_connection_close; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb_dbi.c b/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb_dbi.c new file mode 100644 index 000000000..0c78e12ab --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb_dbi.c @@ -0,0 +1,331 @@ +#include "first.h" + +#include <dbi/dbi.h> + +#include <string.h> +#include <stdlib.h> + +#include "base.h" +#include "http_vhostdb.h" +#include "fdevent.h" +#include "log.h" +#include "plugin.h" + +/* + * virtual host plugin using DBI for domain to directory lookups + * + * e.g. + * vhostdb.dbi = ( "sql" => "SELECT docroot FROM vhosts WHERE host='?'" + * "dbtype" => "sqlite3", + * "dbname" => "mydb.sqlite", + * "sqlite_dbdir" => "/path/to/sqlite/dbs/" ) + */ + +typedef struct { + dbi_conn dbconn; + dbi_inst dbinst; + buffer *sqlquery; + server *srv; + short reconnect_count; +} vhostdb_config; + +typedef struct { + void *vdata; + array *options; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +/* used to reconnect to the database when we get disconnected */ +static void mod_vhostdb_dbi_error_callback (dbi_conn dbconn, void *vdata) +{ + vhostdb_config *dbconf = (vhostdb_config *)vdata; + const char *errormsg = NULL; + /*assert(dbconf->dbconn == dbconn);*/ + + while (++dbconf->reconnect_count <= 3) { /* retry */ + if (0 == dbi_conn_connect(dbconn)) { + fdevent_setfd_cloexec(dbi_conn_get_socket(dbconn)); + return; + } + } + + dbi_conn_error(dbconn, &errormsg); + log_error_write(dbconf->srv, __FILE__, __LINE__, "ss", + "dbi_conn_connect():", errormsg); +} + +static void mod_vhostdb_dbconf_free (void *vdata) +{ + vhostdb_config *dbconf = (vhostdb_config *)vdata; + if (!dbconf) return; + dbi_conn_close(dbconf->dbconn); + dbi_shutdown_r(dbconf->dbinst); + free(dbconf); +} + +static int mod_vhostdb_dbconf_setup (server *srv, array *opts, void **vdata) +{ + buffer *sqlquery = NULL; + const buffer *dbtype=NULL, *dbname=NULL; + + for (size_t i = 0; i < opts->used; ++i) { + const data_string *ds = (data_string *)opts->data[i]; + if (ds->type == TYPE_STRING) { + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("sql"))) { + sqlquery = ds->value; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("dbname"))) { + dbname = ds->value; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("dbtype"))) { + dbtype = ds->value; + } + } + } + + /* required: + * - sql (sql query) + * - dbtype + * - dbname + * + * optional: + * - username, some databases don't require this (sqlite) + * - password, default: empty + * - socket, default: database type default + * - hostname, if set overrides socket + * - port, default: database default + * - encoding, default: database default + */ + + if (!buffer_string_is_empty(sqlquery) + && !buffer_is_empty(dbname) && !buffer_is_empty(dbtype)) { + /* create/initialise database */ + vhostdb_config *dbconf; + dbi_inst dbinst = NULL; + dbi_conn dbconn; + if (dbi_initialize_r(NULL, &dbinst) < 1) { + log_error_write(srv, __FILE__, __LINE__, "s", + "dbi_initialize_r() failed. " + "Do you have the DBD for this db type installed?"); + return -1; + } + dbconn = dbi_conn_new_r(dbtype->ptr, dbinst); + if (NULL == dbconn) { + log_error_write(srv, __FILE__, __LINE__, "s", + "dbi_conn_new_r() failed. " + "Do you have the DBD for this db type installed?"); + dbi_shutdown_r(dbinst); + return -1; + } + + /* set options */ + for (size_t j = 0; j < opts->used; ++j) { + data_unset *du = opts->data[j]; + const buffer *opt = du->key; + if (!buffer_string_is_empty(opt)) { + if (du->type == TYPE_INTEGER) { + data_integer *di = (data_integer *)du; + dbi_conn_set_option_numeric(dbconn, opt->ptr, di->value); + } else if (du->type == TYPE_STRING) { + data_string *ds = (data_string *)du; + if (ds->value != sqlquery && ds->value != dbtype) { + dbi_conn_set_option(dbconn, opt->ptr, ds->value->ptr); + } + } + } + } + + dbconf = (vhostdb_config *)calloc(1, sizeof(*dbconf)); + dbconf->dbinst = dbinst; + dbconf->dbconn = dbconn; + dbconf->sqlquery = sqlquery; + dbconf->srv = srv; + dbconf->reconnect_count = 0; + *vdata = dbconf; + + /* used to automatically reconnect to the database */ + dbi_conn_error_handler(dbconn, mod_vhostdb_dbi_error_callback, dbconf); + + /* connect to database */ + mod_vhostdb_dbi_error_callback(dbconn, dbconf); + if (dbconf->reconnect_count >= 3) return -1; + } + + return 0; +} + +static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p); + +static int mod_vhostdb_dbi_query(server *srv, connection *con, void *p_d, buffer *docroot) +{ + plugin_data *p = (plugin_data *)p_d; + vhostdb_config *dbconf; + dbi_result result; + unsigned long long nrows; + int retry_count = 0; + + /*(reuse buffer for sql query before generating docroot result)*/ + buffer *sqlquery = docroot; + buffer_clear(sqlquery); /*(also resets docroot (alias))*/ + + mod_vhostdb_patch_connection(srv, con, p); + if (NULL == p->conf.vdata) return 0; /*(after resetting docroot)*/ + dbconf = (vhostdb_config *)p->conf.vdata; + + for (char *b = dbconf->sqlquery->ptr, *d; *b; b = d+1) { + if (NULL != (d = strchr(b, '?'))) { + /* escape the uri.authority */ + char *esc = NULL; + size_t len = dbi_conn_escape_string_copy(dbconf->dbconn, con->uri.authority->ptr, &esc); + buffer_append_string_len(sqlquery, b, (size_t)(d - b)); + buffer_append_string_len(sqlquery, esc, len); + free(esc); + if (0 == len) return -1; + } else { + d = dbconf->sqlquery->ptr + buffer_string_length(dbconf->sqlquery); + buffer_append_string_len(sqlquery, b, (size_t)(d - b)); + break; + } + } + + /* reset our reconnect-attempt counter, this is a new query. */ + dbconf->reconnect_count = 0; + + do { + result = dbi_conn_query(dbconf->dbconn, sqlquery->ptr); + } while (!result && ++retry_count < 2); + + buffer_clear(docroot); /*(reset buffer to store result)*/ + + if (!result) { + const char *errmsg; + dbi_conn_error(dbconf->dbconn, &errmsg); + log_error_write(srv, __FILE__, __LINE__, "s", errmsg); + return -1; + } + + nrows = dbi_result_get_numrows(result); + if (nrows && nrows != DBI_ROW_ERROR && dbi_result_next_row(result)) { + buffer_copy_string(docroot, dbi_result_get_string_idx(result, 1)); + } /* else no such virtual host */ + + dbi_result_free(result); + return 0; +} + + + + +INIT_FUNC(mod_vhostdb_init) { + static http_vhostdb_backend_t http_vhostdb_backend_dbi = + { "dbi", mod_vhostdb_dbi_query, NULL }; + plugin_data *p = calloc(1, sizeof(*p)); + + /* register http_vhostdb_backend_dbi */ + http_vhostdb_backend_dbi.p_d = p; + http_vhostdb_backend_set(&http_vhostdb_backend_dbi); + + return p; +} + +FREE_FUNC(mod_vhostdb_cleanup) { + plugin_data *p = p_d; + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + for (size_t i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + if (!s) continue; + mod_vhostdb_dbconf_free(s->vdata); + array_free(s->options); + free(s); + } + free(p->config_storage); + } + free(p); + + UNUSED(srv); + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) { + plugin_data *p = p_d; + + config_values_t cv[] = { + { "vhostdb.dbi", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (size_t i = 0; i < srv->config_context->used; ++i) { + data_config const *config = (data_config const*)srv->config_context->data[i]; + plugin_config *s = calloc(1, sizeof(plugin_config)); + + s->options = array_init(); + cv[0].destination = s->options; + + p->config_storage[i] = s; + + if (config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_kvany(s->options)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for vhostdb.dbi; expected list of \"option\" => \"value\""); + return HANDLER_ERROR; + } + + if (s->options->used + && 0 != mod_vhostdb_dbconf_setup(srv, s->options, &s->vdata)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p) +{ + plugin_config *s = p->config_storage[0]; + PATCH(vdata); + + /* skip the first, the global context */ + for (size_t i = 1; i < srv->config_context->used; ++i) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (size_t j = 0; j < dc->value->used; ++j) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("vhostdb.dbi"))) { + PATCH(vdata); + } + } + } +} +#undef PATCH + +/* this function is called at dlopen() time and inits the callbacks */ +int mod_vhostdb_dbi_plugin_init (plugin *p); +int mod_vhostdb_dbi_plugin_init (plugin *p) +{ + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("vhostdb_dbi"); + + p->init = mod_vhostdb_init; + p->cleanup = mod_vhostdb_cleanup; + p->set_defaults = mod_vhostdb_set_defaults; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb_ldap.c b/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb_ldap.c new file mode 100644 index 000000000..e81c4933d --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb_ldap.c @@ -0,0 +1,556 @@ +#include "first.h" + +#include <ldap.h> + +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +#include "base.h" +#include "http_vhostdb.h" +#include "log.h" +#include "plugin.h" + +/* + * virtual host plugin using LDAP for domain to directory lookups + */ + +typedef struct { + LDAP *ldap; + buffer *filter; + server *srv; + + const char *attr; + const char *host; + const char *basedn; + const char *binddn; + const char *bindpw; + const char *cafile; + unsigned short starttls; +} vhostdb_config; + +typedef struct { + void *vdata; + array *options; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +static void mod_vhostdb_dbconf_free (void *vdata) +{ + vhostdb_config *dbconf = (vhostdb_config *)vdata; + if (!dbconf) return; + if (NULL != dbconf->ldap) ldap_unbind_ext_s(dbconf->ldap, NULL, NULL); + free(dbconf); +} + +/*(copied from mod_authn_ldap.c)*/ +static void mod_vhostdb_dbconf_add_scheme (server *srv, buffer *host) +{ + if (!buffer_string_is_empty(host)) { + /* reformat hostname(s) as LDAP URIs (scheme://host:port) */ + static const char *schemes[] = { + "ldap://", "ldaps://", "ldapi://", "cldap://" + }; + char *b, *e = host->ptr; + buffer_clear(srv->tmp_buf); + while (*(b = e)) { + unsigned int j; + while (*b==' '||*b=='\t'||*b=='\r'||*b=='\n'||*b==',') ++b; + if (*b == '\0') break; + e = b; + while (*e!=' '&&*e!='\t'&&*e!='\r'&&*e!='\n'&&*e!=','&&*e!='\0') + ++e; + if (!buffer_string_is_empty(srv->tmp_buf)) + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(",")); + for (j = 0; j < sizeof(schemes)/sizeof(char *); ++j) { + if (0 == strncasecmp(b, schemes[j], strlen(schemes[j]))) { + break; + } + } + if (j == sizeof(schemes)/sizeof(char *)) + buffer_append_string_len(srv->tmp_buf, + CONST_STR_LEN("ldap://")); + buffer_append_string_len(srv->tmp_buf, b, (size_t)(e - b)); + } + buffer_copy_buffer(host, srv->tmp_buf); + } +} + +static int mod_vhostdb_dbconf_setup (server *srv, array *opts, void **vdata) +{ + buffer *filter = NULL; + const char *attr = "documentRoot"; + const char *basedn=NULL,*binddn=NULL,*bindpw=NULL,*host=NULL,*cafile=NULL; + unsigned short starttls = 0; + + for (size_t i = 0; i < opts->used; ++i) { + const data_string *ds = (data_string *)opts->data[i]; + if (ds->type == TYPE_STRING) { + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("filter"))) { + filter = ds->value; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("attr"))) { + if (!buffer_string_is_empty(ds->value)) attr = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("host"))) { + mod_vhostdb_dbconf_add_scheme(srv, ds->value); + host = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("base-dn"))) { + if (!buffer_string_is_empty(ds->value)) basedn = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("bind-dn"))) { + if (!buffer_string_is_empty(ds->value)) binddn = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("bind-pw"))) { + bindpw = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("ca-file"))) { + if (!buffer_string_is_empty(ds->value)) cafile = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("starttls"))) { + starttls = !buffer_is_equal_string(ds->value, CONST_STR_LEN("disable")) + && !buffer_is_equal_string(ds->value, CONST_STR_LEN("0")); + } + } + } + + /* required: + * - host + * - filter (LDAP query) + * - base-dn + * + * optional: + * - attr (LDAP attribute with docroot; default "documentRoot") + * - bind-dn + * - bind-pw + * - ca-file + * - starttls + */ + + if (!buffer_string_is_empty(filter) && NULL != host && NULL != basedn) { + vhostdb_config *dbconf; + + if (NULL == strchr(filter->ptr, '?')) { + log_error_write(srv, __FILE__, __LINE__, "s", + "ldap: filter is missing a replace-operator '?'"); + return -1; + } + + /* openldap sets FD_CLOEXEC on database socket descriptors + * (still race between creation of socket and fcntl FD_CLOEXEC) + * (YMMV with other LDAP client libraries) */ + + dbconf = (vhostdb_config *)calloc(1, sizeof(*dbconf)); + dbconf->ldap = NULL; + dbconf->filter = filter; + dbconf->attr = attr; + dbconf->host = host; + dbconf->basedn = basedn; + dbconf->binddn = binddn; + dbconf->bindpw = bindpw; + dbconf->cafile = cafile; + dbconf->starttls = starttls; + *vdata = dbconf; + } + return 0; +} + +/* + * Note: a large portion of the LDAP code is copied verbatim from mod_authn_ldap + * with only changes being use of vhostdb_config instead of plugin_config struct + * and (const char *) strings in vhostdb_config instead of (buffer *). + */ + +static void mod_authn_ldap_err(server *srv, const char *file, unsigned long line, const char *fn, int err) +{ + log_error_write(srv,file,line,"sSss","ldap:",fn,":",ldap_err2string(err)); +} + +static void mod_authn_ldap_opt_err(server *srv, const char *file, unsigned long line, const char *fn, LDAP *ld) +{ + int err; + ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err); + mod_authn_ldap_err(srv, file, line, fn, err); +} + +static void mod_authn_append_ldap_filter_escape(buffer * const filter, const buffer * const raw) { + /* [RFC4515] 3. String Search Filter Definition + * + * [...] + * + * The <valueencoding> rule ensures that the entire filter string is a + * valid UTF-8 string and provides that the octets that represent the + * ASCII characters "*" (ASCII 0x2a), "(" (ASCII 0x28), ")" (ASCII + * 0x29), "\" (ASCII 0x5c), and NUL (ASCII 0x00) are represented as a + * backslash "\" (ASCII 0x5c) followed by the two hexadecimal digits + * representing the value of the encoded octet. + * + * [...] + * + * As indicated by the <valueencoding> rule, implementations MUST escape + * all octets greater than 0x7F that are not part of a valid UTF-8 + * encoding sequence when they generate a string representation of a + * search filter. Implementations SHOULD accept as input strings that + * are not valid UTF-8 strings. This is necessary because RFC 2254 did + * not clearly define the term "string representation" (and in + * particular did not mention that the string representation of an LDAP + * search filter is a string of UTF-8-encoded Unicode characters). + * + * + * https://www.ldap.com/ldap-filters + * Although not required, you may escape any other characters that you want + * in the assertion value (or substring component) of a filter. This may be + * accomplished by prefixing the hexadecimal representation of each byte of + * the UTF-8 encoding of the character to escape with a backslash character. + */ + const char * const b = raw->ptr; + const size_t rlen = buffer_string_length(raw); + for (size_t i = 0; i < rlen; ++i) { + size_t len = i; + char *f; + do { + /* encode all UTF-8 chars with high bit set + * (instead of validating UTF-8 and escaping only invalid UTF-8) */ + if (((unsigned char *)b)[len] > 0x7f) + break; + switch (b[len]) { + default: + continue; + case '\0': case '(': case ')': case '*': case '\\': + break; + } + break; + } while (++len < rlen); + len -= i; + + if (len) { + buffer_append_string_len(filter, b+i, len); + if ((i += len) == rlen) break; + } + + /* escape * ( ) \ NUL ('\0') (and all UTF-8 chars with high bit set) */ + buffer_string_prepare_append(filter, 3); + f = filter->ptr + buffer_string_length(filter); + f[0] = '\\'; + f[1] = "0123456789abcdef"[(((unsigned char *)b)[i] >> 4) & 0xf]; + f[2] = "0123456789abcdef"[(((unsigned char *)b)[i] ) & 0xf]; + buffer_commit(filter, 3); + } +} + +static LDAP * mod_authn_ldap_host_init(server *srv, vhostdb_config *s) { + LDAP *ld; + int ret; + + ret = ldap_initialize(&ld, s->host); + if (LDAP_SUCCESS != ret) { + log_error_write(srv, __FILE__, __LINE__, "sss", "ldap:", + "ldap_initialize():", strerror(errno)); + return NULL; + } + + ret = LDAP_VERSION3; + ret = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ret); + if (LDAP_OPT_SUCCESS != ret) { + mod_authn_ldap_err(srv, __FILE__, __LINE__, "ldap_set_options()", ret); + ldap_destroy(ld); + return NULL; + } + + if (s->starttls) { + /* if no CA file is given, it is ok, as we will use encryption + * if the server requires a CAfile it will tell us */ + if (s->cafile) { + ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, s->cafile); + if (LDAP_OPT_SUCCESS != ret) { + mod_authn_ldap_err(srv, __FILE__, __LINE__, + "ldap_set_option(LDAP_OPT_X_TLS_CACERTFILE)", + ret); + ldap_destroy(ld); + return NULL; + } + } + + ret = ldap_start_tls_s(ld, NULL, NULL); + if (LDAP_OPT_SUCCESS != ret) { + mod_authn_ldap_err(srv,__FILE__,__LINE__,"ldap_start_tls_s()",ret); + ldap_destroy(ld); + return NULL; + } + } + + return ld; +} + +static int mod_authn_ldap_bind(server *srv, LDAP *ld, const char *dn, const char *pw) { + struct berval creds; + int ret; + + if (NULL != pw) { + *((const char **)&creds.bv_val) = pw; /*(cast away const)*/ + creds.bv_len = strlen(pw); + } else { + creds.bv_val = NULL; + creds.bv_len = 0; + } + + /* RFE: add functionality: LDAP_SASL_EXTERNAL (or GSS-SPNEGO, etc.) */ + + ret = ldap_sasl_bind_s(ld,dn,LDAP_SASL_SIMPLE,&creds,NULL,NULL,NULL); + if (ret != LDAP_SUCCESS) { + mod_authn_ldap_err(srv, __FILE__, __LINE__, "ldap_sasl_bind_s()", ret); + } + + return ret; +} + +static int mod_authn_ldap_rebind_proc (LDAP *ld, LDAP_CONST char *url, ber_tag_t ldap_request, ber_int_t msgid, void *params) { + vhostdb_config *s = (vhostdb_config *)params; + UNUSED(url); + UNUSED(ldap_request); + UNUSED(msgid); + return mod_authn_ldap_bind(s->srv, ld, s->binddn, s->bindpw); +} + +static LDAPMessage * mod_authn_ldap_search(server *srv, vhostdb_config *s, char *base, char *filter) { + LDAPMessage *lm = NULL; + char *attrs[] = { LDAP_NO_ATTRS, NULL }; + int ret; + + /* + * 1. connect anonymously (if not already connected) + * (ldap connection is kept open unless connection-level error occurs) + * 2. issue search using filter + */ + + if (s->ldap != NULL) { + ret = ldap_search_ext_s(s->ldap, base, LDAP_SCOPE_SUBTREE, filter, + attrs, 0, NULL, NULL, NULL, 0, &lm); + if (LDAP_SUCCESS == ret) { + return lm; + } else if (LDAP_SERVER_DOWN != ret) { + /* try again (or initial request); + * ldap lib sometimes fails for the first call but reconnects */ + ret = ldap_search_ext_s(s->ldap, base, LDAP_SCOPE_SUBTREE, filter, + attrs, 0, NULL, NULL, NULL, 0, &lm); + if (LDAP_SUCCESS == ret) { + return lm; + } + } + + ldap_unbind_ext_s(s->ldap, NULL, NULL); + } + + s->ldap = mod_authn_ldap_host_init(srv, s); + if (NULL == s->ldap) { + return NULL; + } + + ldap_set_rebind_proc(s->ldap, mod_authn_ldap_rebind_proc, s); + ret = mod_authn_ldap_bind(srv, s->ldap, s->binddn, s->bindpw); + if (LDAP_SUCCESS != ret) { + ldap_destroy(s->ldap); + s->ldap = NULL; + return NULL; + } + + ret = ldap_search_ext_s(s->ldap, base, LDAP_SCOPE_SUBTREE, filter, + attrs, 0, NULL, NULL, NULL, 0, &lm); + if (LDAP_SUCCESS != ret) { + log_error_write(srv, __FILE__, __LINE__, "sSss", + "ldap:", ldap_err2string(ret), "; filter:", filter); + ldap_unbind_ext_s(s->ldap, NULL, NULL); + s->ldap = NULL; + return NULL; + } + + return lm; +} + +static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p); + +static int mod_vhostdb_ldap_query(server *srv, connection *con, void *p_d, buffer *docroot) +{ + plugin_data *p = (plugin_data *)p_d; + vhostdb_config *dbconf; + LDAP *ld; + LDAPMessage *lm, *first; + struct berval **vals; + int count; + char *basedn; + buffer *template; + + /*(reuse buffer for ldap query before generating docroot result)*/ + buffer *filter = docroot; + buffer_clear(filter); /*(also resets docroot (alias))*/ + + mod_vhostdb_patch_connection(srv, con, p); + if (NULL == p->conf.vdata) return 0; /*(after resetting docroot)*/ + dbconf = (vhostdb_config *)p->conf.vdata; + dbconf->srv = srv; + + template = dbconf->filter; + for (char *b = template->ptr, *d; *b; b = d+1) { + if (NULL != (d = strchr(b, '?'))) { + buffer_append_string_len(filter, b, (size_t)(d - b)); + mod_authn_append_ldap_filter_escape(filter, con->uri.authority); + } else { + d = template->ptr + buffer_string_length(template); + buffer_append_string_len(filter, b, (size_t)(d - b)); + break; + } + } + + /* (cast away const for poor LDAP ldap_search_ext_s() prototype) */ + *(const char **)&basedn = dbconf->basedn; + + /* ldap_search (synchronous; blocking) */ + lm = mod_authn_ldap_search(srv, dbconf, basedn, filter->ptr); + if (NULL == lm) { + return -1; + } + + /*(must be after mod_authn_ldap_search(); might reconnect)*/ + ld = dbconf->ldap; + + count = ldap_count_entries(ld, lm); + if (count > 1) { + log_error_write(srv, __FILE__, __LINE__, "ssb", + "ldap:", "more than one record returned. " + "you might have to refine the filter:", filter); + } + + buffer_clear(docroot); /*(reset buffer to store result)*/ + + if (0 == count) { /*(no entries found)*/ + ldap_msgfree(lm); + return 0; + } + + if (NULL == (first = ldap_first_entry(ld, lm))) { + mod_authn_ldap_opt_err(srv,__FILE__,__LINE__,"ldap_first_entry()",ld); + ldap_msgfree(lm); + return -1; + } + + if (NULL != (vals = ldap_get_values_len(ld, first, dbconf->attr))) { + buffer_copy_string_len(docroot, vals[0]->bv_val, vals[0]->bv_len); + ldap_value_free_len(vals); + } + + ldap_msgfree(lm); + return 0; +} + + + + +INIT_FUNC(mod_vhostdb_init) { + static http_vhostdb_backend_t http_vhostdb_backend_ldap = + { "ldap", mod_vhostdb_ldap_query, NULL }; + plugin_data *p = calloc(1, sizeof(*p)); + + /* register http_vhostdb_backend_ldap */ + http_vhostdb_backend_ldap.p_d = p; + http_vhostdb_backend_set(&http_vhostdb_backend_ldap); + + return p; +} + +FREE_FUNC(mod_vhostdb_cleanup) { + plugin_data *p = p_d; + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + for (size_t i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + if (!s) continue; + mod_vhostdb_dbconf_free(s->vdata); + array_free(s->options); + free(s); + } + free(p->config_storage); + } + free(p); + + UNUSED(srv); + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) { + plugin_data *p = p_d; + + config_values_t cv[] = { + { "vhostdb.ldap", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (size_t i = 0; i < srv->config_context->used; ++i) { + data_config const *config = (data_config const*)srv->config_context->data[i]; + plugin_config *s = calloc(1, sizeof(plugin_config)); + + s->options = array_init(); + cv[0].destination = s->options; + + p->config_storage[i] = s; + + if (config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_kvstring(s->options)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for vhostdb.ldap; expected list of \"option\" => \"value\""); + return HANDLER_ERROR; + } + + if (s->options->used + && 0 != mod_vhostdb_dbconf_setup(srv, s->options, &s->vdata)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p) +{ + plugin_config *s = p->config_storage[0]; + PATCH(vdata); + + /* skip the first, the global context */ + for (size_t i = 1; i < srv->config_context->used; ++i) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (size_t j = 0; j < dc->value->used; ++j) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key,CONST_STR_LEN("vhostdb.ldap"))) { + PATCH(vdata); + } + } + } +} +#undef PATCH + +/* this function is called at dlopen() time and inits the callbacks */ +int mod_vhostdb_ldap_plugin_init (plugin *p); +int mod_vhostdb_ldap_plugin_init (plugin *p) +{ + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("vhostdb_ldap"); + + p->init = mod_vhostdb_init; + p->cleanup = mod_vhostdb_cleanup; + p->set_defaults = mod_vhostdb_set_defaults; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb_mysql.c b/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb_mysql.c new file mode 100644 index 000000000..5905bc81a --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb_mysql.c @@ -0,0 +1,295 @@ +#include "first.h" + +#include <mysql.h> + +#include <string.h> +#include <stdlib.h> + +#include "base.h" +#include "http_vhostdb.h" +#include "fdevent.h" +#include "log.h" +#include "plugin.h" + +/* + * virtual host plugin using MySQL for domain to directory lookups + */ + +typedef struct { + MYSQL *dbconn; + buffer *sqlquery; +} vhostdb_config; + +typedef struct { + void *vdata; + array *options; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +static void mod_vhostdb_dbconf_free (void *vdata) +{ + vhostdb_config *dbconf = (vhostdb_config *)vdata; + if (!dbconf) return; + mysql_close(dbconf->dbconn); + free(dbconf); +} + +static int mod_vhostdb_dbconf_setup (server *srv, array *opts, void **vdata) +{ + buffer *sqlquery = NULL; + const char *dbname=NULL, *user=NULL, *pass=NULL, *host=NULL, *sock=NULL; + unsigned int port = 0; + + for (size_t i = 0; i < opts->used; ++i) { + const data_string *ds = (data_string *)opts->data[i]; + if (ds->type == TYPE_STRING && !buffer_string_is_empty(ds->value)) { + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("sql"))) { + sqlquery = ds->value; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("dbname"))) { + dbname = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("user"))) { + user = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("password"))) { + pass = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("host"))) { + host = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("port"))) { + port = strtoul(ds->value->ptr, NULL, 10); + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("sock"))) { + sock = ds->value->ptr; + } + } + } + + /* required: + * - sql (sql query) + * - dbname + * - user + * + * optional: + * - password, default: empty + * - socket, default: mysql default + * - hostname, if set overrides socket + * - port, default: 3306 + */ + + if (!buffer_string_is_empty(sqlquery) + && dbname && *dbname && user && *user) { + vhostdb_config *dbconf; + MYSQL *dbconn = mysql_init(NULL); + if (NULL == dbconn) { + log_error_write(srv, __FILE__, __LINE__, "s", + "mysql_init() failed, exiting..."); + return -1; + } + + #if MYSQL_VERSION_ID >= 50013 + /* in mysql versions above 5.0.3 the reconnect flag is off by default */ + { + char reconnect = 1; + mysql_options(dbconn, MYSQL_OPT_RECONNECT, &reconnect); + } + #endif + + /* CLIENT_MULTI_STATEMENTS first appeared in 4.1 */ + #if MYSQL_VERSION_ID < 40100 + #ifndef CLIENT_MULTI_STATEMENTS + #define CLIENT_MULTI_STATEMENTS 0 + #endif + #endif + if (!mysql_real_connect(dbconn, host, user, pass, dbname, port, sock, + CLIENT_MULTI_STATEMENTS)) { + log_error_write(srv, __FILE__, __LINE__, "s", + mysql_error(dbconn)); + mysql_close(dbconn); + return -1; + } + + fdevent_setfd_cloexec(dbconn->net.fd); + + dbconf = (vhostdb_config *)calloc(1, sizeof(*dbconf)); + dbconf->dbconn = dbconn; + dbconf->sqlquery = sqlquery; + *vdata = dbconf; + } + + return 0; +} + +static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p); + +static int mod_vhostdb_mysql_query(server *srv, connection *con, void *p_d, buffer *docroot) +{ + plugin_data *p = (plugin_data *)p_d; + vhostdb_config *dbconf; + unsigned cols; + MYSQL_ROW row; + MYSQL_RES *result; + + /*(reuse buffer for sql query before generating docroot result)*/ + buffer *sqlquery = docroot; + buffer_clear(sqlquery); /*(also resets docroot (alias))*/ + + mod_vhostdb_patch_connection(srv, con, p); + if (NULL == p->conf.vdata) return 0; /*(after resetting docroot)*/ + dbconf = (vhostdb_config *)p->conf.vdata; + + for (char *b = dbconf->sqlquery->ptr, *d; *b; b = d+1) { + if (NULL != (d = strchr(b, '?'))) { + /* escape the uri.authority */ + unsigned long len; + buffer_append_string_len(sqlquery, b, (size_t)(d - b)); + buffer_string_prepare_append(sqlquery, buffer_string_length(con->uri.authority) * 2); + len = mysql_real_escape_string(dbconf->dbconn, + sqlquery->ptr + buffer_string_length(sqlquery), + CONST_BUF_LEN(con->uri.authority)); + if ((unsigned long)~0 == len) return -1; + buffer_commit(sqlquery, len); + } else { + d = dbconf->sqlquery->ptr + buffer_string_length(dbconf->sqlquery); + buffer_append_string_len(sqlquery, b, (size_t)(d - b)); + break; + } + } + + if (mysql_real_query(dbconf->dbconn, CONST_BUF_LEN(sqlquery))) { + log_error_write(srv, __FILE__, __LINE__, "s", + mysql_error(dbconf->dbconn)); + buffer_clear(docroot); /*(reset buffer; no result)*/ + return -1; + } + + buffer_clear(docroot); /*(reset buffer to store result)*/ + + result = mysql_store_result(dbconf->dbconn); + cols = mysql_num_fields(result); + row = mysql_fetch_row(result); + if (row && cols >= 1) { + buffer_copy_string(docroot, row[0]); + } /* else no such virtual host */ + + mysql_free_result(result); + #if MYSQL_VERSION_ID >= 40100 + while (0 == mysql_next_result(dbconf->dbconn)) ; + #endif + return 0; +} + + + + +INIT_FUNC(mod_vhostdb_init) { + static http_vhostdb_backend_t http_vhostdb_backend_mysql = + { "mysql", mod_vhostdb_mysql_query, NULL }; + plugin_data *p = calloc(1, sizeof(*p)); + + /* register http_vhostdb_backend_mysql */ + http_vhostdb_backend_mysql.p_d = p; + http_vhostdb_backend_set(&http_vhostdb_backend_mysql); + + return p; +} + +FREE_FUNC(mod_vhostdb_cleanup) { + plugin_data *p = p_d; + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + for (size_t i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + if (!s) continue; + mod_vhostdb_dbconf_free(s->vdata); + array_free(s->options); + free(s); + } + free(p->config_storage); + } + free(p); + + UNUSED(srv); + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) { + plugin_data *p = p_d; + + config_values_t cv[] = { + { "vhostdb.mysql", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (size_t i = 0; i < srv->config_context->used; ++i) { + data_config const *config = (data_config const*)srv->config_context->data[i]; + plugin_config *s = calloc(1, sizeof(plugin_config)); + + s->options = array_init(); + cv[0].destination = s->options; + + p->config_storage[i] = s; + + if (config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_kvstring(s->options)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for vhostdb.mysql; expected list of \"option\" => \"value\""); + return HANDLER_ERROR; + } + + if (s->options->used + && 0 != mod_vhostdb_dbconf_setup(srv, s->options, &s->vdata)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p) +{ + plugin_config *s = p->config_storage[0]; + PATCH(vdata); + + /* skip the first, the global context */ + for (size_t i = 1; i < srv->config_context->used; ++i) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (size_t j = 0; j < dc->value->used; ++j) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key,CONST_STR_LEN("vhostdb.mysql"))){ + PATCH(vdata); + } + } + } +} +#undef PATCH + +/* this function is called at dlopen() time and inits the callbacks */ +int mod_vhostdb_mysql_plugin_init (plugin *p); +int mod_vhostdb_mysql_plugin_init (plugin *p) +{ + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("vhostdb_mysql"); + + p->init = mod_vhostdb_init; + p->cleanup = mod_vhostdb_cleanup; + p->set_defaults = mod_vhostdb_set_defaults; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb_pgsql.c b/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb_pgsql.c new file mode 100644 index 000000000..a87f43599 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_vhostdb_pgsql.c @@ -0,0 +1,272 @@ +#include "first.h" + +#include <libpq-fe.h> + +#include <string.h> +#include <stdlib.h> + +#include "base.h" +#include "http_vhostdb.h" +#include "log.h" +#include "plugin.h" + +/* + * virtual host plugin using Postgres for domain to directory lookups + */ + +typedef struct { + PGconn *dbconn; + buffer *sqlquery; +} vhostdb_config; + +typedef struct { + void *vdata; + array *options; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +static void mod_vhostdb_dbconf_free (void *vdata) +{ + vhostdb_config *dbconf = (vhostdb_config *)vdata; + if (!dbconf) return; + PQfinish(dbconf->dbconn); + free(dbconf); +} + +static int mod_vhostdb_dbconf_setup (server *srv, array *opts, void **vdata) +{ + buffer *sqlquery = NULL; + const char *dbname=NULL, *user=NULL, *pass=NULL, *host=NULL, *port=NULL; + + for (size_t i = 0; i < opts->used; ++i) { + const data_string *ds = (data_string *)opts->data[i]; + if (ds->type == TYPE_STRING) { + if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("sql"))) { + sqlquery = ds->value; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("dbname"))) { + dbname = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("user"))) { + user = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("password"))) { + pass = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("host"))) { + host = ds->value->ptr; + } else if (buffer_is_equal_caseless_string(ds->key, CONST_STR_LEN("port"))) { + port = ds->value->ptr; + } + } + } + + /* required: + * - sql (sql query) + * - dbname + * - user (unless dbname is a pgsql conninfo URI) + * + * optional: + * - password, default: empty + * - hostname + * - port, default: 5432 + */ + + if (!buffer_string_is_empty(sqlquery) && NULL != dbname) { + vhostdb_config *dbconf; + PGconn *dbconn = PQsetdbLogin(host,port,NULL,NULL,dbname,user,pass); + if (NULL == dbconn) { + log_error_write(srv, __FILE__, __LINE__, "s", + "PGsetdbLogin() failed, exiting..."); + return -1; + } + + if (CONNECTION_OK != PQstatus(dbconn)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Failed to login to database, exiting..."); + PQfinish(dbconn); + return -1; + } + + /* Postgres sets FD_CLOEXEC on database socket descriptors */ + + dbconf = (vhostdb_config *)calloc(1, sizeof(*dbconf)); + dbconf->dbconn = dbconn; + dbconf->sqlquery = sqlquery; + *vdata = dbconf; + } + + return 0; +} + +static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p); + +static int mod_vhostdb_pgsql_query(server *srv, connection *con, void *p_d, buffer *docroot) +{ + plugin_data *p = (plugin_data *)p_d; + vhostdb_config *dbconf; + PGresult *res; + int cols, rows; + + /*(reuse buffer for sql query before generating docroot result)*/ + buffer *sqlquery = docroot; + buffer_clear(sqlquery); /*(also resets docroot (alias))*/ + + mod_vhostdb_patch_connection(srv, con, p); + if (NULL == p->conf.vdata) return 0; /*(after resetting docroot)*/ + dbconf = (vhostdb_config *)p->conf.vdata; + + for (char *b = dbconf->sqlquery->ptr, *d; *b; b = d+1) { + if (NULL != (d = strchr(b, '?'))) { + /* escape the uri.authority */ + size_t len; + int err; + buffer_append_string_len(sqlquery, b, (size_t)(d - b)); + buffer_string_prepare_append(sqlquery, buffer_string_length(con->uri.authority) * 2); + len = PQescapeStringConn(dbconf->dbconn, + sqlquery->ptr + buffer_string_length(sqlquery), + CONST_BUF_LEN(con->uri.authority), &err); + buffer_commit(sqlquery, len); + if (0 != err) return -1; + } else { + d = dbconf->sqlquery->ptr + buffer_string_length(dbconf->sqlquery); + buffer_append_string_len(sqlquery, b, (size_t)(d - b)); + break; + } + } + + res = PQexec(dbconf->dbconn, sqlquery->ptr); + + buffer_clear(docroot); /*(reset buffer to store result)*/ + + if (PGRES_TUPLES_OK != PQresultStatus(res)) { + log_error_write(srv, __FILE__, __LINE__, "s", + PQerrorMessage(dbconf->dbconn)); + PQclear(res); + return -1; + } + + cols = PQnfields(res); + rows = PQntuples(res); + if (rows == 1 && cols >= 1) { + buffer_copy_string(docroot, PQgetvalue(res, 0, 0)); + } /* else no such virtual host */ + + PQclear(res); + return 0; +} + + + + +INIT_FUNC(mod_vhostdb_init) { + static http_vhostdb_backend_t http_vhostdb_backend_pgsql = + { "pgsql", mod_vhostdb_pgsql_query, NULL }; + plugin_data *p = calloc(1, sizeof(*p)); + + /* register http_vhostdb_backend_pgsql */ + http_vhostdb_backend_pgsql.p_d = p; + http_vhostdb_backend_set(&http_vhostdb_backend_pgsql); + + return p; +} + +FREE_FUNC(mod_vhostdb_cleanup) { + plugin_data *p = p_d; + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + for (size_t i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + if (!s) continue; + mod_vhostdb_dbconf_free(s->vdata); + array_free(s->options); + free(s); + } + free(p->config_storage); + } + free(p); + + UNUSED(srv); + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_vhostdb_set_defaults) { + plugin_data *p = p_d; + + config_values_t cv[] = { + { "vhostdb.pgsql", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (size_t i = 0; i < srv->config_context->used; ++i) { + data_config const *config = (data_config const*)srv->config_context->data[i]; + plugin_config *s = calloc(1, sizeof(plugin_config)); + + s->options = array_init(); + cv[0].destination = s->options; + + p->config_storage[i] = s; + + if (config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!array_is_kvstring(s->options)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for vhostdb.pgsql; expected list of \"option\" => \"value\""); + return HANDLER_ERROR; + } + + if (s->options->used + && 0 != mod_vhostdb_dbconf_setup(srv, s->options, &s->vdata)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static void mod_vhostdb_patch_connection (server *srv, connection *con, plugin_data *p) +{ + plugin_config *s = p->config_storage[0]; + PATCH(vdata); + + /* skip the first, the global context */ + for (size_t i = 1; i < srv->config_context->used; ++i) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (size_t j = 0; j < dc->value->used; ++j) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key,CONST_STR_LEN("vhostdb.pgsql"))){ + PATCH(vdata); + } + } + } +} +#undef PATCH + +/* this function is called at dlopen() time and inits the callbacks */ +int mod_vhostdb_pgsql_plugin_init (plugin *p); +int mod_vhostdb_pgsql_plugin_init (plugin *p) +{ + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("vhostdb_pgsql"); + + p->init = mod_vhostdb_init; + p->cleanup = mod_vhostdb_cleanup; + p->set_defaults = mod_vhostdb_set_defaults; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_webdav.c b/data/lighttpd/lighttpd-1.4.53/src/mod_webdav.c new file mode 100644 index 000000000..67153e3c4 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_webdav.c @@ -0,0 +1,2820 @@ +#include "first.h" + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "fdevent.h" +#include "http_header.h" +#include "response.h" +#include "connections.h" + +#include "plugin.h" + +#include "stat_cache.h" + +#include "sys-mmap.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> + +#include <unistd.h> +#include <dirent.h> + +#if defined(HAVE_LIBXML_H) && defined(HAVE_SQLITE3_H) +#define USE_PROPPATCH +#include <libxml/tree.h> +#include <libxml/parser.h> + +#include <sqlite3.h> +#endif + +#if defined(HAVE_LIBXML_H) && defined(HAVE_SQLITE3_H) \ + && defined(HAVE_UUID) && defined(HAVE_UUID_UUID_H) +#define USE_LOCKS +#include <uuid/uuid.h> +#endif + +/** + * this is a webdav for a lighttpd plugin + * + * at least a very basic one. + * - for now it is read-only and we only support PROPFIND + * + */ + +#define WEBDAV_FILE_MODE S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH +#define WEBDAV_DIR_MODE S_IRWXU | S_IRWXG | S_IRWXO + +/* plugin config for all request/connections */ + +typedef struct { + unsigned short enabled; + unsigned short is_readonly; + unsigned short log_xml; + + buffer *sqlite_db_name; +#ifdef USE_PROPPATCH + sqlite3 *sql; + sqlite3_stmt *stmt_update_prop; + sqlite3_stmt *stmt_delete_prop; + sqlite3_stmt *stmt_select_prop; + sqlite3_stmt *stmt_select_propnames; + + sqlite3_stmt *stmt_delete_uri; + sqlite3_stmt *stmt_move_uri; + sqlite3_stmt *stmt_copy_uri; + + sqlite3_stmt *stmt_remove_lock; + sqlite3_stmt *stmt_create_lock; + sqlite3_stmt *stmt_read_lock; + sqlite3_stmt *stmt_read_lock_by_uri; + sqlite3_stmt *stmt_refresh_lock; +#endif +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + buffer *tmp_buf; + request_uri uri; + physical physical; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +typedef struct { + plugin_config conf; +} handler_ctx; + +/* init the plugin data */ +INIT_FUNC(mod_webdav_init) { + plugin_data *p; + + p = calloc(1, sizeof(*p)); + + p->tmp_buf = buffer_init(); + + p->uri.scheme = buffer_init(); + p->uri.path = buffer_init(); + p->uri.authority = buffer_init(); + + p->physical.path = buffer_init(); + p->physical.rel_path = buffer_init(); + p->physical.doc_root = buffer_init(); + p->physical.basedir = buffer_init(); + + return p; +} + +/* detroy the plugin data */ +FREE_FUNC(mod_webdav_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->sqlite_db_name); +#ifdef USE_PROPPATCH + if (s->sql) { + sqlite3_finalize(s->stmt_delete_prop); + sqlite3_finalize(s->stmt_delete_uri); + sqlite3_finalize(s->stmt_copy_uri); + sqlite3_finalize(s->stmt_move_uri); + sqlite3_finalize(s->stmt_update_prop); + sqlite3_finalize(s->stmt_select_prop); + sqlite3_finalize(s->stmt_select_propnames); + + sqlite3_finalize(s->stmt_read_lock); + sqlite3_finalize(s->stmt_read_lock_by_uri); + sqlite3_finalize(s->stmt_create_lock); + sqlite3_finalize(s->stmt_remove_lock); + sqlite3_finalize(s->stmt_refresh_lock); + sqlite3_close(s->sql); + } +#endif + free(s); + } + free(p->config_storage); + } + + buffer_free(p->uri.scheme); + buffer_free(p->uri.path); + buffer_free(p->uri.authority); + + buffer_free(p->physical.path); + buffer_free(p->physical.rel_path); + buffer_free(p->physical.doc_root); + buffer_free(p->physical.basedir); + + buffer_free(p->tmp_buf); + + free(p); + + return HANDLER_GO_ON; +} + +/* handle plugin config and check values */ + +SETDEFAULTS_FUNC(mod_webdav_set_defaults) { + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { "webdav.activate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "webdav.is-readonly", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "webdav.sqlite-db-name", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "webdav.log-xml", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->sqlite_db_name = buffer_init(); + + cv[0].destination = &(s->enabled); + cv[1].destination = &(s->is_readonly); + cv[2].destination = s->sqlite_db_name; + cv[3].destination = &(s->log_xml); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + if (!buffer_string_is_empty(s->sqlite_db_name)) { +#ifdef USE_PROPPATCH + const char *next_stmt; + char *err; + + if (SQLITE_OK != sqlite3_open(s->sqlite_db_name->ptr, &(s->sql))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", "sqlite3_open failed for", + s->sqlite_db_name, + sqlite3_errmsg(s->sql)); + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_exec(s->sql, + "CREATE TABLE IF NOT EXISTS properties (" + " resource TEXT NOT NULL," + " prop TEXT NOT NULL," + " ns TEXT NOT NULL," + " value TEXT NOT NULL," + " PRIMARY KEY(resource, prop, ns))", + NULL, NULL, &err)) { + + if (0 != strcmp(err, "table properties already exists")) { + log_error_write(srv, __FILE__, __LINE__, "ss", "can't open transaction:", err); + sqlite3_free(err); + + return HANDLER_ERROR; + } + sqlite3_free(err); + } + + if (SQLITE_OK != sqlite3_exec(s->sql, + "CREATE TABLE IF NOT EXISTS locks (" + " locktoken TEXT NOT NULL," + " resource TEXT NOT NULL," + " lockscope TEXT NOT NULL," + " locktype TEXT NOT NULL," + " owner TEXT NOT NULL," + " depth INT NOT NULL," + " timeout TIMESTAMP NOT NULL," + " PRIMARY KEY(locktoken))", + NULL, NULL, &err)) { + + if (0 != strcmp(err, "table locks already exists")) { + log_error_write(srv, __FILE__, __LINE__, "ss", "can't open transaction:", err); + sqlite3_free(err); + + return HANDLER_ERROR; + } + sqlite3_free(err); + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("SELECT value FROM properties WHERE resource = ? AND prop = ? AND ns = ?"), + &(s->stmt_select_prop), &next_stmt)) { + /* prepare failed */ + + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql)); + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("SELECT ns, prop FROM properties WHERE resource = ?"), + &(s->stmt_select_propnames), &next_stmt)) { + /* prepare failed */ + + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql)); + return HANDLER_ERROR; + } + + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("REPLACE INTO properties (resource, prop, ns, value) VALUES (?, ?, ?, ?)"), + &(s->stmt_update_prop), &next_stmt)) { + /* prepare failed */ + + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed:", sqlite3_errmsg(s->sql)); + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("DELETE FROM properties WHERE resource = ? AND prop = ? AND ns = ?"), + &(s->stmt_delete_prop), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("DELETE FROM properties WHERE resource = ?"), + &(s->stmt_delete_uri), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("INSERT INTO properties SELECT ?, prop, ns, value FROM properties WHERE resource = ?"), + &(s->stmt_copy_uri), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("UPDATE OR REPLACE properties SET resource = ? WHERE resource = ?"), + &(s->stmt_move_uri), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + /* LOCKS */ + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("INSERT INTO locks (locktoken, resource, lockscope, locktype, owner, depth, timeout) VALUES (?,?,?,?,?,?, CURRENT_TIME + 600)"), + &(s->stmt_create_lock), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("DELETE FROM locks WHERE locktoken = ?"), + &(s->stmt_remove_lock), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout-CURRENT_TIME FROM locks WHERE locktoken = ?"), + &(s->stmt_read_lock), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout-CURRENT_TIME FROM locks WHERE resource = ?"), + &(s->stmt_read_lock_by_uri), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + if (SQLITE_OK != sqlite3_prepare(s->sql, + CONST_STR_LEN("UPDATE locks SET timeout = CURRENT_TIME + 600 WHERE locktoken = ?"), + &(s->stmt_refresh_lock), &next_stmt)) { + /* prepare failed */ + log_error_write(srv, __FILE__, __LINE__, "ss", "sqlite3_prepare failed", sqlite3_errmsg(s->sql)); + + return HANDLER_ERROR; + } + + +#else + log_error_write(srv, __FILE__, __LINE__, "s", "Sorry, no sqlite3 and libxml2 support include, compile with --with-webdav-props"); + return HANDLER_ERROR; +#endif + } + } + + return HANDLER_GO_ON; +} + +#define PATCH_OPTION(x) \ + p->conf.x = s->x; +static int mod_webdav_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH_OPTION(enabled); + PATCH_OPTION(is_readonly); + PATCH_OPTION(log_xml); + +#ifdef USE_PROPPATCH + PATCH_OPTION(sql); + PATCH_OPTION(stmt_update_prop); + PATCH_OPTION(stmt_delete_prop); + PATCH_OPTION(stmt_select_prop); + PATCH_OPTION(stmt_select_propnames); + + PATCH_OPTION(stmt_delete_uri); + PATCH_OPTION(stmt_move_uri); + PATCH_OPTION(stmt_copy_uri); + + PATCH_OPTION(stmt_remove_lock); + PATCH_OPTION(stmt_refresh_lock); + PATCH_OPTION(stmt_create_lock); + PATCH_OPTION(stmt_read_lock); + PATCH_OPTION(stmt_read_lock_by_uri); +#endif + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.activate"))) { + PATCH_OPTION(enabled); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.is-readonly"))) { + PATCH_OPTION(is_readonly); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.log-xml"))) { + PATCH_OPTION(log_xml); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("webdav.sqlite-db-name"))) { +#ifdef USE_PROPPATCH + PATCH_OPTION(sql); + PATCH_OPTION(stmt_update_prop); + PATCH_OPTION(stmt_delete_prop); + PATCH_OPTION(stmt_select_prop); + PATCH_OPTION(stmt_select_propnames); + + PATCH_OPTION(stmt_delete_uri); + PATCH_OPTION(stmt_move_uri); + PATCH_OPTION(stmt_copy_uri); + + PATCH_OPTION(stmt_remove_lock); + PATCH_OPTION(stmt_refresh_lock); + PATCH_OPTION(stmt_create_lock); + PATCH_OPTION(stmt_read_lock); + PATCH_OPTION(stmt_read_lock_by_uri); +#endif + } + } + } + + return 0; +} + +URIHANDLER_FUNC(mod_webdav_uri_handler) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; + + mod_webdav_patch_connection(srv, con, p); + + if (!p->conf.enabled) return HANDLER_GO_ON; + + switch (con->request.http_method) { + case HTTP_METHOD_OPTIONS: + /* we fake a little bit but it makes MS W2k happy and it let's us mount the volume */ + http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("DAV"), CONST_STR_LEN("1,2")); + http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("MS-Author-Via"), CONST_STR_LEN("DAV")); + + if (p->conf.is_readonly) { + http_header_response_append(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Allow"), CONST_STR_LEN("PROPFIND")); + } else { + http_header_response_append(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Allow"), CONST_STR_LEN("PROPFIND, DELETE, MKCOL, PUT, MOVE, COPY, PROPPATCH, LOCK, UNLOCK")); + } + break; + default: + break; + } + + /* not found */ + return HANDLER_GO_ON; +} +static int webdav_gen_prop_tag(server *srv, connection *con, + char *prop_name, + char *prop_ns, + char *value, + buffer *b) { + + UNUSED(srv); + UNUSED(con); + + if (value) { + buffer_append_string_len(b,CONST_STR_LEN("<")); + buffer_append_string(b, prop_name); + buffer_append_string_len(b, CONST_STR_LEN(" xmlns=\"")); + buffer_append_string(b, prop_ns); + buffer_append_string_len(b, CONST_STR_LEN("\">")); + + buffer_append_string(b, value); + + buffer_append_string_len(b,CONST_STR_LEN("</")); + buffer_append_string(b, prop_name); + buffer_append_string_len(b, CONST_STR_LEN(">")); + } else { + buffer_append_string_len(b,CONST_STR_LEN("<")); + buffer_append_string(b, prop_name); + buffer_append_string_len(b, CONST_STR_LEN(" xmlns=\"")); + buffer_append_string(b, prop_ns); + buffer_append_string_len(b, CONST_STR_LEN("\"/>")); + } + + return 0; +} + + +static int webdav_gen_response_status_tag(server *srv, connection *con, physical *dst, int status, buffer *b) { + UNUSED(srv); + + buffer_append_string_len(b,CONST_STR_LEN("<D:response xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:href>\n")); + buffer_append_string_buffer(b, dst->rel_path); + buffer_append_string_len(b,CONST_STR_LEN("</D:href>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:status>\n")); + + if (con->request.http_version == HTTP_VERSION_1_1) { + buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1 ")); + } else { + buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.0 ")); + } + http_status_append(b, status); + + buffer_append_string_len(b,CONST_STR_LEN("</D:status>\n")); + buffer_append_string_len(b,CONST_STR_LEN("</D:response>\n")); + + return 0; +} + +static int webdav_delete_file(server *srv, connection *con, handler_ctx *hctx, physical *dst, buffer *b) { + int status = 0; + + /* try to unlink it */ + if (-1 == unlink(dst->path->ptr)) { + switch(errno) { + case EACCES: + case EPERM: + /* 403 */ + status = 403; + break; + default: + status = 501; + break; + } + webdav_gen_response_status_tag(srv, con, dst, status, b); + } else { +#ifdef USE_PROPPATCH + sqlite3_stmt *stmt = hctx->conf.stmt_delete_uri; + + if (!stmt) { + status = 403; + webdav_gen_response_status_tag(srv, con, dst, status, b); + } else { + sqlite3_reset(stmt); + + /* bind the values to the insert */ + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(dst->rel_path), + SQLITE_TRANSIENT); + + if (SQLITE_DONE != sqlite3_step(stmt)) { + /* */ + } + } +#else + UNUSED(hctx); +#endif + } + + return (status != 0); +} + +static int webdav_delete_dir(server *srv, connection *con, handler_ctx *hctx, physical *dst, buffer *b) { + DIR *dir; + int have_multi_status = 0; + physical d; + + d.path = buffer_init(); + d.rel_path = buffer_init(); + + if (NULL != (dir = opendir(dst->path->ptr))) { + struct dirent *de; + + while(NULL != (de = readdir(dir))) { + struct stat st; + size_t nlen; + + if ((de->d_name[0] == '.' && de->d_name[1] == '\0') || + (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) { + continue; + /* ignore the parent dir */ + } + + nlen = strlen(de->d_name); + buffer_copy_buffer(d.path, dst->path); + buffer_append_path_len(d.path, de->d_name, nlen); + + buffer_copy_buffer(d.rel_path, dst->rel_path); + buffer_append_path_len(d.rel_path, de->d_name, nlen); + + /* stat and unlink afterwards */ + if (-1 == stat(d.path->ptr, &st)) { + /* don't about it yet, rmdir will fail too */ + } else if (S_ISDIR(st.st_mode)) { + have_multi_status = webdav_delete_dir(srv, con, hctx, &d, b); + + /* try to unlink it */ + if (-1 == rmdir(d.path->ptr)) { + int status; + switch(errno) { + case EACCES: + case EPERM: + /* 403 */ + status = 403; + break; + default: + status = 501; + break; + } + have_multi_status = 1; + + webdav_gen_response_status_tag(srv, con, &d, status, b); + } else { +#ifdef USE_PROPPATCH + sqlite3_stmt *stmt = hctx->conf.stmt_delete_uri; + + if (stmt) { + sqlite3_reset(stmt); + + /* bind the values to the insert */ + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(d.rel_path), + SQLITE_TRANSIENT); + + if (SQLITE_DONE != sqlite3_step(stmt)) { + /* */ + } + } +#endif + } + } else { + have_multi_status = webdav_delete_file(srv, con, hctx, &d, b); + } + } + closedir(dir); + + buffer_free(d.path); + buffer_free(d.rel_path); + } + + return have_multi_status; +} + +/* don't want to block when open()ing a fifo */ +#if defined(O_NONBLOCK) +# define FIFO_NONBLOCK O_NONBLOCK +#else +# define FIFO_NONBLOCK 0 +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +static int webdav_copy_file(server *srv, connection *con, handler_ctx *hctx, physical *src, physical *dst, int overwrite) { + char *data; + ssize_t rd, wr, offset; + int status = 0, ifd, ofd; + UNUSED(srv); + UNUSED(con); + + if (-1 == (ifd = open(src->path->ptr, O_RDONLY | O_BINARY | FIFO_NONBLOCK))) { + return 403; + } + + if (-1 == (ofd = open(dst->path->ptr, O_WRONLY|O_TRUNC|O_CREAT|(overwrite ? 0 : O_EXCL), WEBDAV_FILE_MODE))) { + /* opening the destination failed for some reason */ + switch(errno) { + case EEXIST: + status = 412; + break; + case EISDIR: + status = 409; + break; + case ENOENT: + /* at least one part in the middle wasn't existing */ + status = 409; + break; + default: + status = 403; + break; + } + close(ifd); + return status; + } + + data = malloc(131072); + force_assert(data); + + while (0 < (rd = read(ifd, data, 131072))) { + offset = 0; + do { + wr = write(ofd, data+offset, (size_t)(rd-offset)); + } while (wr >= 0 ? (offset += wr) != rd : (errno == EINTR)); + if (-1 == wr) { + status = (errno == ENOSPC) ? 507 : 403; + break; + } + + } + if (0 != rd && 0 == status) status = 403; + + free(data); + close(ifd); + if (0 != close(ofd)) { + if (0 == status) status = (errno == ENOSPC) ? 507 : 403; + log_error_write(srv, __FILE__, __LINE__, "sbss", + "close ", dst->path, "failed: ", strerror(errno)); + } + +#ifdef USE_PROPPATCH + if (0 == status) { + /* copy worked fine, copy connected properties */ + sqlite3_stmt *stmt = hctx->conf.stmt_copy_uri; + + if (stmt) { + sqlite3_reset(stmt); + + /* bind the values to the insert */ + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(dst->rel_path), + SQLITE_TRANSIENT); + + sqlite3_bind_text(stmt, 2, + CONST_BUF_LEN(src->rel_path), + SQLITE_TRANSIENT); + + if (SQLITE_DONE != sqlite3_step(stmt)) { + /* */ + } + } + } +#else + UNUSED(hctx); +#endif + return status; +} + +static int webdav_copy_dir(server *srv, connection *con, handler_ctx *hctx, physical *src, physical *dst, int overwrite) { + DIR *srcdir; + int status = 0; + + if (NULL != (srcdir = opendir(src->path->ptr))) { + struct dirent *de; + physical s, d; + + s.path = buffer_init(); + s.rel_path = buffer_init(); + + d.path = buffer_init(); + d.rel_path = buffer_init(); + + while (NULL != (de = readdir(srcdir))) { + struct stat st; + size_t nlen; + + if ((de->d_name[0] == '.' && de->d_name[1] == '\0') + || (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) { + continue; + } + + nlen = strlen(de->d_name); + buffer_copy_buffer(s.path, src->path); + buffer_append_path_len(s.path, de->d_name, nlen); + + buffer_copy_buffer(d.path, dst->path); + buffer_append_path_len(d.path, de->d_name, nlen); + + buffer_copy_buffer(s.rel_path, src->rel_path); + buffer_append_path_len(s.rel_path, de->d_name, nlen); + + buffer_copy_buffer(d.rel_path, dst->rel_path); + buffer_append_path_len(d.rel_path, de->d_name, nlen); + + if (-1 == stat(s.path->ptr, &st)) { + /* why ? */ + } else if (S_ISDIR(st.st_mode)) { + /* a directory */ + if (-1 == mkdir(d.path->ptr, WEBDAV_DIR_MODE) && + errno != EEXIST) { + /* WTH ? */ + } else { +#ifdef USE_PROPPATCH + sqlite3_stmt *stmt = hctx->conf.stmt_copy_uri; + + if (0 != (status = webdav_copy_dir(srv, con, hctx, &s, &d, overwrite))) { + break; + } + /* directory is copied, copy the properties too */ + + if (stmt) { + sqlite3_reset(stmt); + + /* bind the values to the insert */ + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(dst->rel_path), + SQLITE_TRANSIENT); + + sqlite3_bind_text(stmt, 2, + CONST_BUF_LEN(src->rel_path), + SQLITE_TRANSIENT); + + if (SQLITE_DONE != sqlite3_step(stmt)) { + /* */ + } + } +#endif + } + } else if (S_ISREG(st.st_mode)) { + /* a plain file */ + if (0 != (status = webdav_copy_file(srv, con, hctx, &s, &d, overwrite))) { + break; + } + } + } + + buffer_free(s.path); + buffer_free(s.rel_path); + buffer_free(d.path); + buffer_free(d.rel_path); + + closedir(srcdir); + } + + return status; +} + +#ifdef USE_LOCKS +static void webdav_activelock(buffer *b, + const buffer *locktoken, const char *lockscope, const char *locktype, int depth, int timeout) { + buffer_append_string_len(b, CONST_STR_LEN("<D:activelock>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<D:lockscope>")); + buffer_append_string_len(b, CONST_STR_LEN("<D:")); + buffer_append_string(b, lockscope); + buffer_append_string_len(b, CONST_STR_LEN("/>")); + buffer_append_string_len(b, CONST_STR_LEN("</D:lockscope>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<D:locktype>")); + buffer_append_string_len(b, CONST_STR_LEN("<D:")); + buffer_append_string(b, locktype); + buffer_append_string_len(b, CONST_STR_LEN("/>")); + buffer_append_string_len(b, CONST_STR_LEN("</D:locktype>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<D:depth>")); + buffer_append_string(b, depth == 0 ? "0" : "infinity"); + buffer_append_string_len(b, CONST_STR_LEN("</D:depth>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<D:timeout>")); + buffer_append_string_len(b, CONST_STR_LEN("Second-")); + buffer_append_int(b, timeout); + buffer_append_string_len(b, CONST_STR_LEN("</D:timeout>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<D:owner>")); + buffer_append_string_len(b, CONST_STR_LEN("</D:owner>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("<D:locktoken>")); + buffer_append_string_len(b, CONST_STR_LEN("<D:href>")); + buffer_append_string_buffer(b, locktoken); + buffer_append_string_len(b, CONST_STR_LEN("</D:href>")); + buffer_append_string_len(b, CONST_STR_LEN("</D:locktoken>\n")); + + buffer_append_string_len(b, CONST_STR_LEN("</D:activelock>\n")); +} + +static void webdav_get_live_property_lockdiscovery(server *srv, connection *con, handler_ctx *hctx, physical *dst, buffer *b) { + + sqlite3_stmt *stmt = hctx->conf.stmt_read_lock_by_uri; + if (!stmt) { /*(should not happen)*/ + buffer_append_string_len(b, CONST_STR_LEN("<D:lockdiscovery>\n</D:lockdiscovery>\n")); + return; + } + UNUSED(srv); + UNUSED(con); + + /* SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout + * FROM locks + * WHERE resource = ? */ + + sqlite3_reset(stmt); + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(dst->rel_path), + SQLITE_TRANSIENT); + + buffer_append_string_len(b, CONST_STR_LEN("<D:lockdiscovery>\n")); + while (SQLITE_ROW == sqlite3_step(stmt)) { + const char *lockscope = (const char *)sqlite3_column_text(stmt, 2); + const char *locktype = (const char *)sqlite3_column_text(stmt, 3); + const int depth = sqlite3_column_int(stmt, 5); + const int timeout = sqlite3_column_int(stmt, 6); + buffer locktoken = { NULL, 0, 0 }; + locktoken.ptr = (char *)sqlite3_column_text(stmt, 0); + locktoken.used = sqlite3_column_bytes(stmt, 0); + if (locktoken.used) ++locktoken.used; + locktoken.size = locktoken.used; + + if (timeout > 0) { + webdav_activelock(b, &locktoken, lockscope, locktype, depth, timeout); + } + } + buffer_append_string_len(b, CONST_STR_LEN("</D:lockdiscovery>\n")); +} +#endif + +static int webdav_get_live_property(server *srv, connection *con, handler_ctx *hctx, physical *dst, char *prop_name, buffer *b) { + stat_cache_entry *sce = NULL; + int found = 0; + + UNUSED(hctx); + + if (HANDLER_ERROR != (stat_cache_get_entry(srv, con, dst->path, &sce))) { + char ctime_buf[] = "2005-08-18T07:27:16Z"; + char mtime_buf[] = "Thu, 18 Aug 2005 07:27:16 GMT"; + + if (0 == strcmp(prop_name, "resourcetype")) { + if (S_ISDIR(sce->st.st_mode)) { + buffer_append_string_len(b, CONST_STR_LEN("<D:resourcetype><D:collection/></D:resourcetype>")); + } else { + buffer_append_string_len(b, CONST_STR_LEN("<D:resourcetype/>")); + } + found = 1; + } else if (0 == strcmp(prop_name, "getcontenttype")) { + if (S_ISDIR(sce->st.st_mode)) { + buffer_append_string_len(b, CONST_STR_LEN("<D:getcontenttype>httpd/unix-directory</D:getcontenttype>")); + found = 1; + } else if(S_ISREG(sce->st.st_mode)) { + const buffer *type = stat_cache_mimetype_by_ext(con, CONST_BUF_LEN(dst->path)); + if (NULL != type) { + buffer_append_string_len(b, CONST_STR_LEN("<D:getcontenttype>")); + buffer_append_string_buffer(b, type); + buffer_append_string_len(b, CONST_STR_LEN("</D:getcontenttype>")); + found = 1; + } + } + } else if (0 == strcmp(prop_name, "creationdate")) { + buffer_append_string_len(b, CONST_STR_LEN("<D:creationdate ns0:dt=\"dateTime.tz\">")); + strftime(ctime_buf, sizeof(ctime_buf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&(sce->st.st_ctime))); + buffer_append_string(b, ctime_buf); + buffer_append_string_len(b, CONST_STR_LEN("</D:creationdate>")); + found = 1; + } else if (0 == strcmp(prop_name, "getlastmodified")) { + buffer_append_string_len(b,CONST_STR_LEN("<D:getlastmodified ns0:dt=\"dateTime.rfc1123\">")); + strftime(mtime_buf, sizeof(mtime_buf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(sce->st.st_mtime))); + buffer_append_string(b, mtime_buf); + buffer_append_string_len(b, CONST_STR_LEN("</D:getlastmodified>")); + found = 1; + } else if (0 == strcmp(prop_name, "getcontentlength")) { + buffer_append_string_len(b,CONST_STR_LEN("<D:getcontentlength>")); + buffer_append_int(b, sce->st.st_size); + buffer_append_string_len(b, CONST_STR_LEN("</D:getcontentlength>")); + found = 1; + } else if (0 == strcmp(prop_name, "getcontentlanguage")) { + buffer_append_string_len(b,CONST_STR_LEN("<D:getcontentlanguage>")); + buffer_append_string_len(b, CONST_STR_LEN("en")); + buffer_append_string_len(b, CONST_STR_LEN("</D:getcontentlanguage>")); + found = 1; + } else if (0 == strcmp(prop_name, "getetag")) { + etag_create(con->physical.etag, &sce->st, con->etag_flags); + etag_mutate(con->physical.etag, con->physical.etag); + buffer_append_string_len(b, CONST_STR_LEN("<D:getetag>")); + buffer_append_string_buffer(b, con->physical.etag); + buffer_append_string_len(b, CONST_STR_LEN("</D:getetag>")); + buffer_clear(con->physical.etag); + found = 1; + #ifdef USE_LOCKS + } else if (0 == strcmp(prop_name, "lockdiscovery")) { + webdav_get_live_property_lockdiscovery(srv, con, hctx, dst, b); + found = 1; + } else if (0 == strcmp(prop_name, "supportedlock")) { + buffer_append_string_len(b,CONST_STR_LEN("<D:supportedlock>")); + buffer_append_string_len(b,CONST_STR_LEN("<D:lockentry>")); + buffer_append_string_len(b,CONST_STR_LEN("<D:lockscope><D:exclusive/></D:lockscope>")); + buffer_append_string_len(b,CONST_STR_LEN("<D:locktype><D:write/></D:locktype>")); + buffer_append_string_len(b,CONST_STR_LEN("</D:lockentry>")); + buffer_append_string_len(b, CONST_STR_LEN("</D:supportedlock>")); + found = 1; + #endif + } + } + + return found ? 0 : -1; +} + +static int webdav_get_property(server *srv, connection *con, handler_ctx *hctx, physical *dst, char *prop_name, char *prop_ns, buffer *b) { + if (0 == strcmp(prop_ns, "DAV:")) { + /* a local 'live' property */ + return webdav_get_live_property(srv, con, hctx, dst, prop_name, b); + } else { + int found = 0; +#ifdef USE_PROPPATCH + sqlite3_stmt *stmt = hctx->conf.stmt_select_prop; + + if (stmt) { + /* perhaps it is in sqlite3 */ + sqlite3_reset(stmt); + + /* bind the values to the insert */ + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(dst->rel_path), + SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, + prop_name, + strlen(prop_name), + SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 3, + prop_ns, + strlen(prop_ns), + SQLITE_TRANSIENT); + + /* it is the PK */ + while (SQLITE_ROW == sqlite3_step(stmt)) { + /* there is a row for us, we only expect a single col 'value' */ + webdav_gen_prop_tag(srv, con, prop_name, prop_ns, (char *)sqlite3_column_text(stmt, 0), b); + found = 1; + } + } +#endif + return found ? 0 : -1; + } + + /* not found */ + return -1; +} + +typedef struct { + char *ns; + char *prop; +} webdav_property; + +static webdav_property live_properties[] = { + { "DAV:", "creationdate" }, + /*{ "DAV:", "displayname" },*//*(not implemented)*/ + { "DAV:", "getcontentlanguage" }, + { "DAV:", "getcontentlength" }, + { "DAV:", "getcontenttype" }, + { "DAV:", "getetag" }, + { "DAV:", "getlastmodified" }, + { "DAV:", "resourcetype" }, + /*{ "DAV:", "source" },*//*(not implemented)*/ + #ifdef USE_LOCKS + { "DAV:", "lockdiscovery" }, + { "DAV:", "supportedlock" }, + #endif + + { NULL, NULL } +}; + +typedef struct { + webdav_property **ptr; + + size_t used; + size_t size; +} webdav_properties; + +static int webdav_get_props(server *srv, connection *con, handler_ctx *hctx, physical *dst, webdav_properties *props, buffer *b_200, buffer *b_404) { + size_t i; + + if (props && props->used) { + for (i = 0; i < props->used; i++) { + webdav_property *prop; + + prop = props->ptr[i]; + + if (0 != webdav_get_property(srv, con, hctx, + dst, prop->prop, prop->ns, b_200)) { + webdav_gen_prop_tag(srv, con, prop->prop, prop->ns, NULL, b_404); + } + } + } else { + for (i = 0; live_properties[i].prop; i++) { + /* a local 'live' property */ + webdav_get_live_property(srv, con, hctx, dst, live_properties[i].prop, b_200); + } + } + + return 0; +} + +#ifdef USE_PROPPATCH +static int webdav_parse_chunkqueue(server *srv, connection *con, handler_ctx *hctx, chunkqueue *cq, xmlDoc **ret_xml) { + xmlParserCtxtPtr ctxt; + xmlDoc *xml; + int res; + int err; + + chunk *c; + + UNUSED(con); + + /* read the chunks in to the XML document */ + ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL); + + for (c = cq->first; cq->bytes_out != cq->bytes_in; c = cq->first) { + size_t weWant = cq->bytes_out - cq->bytes_in; + size_t weHave; + int mapped; + void *data; + + switch(c->type) { + case FILE_CHUNK: + weHave = c->file.length - c->offset; + + if (weHave > weWant) weHave = weWant; + + /* xml chunks are always memory, mmap() is our friend */ + mapped = (c->file.mmap.start != MAP_FAILED); + if (mapped) { + data = c->file.mmap.start + c->offset; + } else { + if (-1 == c->file.fd && /* open the file if not already open */ + -1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, O_RDONLY, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); + + return -1; + } + + if (MAP_FAILED != (c->file.mmap.start = mmap(0, c->file.length, PROT_READ, MAP_PRIVATE, c->file.fd, 0))) { + /* chunk_reset() or chunk_free() will cleanup for us */ + c->file.mmap.length = c->file.length; + data = c->file.mmap.start + c->offset; + mapped = 1; + } else { + ssize_t rd; + if (weHave > 65536) weHave = 65536; + data = malloc(weHave); + force_assert(data); + if (-1 == lseek(c->file.fd, c->file.start + c->offset, SEEK_SET) + || 0 > (rd = read(c->file.fd, data, weHave))) { + log_error_write(srv, __FILE__, __LINE__, "ssbd", "lseek/read failed: ", + strerror(errno), c->mem, c->file.fd); + free(data); + return -1; + } + weHave = (size_t)rd; + } + } + + if (XML_ERR_OK != (err = xmlParseChunk(ctxt, data, weHave, 0))) { + log_error_write(srv, __FILE__, __LINE__, "sodd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err); + } + + chunkqueue_mark_written(cq, weHave); + + if (!mapped) free(data); + break; + case MEM_CHUNK: + /* append to the buffer */ + weHave = buffer_string_length(c->mem) - c->offset; + + if (weHave > weWant) weHave = weWant; + + if (hctx->conf.log_xml) { + log_error_write(srv, __FILE__, __LINE__, "ss", "XML-request-body:", c->mem->ptr + c->offset); + } + + if (XML_ERR_OK != (err = xmlParseChunk(ctxt, c->mem->ptr + c->offset, weHave, 0))) { + log_error_write(srv, __FILE__, __LINE__, "sodd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err); + } + + chunkqueue_mark_written(cq, weHave); + + break; + } + } + + switch ((err = xmlParseChunk(ctxt, 0, 0, 1))) { + case XML_ERR_DOCUMENT_END: + case XML_ERR_OK: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sd", "xmlParseChunk failed at final packet:", err); + break; + } + + xml = ctxt->myDoc; + res = ctxt->wellFormed; + xmlFreeParserCtxt(ctxt); + + if (res == 0) { + xmlFreeDoc(xml); + } else { + *ret_xml = xml; + } + + return res; +} +#endif + +#ifdef USE_LOCKS +static int webdav_lockdiscovery(connection *con, buffer *locktoken, const char *lockscope, const char *locktype, int depth) { + + buffer *b = chunkqueue_append_buffer_open(con->write_queue); + + http_header_response_set(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Lock-Token"), CONST_BUF_LEN(locktoken)); + + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, + CONST_STR_LEN("Content-Type"), + CONST_STR_LEN("text/xml; charset=\"utf-8\"")); + + buffer_copy_string_len(b, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:prop xmlns:D=\"DAV:\" xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:lockdiscovery>\n")); + webdav_activelock(b, locktoken, lockscope, locktype, depth, 600); + buffer_append_string_len(b,CONST_STR_LEN("</D:lockdiscovery>\n")); + buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n")); + + chunkqueue_append_buffer_commit(con->write_queue); + + return 0; +} +#endif + +/** + * check if resource is having the right locks to access to resource + * + * + * + */ +static int webdav_has_lock(server *srv, connection *con, handler_ctx *hctx, buffer *uri) { + int has_lock = 1; + +#ifdef USE_LOCKS + buffer *vb; + UNUSED(srv); + + /** + * This implementation is more fake than real + * we need a parser for the If: header to really handle the full scope + * + * X-Litmus: locks: 11 (owner_modify) + * If: <http://127.0.0.1:1025/dav/litmus/lockme> (<opaquelocktoken:2165478d-0611-49c4-be92-e790d68a38f1>) + * - a tagged check: + * if http://127.0.0.1:1025/dav/litmus/lockme is locked with + * opaquelocktoken:2165478d-0611-49c4-be92-e790d68a38f1, go on + * + * X-Litmus: locks: 16 (fail_cond_put) + * If: (<DAV:no-lock> ["-1622396671"]) + * - untagged: + * go on if the resource has the etag [...] and the lock + */ + if (NULL != (vb = http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("If")))) { + /* Ooh, ooh. A if tag, now the fun begins. + * + * this can only work with a real parser + **/ + } else { + /* we didn't provided a lock-token -> */ + /* if the resource is locked -> 423 */ + + sqlite3_stmt *stmt = hctx->conf.stmt_read_lock_by_uri; + + sqlite3_reset(stmt); + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(uri), + SQLITE_TRANSIENT); + + while (SQLITE_ROW == sqlite3_step(stmt)) { + has_lock = 0; + } + } +#else + UNUSED(srv); + UNUSED(con); + UNUSED(hctx); + UNUSED(uri); +#endif + + return has_lock; +} + +static int mod_webdav_depth(connection *con) { + buffer *b = + http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Depth")); + if (NULL != b && 1 == buffer_string_length(b)) { + if (b->ptr[0] == '0') return 0; + if (b->ptr[0] == '1') return 1; + } + return -1; /* (Depth: infinity) */ +} + +static handler_t mod_webdav_propfind(server *srv, connection *con, plugin_data *p, handler_ctx *hctx) { + buffer *b; + DIR *dir; + int depth = mod_webdav_depth(con); + struct stat st; + buffer *prop_200; + buffer *prop_404; + webdav_properties *req_props; + stat_cache_entry *sce = NULL; + + /* they want to know the properties of the directory */ + req_props = NULL; + + /* is there a content-body ? */ + + switch (stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + case HANDLER_ERROR: + if (errno == ENOENT) { + con->http_status = 404; + return HANDLER_FINISHED; + } + else if (errno == EACCES) { + con->http_status = 403; + return HANDLER_FINISHED; + } + else { + con->http_status = 500; + return HANDLER_FINISHED; + } + break; + default: + break; + } + + if (S_ISDIR(sce->st.st_mode) && con->physical.path->ptr[buffer_string_length(con->physical.path)-1] != '/') { + http_response_redirect_to_directory(srv, con); + return HANDLER_FINISHED; + } + +#ifdef USE_PROPPATCH + /* any special requests or just allprop ? */ + if (con->request.content_length) { + xmlDocPtr xml; + + if (con->state == CON_STATE_READ_POST) { + handler_t r = connection_handle_read_post_state(srv, con); + if (r != HANDLER_GO_ON) return r; + } + + if (1 == webdav_parse_chunkqueue(srv, con, hctx, con->request_content_queue, &xml)) { + xmlNode *rootnode = xmlDocGetRootElement(xml); + + force_assert(rootnode); + + if (0 == xmlStrcmp(rootnode->name, BAD_CAST "propfind")) { + xmlNode *cmd; + + req_props = calloc(1, sizeof(*req_props)); + + for (cmd = rootnode->children; cmd; cmd = cmd->next) { + + if (0 == xmlStrcmp(cmd->name, BAD_CAST "prop")) { + /* get prop by name */ + xmlNode *prop; + + for (prop = cmd->children; prop; prop = prop->next) { + if (prop->type == XML_TEXT_NODE) continue; /* ignore WS */ + + if (prop->ns && + (0 == xmlStrcmp(prop->ns->href, BAD_CAST "")) && + (0 != xmlStrcmp(prop->ns->prefix, BAD_CAST ""))) { + size_t i; + log_error_write(srv, __FILE__, __LINE__, "ss", + "no name space for:", + prop->name); + + xmlFreeDoc(xml); + + for (i = 0; i < req_props->used; i++) { + free(req_props->ptr[i]->ns); + free(req_props->ptr[i]->prop); + free(req_props->ptr[i]); + } + free(req_props->ptr); + free(req_props); + + con->http_status = 400; + return HANDLER_FINISHED; + } + + /* add property to requested list */ + if (req_props->size == 0) { + req_props->size = 16; + req_props->ptr = malloc(sizeof(*(req_props->ptr)) * req_props->size); + } else if (req_props->used == req_props->size) { + req_props->size += 16; + req_props->ptr = realloc(req_props->ptr, sizeof(*(req_props->ptr)) * req_props->size); + } + + req_props->ptr[req_props->used] = malloc(sizeof(webdav_property)); + req_props->ptr[req_props->used]->ns = (char *)xmlStrdup(prop->ns ? prop->ns->href : (xmlChar *)""); + req_props->ptr[req_props->used]->prop = (char *)xmlStrdup(prop->name); + req_props->used++; + } + } else if (0 == xmlStrcmp(cmd->name, BAD_CAST "propname")) { + sqlite3_stmt *stmt = p->conf.stmt_select_propnames; + + if (stmt) { + /* get all property names (EMPTY) */ + sqlite3_reset(stmt); + /* bind the values to the insert */ + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(con->uri.path), + SQLITE_TRANSIENT); + + if (SQLITE_DONE != sqlite3_step(stmt)) { + } + } + } else if (0 == xmlStrcmp(cmd->name, BAD_CAST "allprop")) { + /* get all properties (EMPTY) */ + } + } + } + + xmlFreeDoc(xml); + } else { + con->http_status = 400; + return HANDLER_FINISHED; + } + } +#endif + con->http_status = 207; + + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml; charset=\"utf-8\"")); + + b = chunkqueue_append_buffer_open(con->write_queue); + + buffer_copy_string_len(b, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:multistatus xmlns:D=\"DAV:\" xmlns:ns0=\"urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/\">\n")); + + /* allprop */ + + prop_200 = buffer_init(); + prop_404 = buffer_init(); + + { + /* Depth: 0 or Depth: 1 */ + webdav_get_props(srv, con, hctx, &(con->physical), req_props, prop_200, prop_404); + + buffer_append_string_len(b,CONST_STR_LEN("<D:response>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:href>")); + buffer_append_string_buffer(b, con->uri.scheme); + buffer_append_string_len(b,CONST_STR_LEN("://")); + buffer_append_string_buffer(b, con->uri.authority); + buffer_append_string_encoded(b, CONST_BUF_LEN(con->uri.path), ENCODING_REL_URI); + buffer_append_string_len(b,CONST_STR_LEN("</D:href>\n")); + + if (!buffer_string_is_empty(prop_200)) { + buffer_append_string_len(b,CONST_STR_LEN("<D:propstat>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:prop>\n")); + + buffer_append_string_buffer(b, prop_200); + + buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:status>HTTP/1.1 200 OK</D:status>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("</D:propstat>\n")); + } + if (!buffer_string_is_empty(prop_404)) { + buffer_append_string_len(b,CONST_STR_LEN("<D:propstat>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:prop>\n")); + + buffer_append_string_buffer(b, prop_404); + + buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:status>HTTP/1.1 404 Not Found</D:status>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("</D:propstat>\n")); + } + + buffer_append_string_len(b,CONST_STR_LEN("</D:response>\n")); + } + + if (depth == 1) { + + if (NULL != (dir = opendir(con->physical.path->ptr))) { + struct dirent *de; + physical d; + physical *dst = &(con->physical); + + d.path = buffer_init(); + d.rel_path = buffer_init(); + + while(NULL != (de = readdir(dir))) { + size_t nlen; + if (de->d_name[0] == '.' && (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0'))) { + continue; + /* ignore the parent and target dir */ + } + + nlen = strlen(de->d_name); + buffer_copy_buffer(d.path, dst->path); + buffer_append_path_len(d.path, de->d_name, nlen); + + buffer_copy_buffer(d.rel_path, dst->rel_path); + buffer_append_path_len(d.rel_path, de->d_name, nlen); + + buffer_clear(prop_200); + buffer_clear(prop_404); + + webdav_get_props(srv, con, hctx, &d, req_props, prop_200, prop_404); + + buffer_append_string_len(b,CONST_STR_LEN("<D:response>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:href>")); + buffer_append_string_buffer(b, con->uri.scheme); + buffer_append_string_len(b,CONST_STR_LEN("://")); + buffer_append_string_buffer(b, con->uri.authority); + buffer_append_string_encoded(b, CONST_BUF_LEN(d.rel_path), ENCODING_REL_URI); + if (0 == stat(d.path->ptr, &st) && S_ISDIR(st.st_mode)) { + /* Append a '/' on subdirectories */ + buffer_append_string_len(b,CONST_STR_LEN("/")); + } + buffer_append_string_len(b,CONST_STR_LEN("</D:href>\n")); + + if (!buffer_string_is_empty(prop_200)) { + buffer_append_string_len(b,CONST_STR_LEN("<D:propstat>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:prop>\n")); + + buffer_append_string_buffer(b, prop_200); + + buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:status>HTTP/1.1 200 OK</D:status>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("</D:propstat>\n")); + } + if (!buffer_string_is_empty(prop_404)) { + buffer_append_string_len(b,CONST_STR_LEN("<D:propstat>\n")); + buffer_append_string_len(b,CONST_STR_LEN("<D:prop>\n")); + + buffer_append_string_buffer(b, prop_404); + + buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:status>HTTP/1.1 404 Not Found</D:status>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("</D:propstat>\n")); + } + + buffer_append_string_len(b,CONST_STR_LEN("</D:response>\n")); + } + closedir(dir); + buffer_free(d.path); + buffer_free(d.rel_path); + } + + } + + if (req_props) { + size_t i; + for (i = 0; i < req_props->used; i++) { + free(req_props->ptr[i]->ns); + free(req_props->ptr[i]->prop); + free(req_props->ptr[i]); + } + free(req_props->ptr); + free(req_props); + } + + buffer_free(prop_200); + buffer_free(prop_404); + + buffer_append_string_len(b,CONST_STR_LEN("</D:multistatus>\n")); + + if (p->conf.log_xml) { + log_error_write(srv, __FILE__, __LINE__, "sb", "XML-response-body:", b); + } + + chunkqueue_append_buffer_commit(con->write_queue); + + con->file_finished = 1; + + return HANDLER_FINISHED; +} + +static handler_t mod_webdav_mkcol(connection *con, plugin_data *p) { + if (p->conf.is_readonly) { + con->http_status = 403; + return HANDLER_FINISHED; + } + + if (con->request.content_length != 0) { + /* we don't support MKCOL with a body */ + con->http_status = 415; + + return HANDLER_FINISHED; + } + + /* let's create the directory */ + + if (-1 == mkdir(con->physical.path->ptr, WEBDAV_DIR_MODE)) { + switch(errno) { + case EPERM: + con->http_status = 403; + break; + case ENOENT: + case ENOTDIR: + con->http_status = 409; + break; + case EEXIST: + default: + con->http_status = 405; /* not allowed */ + break; + } + } else { + con->http_status = 201; + con->file_finished = 1; + } + + return HANDLER_FINISHED; +} + +static handler_t mod_webdav_delete(server *srv, connection *con, plugin_data *p, handler_ctx *hctx) { + struct stat st; + + if (p->conf.is_readonly) { + con->http_status = 403; + return HANDLER_FINISHED; + } + + /* does the client have a lock for this connection ? */ + if (!webdav_has_lock(srv, con, hctx, con->uri.path)) { + con->http_status = 423; + return HANDLER_FINISHED; + } + + /* stat and unlink afterwards */ + if (-1 == stat(con->physical.path->ptr, &st)) { + /* don't about it yet, unlink will fail too */ + switch(errno) { + case ENOENT: + con->http_status = 404; + break; + default: + con->http_status = 403; + break; + } + } else if (S_ISDIR(st.st_mode)) { + buffer *multi_status_resp; + + if (con->physical.path->ptr[buffer_string_length(con->physical.path)-1] != '/') { + http_response_redirect_to_directory(srv, con); + return HANDLER_FINISHED; + } + + multi_status_resp = buffer_init(); + + if (webdav_delete_dir(srv, con, hctx, &(con->physical), multi_status_resp)) { + /* we got an error somewhere in between, build a 207 */ + buffer *b; + http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml; charset=\"utf-8\"")); + + b = chunkqueue_append_buffer_open(con->write_queue); + + buffer_copy_string_len(b, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")); + + buffer_append_string_len(b,CONST_STR_LEN("<D:multistatus xmlns:D=\"DAV:\">\n")); + + buffer_append_string_buffer(b, multi_status_resp); + + buffer_append_string_len(b,CONST_STR_LEN("</D:multistatus>\n")); + + if (p->conf.log_xml) { + log_error_write(srv, __FILE__, __LINE__, "sb", "XML-response-body:", b); + } + + chunkqueue_append_buffer_commit(con->write_queue); + + con->http_status = 207; + con->file_finished = 1; + } else { + /* everything went fine, remove the directory */ + + if (-1 == rmdir(con->physical.path->ptr)) { + switch(errno) { + case EPERM: + con->http_status = 403; + break; + case ENOENT: + con->http_status = 404; + break; + default: + con->http_status = 501; + break; + } + } else { + con->http_status = 204; + } + } + + buffer_free(multi_status_resp); + } else if (-1 == unlink(con->physical.path->ptr)) { + switch(errno) { + case EPERM: + con->http_status = 403; + break; + case ENOENT: + con->http_status = 404; + break; + default: + con->http_status = 501; + break; + } + } else { + con->http_status = 204; + } + return HANDLER_FINISHED; +} + +static handler_t mod_webdav_put(server *srv, connection *con, plugin_data *p, handler_ctx *hctx) { + buffer *b; + int fd; + chunkqueue *cq = con->request_content_queue; + chunk *c; + + if (p->conf.is_readonly) { + con->http_status = 403; + return HANDLER_FINISHED; + } + + /* is a exclusive lock set on the source */ + /* (check for lock once before potentially reading large input) */ + if (0 == cq->bytes_in && !webdav_has_lock(srv, con, hctx, con->uri.path)) { + con->http_status = 423; + return HANDLER_FINISHED; + } + + if (con->state == CON_STATE_READ_POST) { + handler_t r = connection_handle_read_post_state(srv, con); + if (r != HANDLER_GO_ON) return r; + } + + /* RFC2616 Section 9.6 PUT requires us to send 501 on all Content-* we don't support + * - most important Content-Range + * + * + * Example: Content-Range: bytes 100-1037/1038 */ + + if (NULL != (b = http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Content-Range")))) { + const char *num = b->ptr; + off_t offset; + char *err = NULL; + + if (0 != strncmp(num, "bytes ", 6)) { + con->http_status = 501; /* not implemented */ + + return HANDLER_FINISHED; + } + + /* we only support <num>- ... */ + + num += 6; + + /* skip WS */ + while (*num == ' ' || *num == '\t') num++; + + if (*num == '\0') { + con->http_status = 501; /* not implemented */ + + return HANDLER_FINISHED; + } + + offset = strtoll(num, &err, 10); + + if (*err != '-' || offset < 0) { + con->http_status = 501; /* not implemented */ + + return HANDLER_FINISHED; + } + + if (-1 == (fd = open(con->physical.path->ptr, O_WRONLY, WEBDAV_FILE_MODE))) { + switch (errno) { + case ENOENT: + con->http_status = 404; /* not found */ + break; + default: + con->http_status = 403; /* not found */ + break; + } + return HANDLER_FINISHED; + } + + if (-1 == lseek(fd, offset, SEEK_SET)) { + con->http_status = 501; /* not implemented */ + + close(fd); + + return HANDLER_FINISHED; + } + con->http_status = 200; /* modified */ + } else { + /* take what we have in the request-body and write it to a file */ + + /* if the file doesn't exist, create it */ + if (-1 == (fd = open(con->physical.path->ptr, O_WRONLY|O_TRUNC, WEBDAV_FILE_MODE))) { + if (errno != ENOENT || + -1 == (fd = open(con->physical.path->ptr, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, WEBDAV_FILE_MODE))) { + /* we can't open the file */ + con->http_status = 403; + + return HANDLER_FINISHED; + } else { + con->http_status = 201; /* created */ + } + } else { + con->http_status = 200; /* modified */ + } + } + + con->file_finished = 1; + + for (c = cq->first; c; c = cq->first) { + int r = 0; + int mapped; + void *data; + size_t dlen; + + /* copy all chunks */ + switch(c->type) { + case FILE_CHUNK: + + mapped = (c->file.mmap.start != MAP_FAILED); + dlen = c->file.length - c->offset; + if (mapped) { + data = c->file.mmap.start + c->offset; + } else { + if (-1 == c->file.fd && /* open the file if not already open */ + -1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, O_RDONLY, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); + close(fd); + return HANDLER_ERROR; + } + + if (MAP_FAILED != (c->file.mmap.start = mmap(NULL, c->file.length, PROT_READ, MAP_PRIVATE, c->file.fd, 0))) { + /* chunk_reset() or chunk_free() will cleanup for us */ + c->file.mmap.length = c->file.length; + data = c->file.mmap.start + c->offset; + mapped = 1; + } else { + ssize_t rd; + if (dlen > 65536) dlen = 65536; + data = malloc(dlen); + force_assert(data); + if (-1 == lseek(c->file.fd, c->file.start + c->offset, SEEK_SET) + || 0 > (rd = read(c->file.fd, data, dlen))) { + log_error_write(srv, __FILE__, __LINE__, "ssbd", "lseek/read failed: ", + strerror(errno), c->mem, c->file.fd); + free(data); + close(fd); + return HANDLER_ERROR; + } + dlen = (size_t)rd; + } + + } + + if ((r = write(fd, data, dlen)) < 0) { + switch(errno) { + case ENOSPC: + con->http_status = 507; + + break; + default: + con->http_status = 403; + break; + } + } + + if (!mapped) free(data); + break; + case MEM_CHUNK: + if ((r = write(fd, c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) { + switch(errno) { + case ENOSPC: + con->http_status = 507; + + break; + default: + con->http_status = 403; + break; + } + } + break; + } + + if (r > 0) { + chunkqueue_mark_written(cq, r); + } else { + break; + } + } + if (0 != close(fd)) { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "close ", con->physical.path, "failed: ", strerror(errno)); + return HANDLER_ERROR; + } + + return HANDLER_FINISHED; +} + +static handler_t mod_webdav_copymove(server *srv, connection *con, plugin_data *p, handler_ctx *hctx) { + buffer *b; + struct stat st; + buffer *destination = NULL; + char *sep, *sep2, *start; + int overwrite = 1; + + if (p->conf.is_readonly) { + con->http_status = 403; + return HANDLER_FINISHED; + } + + /* is a exclusive lock set on the source */ + if (con->request.http_method == HTTP_METHOD_MOVE) { + if (!webdav_has_lock(srv, con, hctx, con->uri.path)) { + con->http_status = 423; + return HANDLER_FINISHED; + } + } + + if (NULL == (destination = http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Destination")))) { + con->http_status = 400; + return HANDLER_FINISHED; + } + + if (NULL != (b = http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Overwrite")))) { + if (buffer_string_length(b) != 1 || + (b->ptr[0] != 'F' && + b->ptr[0] != 'T') ) { + con->http_status = 400; + return HANDLER_FINISHED; + } + overwrite = (b->ptr[0] == 'F' ? 0 : 1); + } + /* let's parse the Destination + * + * http://127.0.0.1:1025/dav/litmus/copydest + * + * - host has to be the same as the Host: header we got + * - we have to stay inside the document root + * - the query string is thrown away + * */ + + start = destination->ptr; + sep = start + buffer_string_length(con->uri.scheme); + + if (0 != strncmp(start, con->uri.scheme->ptr, sep - start) + || sep[0] != ':' || sep[1] != '/' || sep[2] != '/') { + con->http_status = 400; + return HANDLER_FINISHED; + } + buffer_copy_buffer(p->uri.scheme, con->uri.scheme); /*(unused?)*/ + + start = sep + 3; + + if (NULL == (sep = strchr(start, '/'))) { + con->http_status = 400; + return HANDLER_FINISHED; + } + if (NULL != (sep2 = memchr(start, '@', sep - start))) { + /* skip login information */ + start = sep2 + 1; + } + buffer_copy_string_len(p->uri.authority, start, sep - start); + + start = sep + 1; + + if (NULL == (sep = strchr(start, '?'))) { + /* no query string, good */ + buffer_copy_string(p->uri.path, start); + } else { + buffer_copy_string_len(p->uri.path, start, sep - start); + } + + if (!buffer_is_equal(p->uri.authority, con->uri.authority)) { + /* not the same host */ + con->http_status = 502; + return HANDLER_FINISHED; + } + + buffer_urldecode_path(p->uri.path); + if (!buffer_is_valid_UTF8(p->uri.path)) { + /* invalid UTF-8 after url-decode */ + con->http_status = 400; + return HANDLER_FINISHED; + } + buffer_path_simplify(p->uri.path, p->uri.path); + + if (buffer_string_is_empty(p->uri.path) || p->uri.path->ptr[0] != '/') { + con->http_status = 400; + return HANDLER_FINISHED; + } + + /* we now have a URI which is clean. transform it into a physical path */ + buffer_copy_buffer(p->physical.doc_root, con->physical.doc_root); + buffer_copy_buffer(p->physical.rel_path, p->uri.path); + + if (con->conf.force_lowercase_filenames) { + buffer_to_lower(p->physical.rel_path); + } + + /* Destination physical path + * src con->physical.path might have been remapped with mod_alias. + * (but mod_alias does not modify con->physical.rel_path) + * Find matching prefix to support use of mod_alias to remap webdav root. + * Aliasing of paths underneath the webdav root might not work. + * Likewise, mod_rewrite URL rewriting might thwart this comparison. + * Use mod_redirect instead of mod_alias to remap paths *under* webdav root. + * Use mod_redirect instead of mod_rewrite on *any* parts of path to webdav. + * (Related, use mod_auth to protect webdav root, but avoid attempting to + * use mod_auth on paths underneath webdav root, as Destination is not + * validated with mod_auth) + * + * tl;dr: webdav paths and webdav properties are managed by mod_webdav, + * so do not modify paths externally or else undefined behavior + * or corruption may occur + */ + { + /* find matching URI prefix + * check if remaining con->physical.rel_path matches suffix + * of con->physical.basedir so that we can use it to + * remap Destination physical path */ + size_t i, remain; + sep = con->uri.path->ptr; + sep2 = p->uri.path->ptr; + for (i = 0; sep[i] && sep[i] == sep2[i]; ++i) ; + if (sep[i] == '\0' && (sep2[i] == '\0' || sep2[i] == '/' || (i > 0 && sep[i-1] == '/'))) { + /* src and dst URI match or dst is nested inside src; invalid COPY or MOVE */ + con->http_status = 403; + return HANDLER_FINISHED; + } + while (i != 0 && sep[--i] != '/') ; /* find matching directory path */ + remain = buffer_string_length(con->uri.path) - i; + if (!con->conf.force_lowercase_filenames + ? buffer_is_equal_right_len(con->physical.path, con->physical.rel_path, remain) + :(buffer_string_length(con->physical.path) >= remain + && 0 == strncasecmp(con->physical.path->ptr+buffer_string_length(con->physical.path)-remain, con->physical.rel_path->ptr+i, remain))) { + /* (at this point, p->physical.rel_path is identical to (or lowercased version of) p->uri.path) */ + buffer_copy_string_len(p->physical.path, con->physical.path->ptr, buffer_string_length(con->physical.path)-remain); + buffer_append_string_len(p->physical.path, p->physical.rel_path->ptr+i, buffer_string_length(p->physical.rel_path)-i); + + buffer_copy_buffer(p->physical.basedir, con->physical.basedir); + buffer_append_slash(p->physical.basedir); + } else { + /* unable to perform physical path remap here; + * assume doc_root/rel_path and no remapping */ + buffer_copy_buffer(p->physical.path, p->physical.doc_root); + buffer_append_slash(p->physical.path); + buffer_copy_buffer(p->physical.basedir, p->physical.path); + buffer_append_path_len(p->physical.path, CONST_BUF_LEN(p->physical.rel_path)); + } + } + + /* let's see if the source is a directory + * if yes, we fail with 501 */ + + if (-1 == stat(con->physical.path->ptr, &st)) { + /* don't about it yet, unlink will fail too */ + switch(errno) { + case ENOENT: + con->http_status = 404; + break; + default: + con->http_status = 403; + break; + } + } else if (S_ISDIR(st.st_mode)) { + int r; + int created = 0; + /* src is a directory */ + + if (con->physical.path->ptr[buffer_string_length(con->physical.path)-1] != '/') { + http_response_redirect_to_directory(srv, con); + return HANDLER_FINISHED; + } + + if (-1 == stat(p->physical.path->ptr, &st)) { + if (-1 == mkdir(p->physical.path->ptr, WEBDAV_DIR_MODE)) { + con->http_status = 403; + return HANDLER_FINISHED; + } + created = 1; + } else if (!S_ISDIR(st.st_mode)) { + if (overwrite == 0) { + /* copying into a non-dir ? */ + con->http_status = 409; + return HANDLER_FINISHED; + } else { + unlink(p->physical.path->ptr); + if (-1 == mkdir(p->physical.path->ptr, WEBDAV_DIR_MODE)) { + con->http_status = 403; + return HANDLER_FINISHED; + } + created = 1; + } + } + + /* copy the content of src to dest */ + if (0 != (r = webdav_copy_dir(srv, con, hctx, &(con->physical), &(p->physical), overwrite))) { + con->http_status = r; + return HANDLER_FINISHED; + } + if (con->request.http_method == HTTP_METHOD_MOVE) { + b = buffer_init(); + webdav_delete_dir(srv, con, hctx, &(con->physical), b); /* content */ + buffer_free(b); + + rmdir(con->physical.path->ptr); + } + con->http_status = created ? 201 : 204; + con->file_finished = 1; + } else { + /* it is just a file, good */ + int r; + int destdir = 0; + + /* does the client have a lock for this connection ? */ + if (!webdav_has_lock(srv, con, hctx, p->uri.path)) { + con->http_status = 423; + return HANDLER_FINISHED; + } + + /* destination exists */ + if (0 == (r = stat(p->physical.path->ptr, &st))) { + if (S_ISDIR(st.st_mode)) { + /* file to dir/ + * append basename to physical path */ + destdir = 1; + + if (NULL != (sep = strrchr(con->physical.path->ptr, '/'))) { + buffer_append_string(p->physical.path, sep); + r = stat(p->physical.path->ptr, &st); + } + } + } + + if (-1 == r) { + con->http_status = destdir ? 204 : 201; /* we will create a new one */ + con->file_finished = 1; + + switch(errno) { + case ENOTDIR: + con->http_status = 409; + return HANDLER_FINISHED; + } + } else if (overwrite == 0) { + /* destination exists, but overwrite is not set */ + con->http_status = 412; + return HANDLER_FINISHED; + } else { + con->http_status = 204; /* resource already existed */ + } + + if (con->request.http_method == HTTP_METHOD_MOVE) { + /* try a rename */ + + if (0 == rename(con->physical.path->ptr, p->physical.path->ptr)) { +#ifdef USE_PROPPATCH + sqlite3_stmt *stmt; + + stmt = p->conf.stmt_move_uri; + if (stmt) { + + sqlite3_reset(stmt); + + /* bind the values to the insert */ + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(p->uri.path), + SQLITE_TRANSIENT); + + sqlite3_bind_text(stmt, 2, + CONST_BUF_LEN(con->uri.path), + SQLITE_TRANSIENT); + + if (SQLITE_DONE != sqlite3_step(stmt)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "sql-move failed:", sqlite3_errmsg(p->conf.sql)); + } + } +#endif + return HANDLER_FINISHED; + } + + /* rename failed, fall back to COPY + DELETE */ + } + + if (0 != (r = webdav_copy_file(srv, con, hctx, &(con->physical), &(p->physical), overwrite))) { + con->http_status = r; + + return HANDLER_FINISHED; + } + + if (con->request.http_method == HTTP_METHOD_MOVE) { + b = buffer_init(); + webdav_delete_file(srv, con, hctx, &(con->physical), b); + buffer_free(b); + } + } + + return HANDLER_FINISHED; +} + +static handler_t mod_webdav_proppatch(server *srv, connection *con, plugin_data *p, handler_ctx *hctx) { + struct stat st; + if (p->conf.is_readonly) { + con->http_status = 403; + return HANDLER_FINISHED; + } + + if (!webdav_has_lock(srv, con, hctx, con->uri.path)) { + con->http_status = 423; + return HANDLER_FINISHED; + } + + /* check if destination exists */ + if (-1 == stat(con->physical.path->ptr, &st)) { + switch(errno) { + case ENOENT: + con->http_status = 404; + break; + default: + con->http_status = 403; + break; + } + return HANDLER_FINISHED; + } + + if (S_ISDIR(st.st_mode) && con->physical.path->ptr[buffer_string_length(con->physical.path)-1] != '/') { + http_response_redirect_to_directory(srv, con); + return HANDLER_FINISHED; + } + +#ifdef USE_PROPPATCH + if (con->request.content_length) { + xmlDocPtr xml; + + if (con->state == CON_STATE_READ_POST) { + handler_t r = connection_handle_read_post_state(srv, con); + if (r != HANDLER_GO_ON) return r; + } + + if (1 == webdav_parse_chunkqueue(srv, con, hctx, con->request_content_queue, &xml)) { + xmlNode *rootnode = xmlDocGetRootElement(xml); + + if (0 == xmlStrcmp(rootnode->name, BAD_CAST "propertyupdate")) { + xmlNode *cmd; + char *err = NULL; + int empty_ns = 0; /* send 400 on a empty namespace attribute */ + + /* start response */ + + if (SQLITE_OK != sqlite3_exec(p->conf.sql, "BEGIN TRANSACTION", NULL, NULL, &err)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "can't open transaction:", err); + sqlite3_free(err); + + goto propmatch_cleanup; + } + + /* a UPDATE request, we know 'set' and 'remove' */ + for (cmd = rootnode->children; cmd; cmd = cmd->next) { + xmlNode *props; + /* either set or remove */ + + if ((0 == xmlStrcmp(cmd->name, BAD_CAST "set")) || + (0 == xmlStrcmp(cmd->name, BAD_CAST "remove"))) { + + sqlite3_stmt *stmt; + + stmt = (0 == xmlStrcmp(cmd->name, BAD_CAST "remove")) ? + p->conf.stmt_delete_prop : p->conf.stmt_update_prop; + + for (props = cmd->children; props; props = props->next) { + if (0 == xmlStrcmp(props->name, BAD_CAST "prop")) { + xmlNode *prop; + char *propval = NULL; + int r; + + prop = props->children; + + if (prop->ns && + (0 == xmlStrcmp(prop->ns->href, BAD_CAST "")) && + (0 != xmlStrcmp(prop->ns->prefix, BAD_CAST ""))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "no name space for:", + prop->name); + + empty_ns = 1; + break; + } + + sqlite3_reset(stmt); + + /* bind the values to the insert */ + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(con->uri.path), + SQLITE_TRANSIENT); + sqlite3_bind_text(stmt, 2, + (char *)prop->name, + strlen((char *)prop->name), + SQLITE_TRANSIENT); + if (prop->ns) { + sqlite3_bind_text(stmt, 3, + (char *)prop->ns->href, + strlen((char *)prop->ns->href), + SQLITE_TRANSIENT); + } else { + sqlite3_bind_text(stmt, 3, + "", + 0, + SQLITE_TRANSIENT); + } + if (stmt == p->conf.stmt_update_prop) { + propval = prop->children + ? (char *)xmlNodeListGetString(xml, prop->children, 0) + : NULL; + + sqlite3_bind_text(stmt, 4, + propval ? propval : "", + propval ? strlen(propval) : 0, + SQLITE_TRANSIENT); + } + + if (SQLITE_DONE != (r = sqlite3_step(stmt))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "sql-set failed:", sqlite3_errmsg(p->conf.sql)); + } + + if (propval) xmlFree(propval); + } + } + if (empty_ns) break; + } + } + + if (empty_ns) { + if (SQLITE_OK != sqlite3_exec(p->conf.sql, "ROLLBACK", NULL, NULL, &err)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "can't rollback transaction:", err); + sqlite3_free(err); + + goto propmatch_cleanup; + } + + con->http_status = 400; + } else { + if (SQLITE_OK != sqlite3_exec(p->conf.sql, "COMMIT", NULL, NULL, &err)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "can't commit transaction:", err); + sqlite3_free(err); + + goto propmatch_cleanup; + } + con->http_status = 200; + } + con->file_finished = 1; + + xmlFreeDoc(xml); + return HANDLER_FINISHED; + } + +propmatch_cleanup: + + xmlFreeDoc(xml); + } else { + con->http_status = 400; + return HANDLER_FINISHED; + } + } +#endif + con->http_status = 501; + return HANDLER_FINISHED; +} + +#ifdef USE_LOCKS +static handler_t mod_webdav_lock(server *srv, connection *con, plugin_data *p, handler_ctx *hctx) { + /** + * a mac wants to write + * + * LOCK /dav/expire.txt HTTP/1.1\r\n + * User-Agent: WebDAVFS/1.3 (01308000) Darwin/8.1.0 (Power Macintosh)\r\n + * Accept: * / *\r\n + * Depth: 0\r\n + * Timeout: Second-600\r\n + * Content-Type: text/xml; charset=\"utf-8\"\r\n + * Content-Length: 229\r\n + * Connection: keep-alive\r\n + * Host: 192.168.178.23:1025\r\n + * \r\n + * <?xml version=\"1.0\" encoding=\"utf-8\"?>\n + * <D:lockinfo xmlns:D=\"DAV:\">\n + * <D:lockscope><D:exclusive/></D:lockscope>\n + * <D:locktype><D:write/></D:locktype>\n + * <D:owner>\n + * <D:href>http://www.apple.com/webdav_fs/</D:href>\n + * </D:owner>\n + * </D:lockinfo>\n + */ + + int depth = mod_webdav_depth(con); + if (depth != 0 && depth != -1) { + con->http_status = 400; + + return HANDLER_FINISHED; + } + + if (con->request.content_length) { + xmlDocPtr xml; + buffer *hdr_if = NULL; + int created = 0; + struct stat st; + + if (con->state == CON_STATE_READ_POST) { + handler_t r = connection_handle_read_post_state(srv, con); + if (r != HANDLER_GO_ON) return r; + } + + hdr_if = http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("If")); + + if (0 != stat(con->physical.path->ptr, &st)) { + if (errno == ENOENT) { + int fd = open(con->physical.path->ptr, O_WRONLY|O_CREAT|O_APPEND|O_BINARY|FIFO_NONBLOCK, WEBDAV_FILE_MODE); + if (fd >= 0) { + close(fd); + created = 1; + } else { + log_error_write(srv, __FILE__, __LINE__, "sBss", + "create file", con->physical.path, ":", strerror(errno)); + con->http_status = 403; /* Forbidden */ + + return HANDLER_FINISHED; + } + } + else { + log_error_write(srv, __FILE__, __LINE__, "sBss", + "stat", con->physical.path, ":", strerror(errno)); + con->http_status = 403; /* Forbidden */ + return HANDLER_FINISHED; + } + } else if (hdr_if == NULL && depth == -1) { + /* we don't support Depth: Infinity on directories */ + if (S_ISDIR(st.st_mode)) { + con->http_status = 409; /* Conflict */ + + return HANDLER_FINISHED; + } + } + + if (1 == webdav_parse_chunkqueue(srv, con, hctx, con->request_content_queue, &xml)) { + xmlNode *rootnode = xmlDocGetRootElement(xml); + + force_assert(rootnode); + + if (0 == xmlStrcmp(rootnode->name, BAD_CAST "lockinfo")) { + xmlNode *lockinfo; + const xmlChar *lockscope = NULL, *locktype = NULL; /* TODO: compiler says unused: *owner = NULL; */ + + for (lockinfo = rootnode->children; lockinfo; lockinfo = lockinfo->next) { + if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "lockscope")) { + xmlNode *value; + for (value = lockinfo->children; value; value = value->next) { + if ((0 == xmlStrcmp(value->name, BAD_CAST "exclusive")) || + (0 == xmlStrcmp(value->name, BAD_CAST "shared"))) { + lockscope = value->name; + } else { + con->http_status = 400; + + xmlFreeDoc(xml); + return HANDLER_FINISHED; + } + } + } else if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "locktype")) { + xmlNode *value; + for (value = lockinfo->children; value; value = value->next) { + if ((0 == xmlStrcmp(value->name, BAD_CAST "write"))) { + locktype = value->name; + } else { + con->http_status = 400; + + xmlFreeDoc(xml); + return HANDLER_FINISHED; + } + } + + } else if (0 == xmlStrcmp(lockinfo->name, BAD_CAST "owner")) { + } + } + + if (lockscope && locktype) { + sqlite3_stmt *stmt = p->conf.stmt_read_lock_by_uri; + + /* is this resourse already locked ? */ + + /* SELECT locktoken, resource, lockscope, locktype, owner, depth, timeout + * FROM locks + * WHERE resource = ? */ + + if (stmt) { + + sqlite3_reset(stmt); + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(con->uri.path), + SQLITE_TRANSIENT); + + /* it is the PK */ + while (SQLITE_ROW == sqlite3_step(stmt)) { + /* we found a lock + * 1. is it compatible ? + * 2. is it ours */ + char *sql_lockscope = (char *)sqlite3_column_text(stmt, 2); + + if (strcmp(sql_lockscope, "exclusive")) { + con->http_status = 423; + } else if (0 == xmlStrcmp(lockscope, BAD_CAST "exclusive")) { + /* resourse is locked with a shared lock + * client wants exclusive */ + con->http_status = 423; + } + } + if (con->http_status == 423) { + xmlFreeDoc(xml); + return HANDLER_FINISHED; + } + } + + stmt = p->conf.stmt_create_lock; + if (stmt) { + /* create a lock-token */ + uuid_t id; + char uuid[37] /* 36 + \0 */; + + uuid_generate(id); + uuid_unparse(id, uuid); + + buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("opaquelocktoken:")); + buffer_append_string(p->tmp_buf, uuid); + + /* "CREATE TABLE locks (" + * " locktoken TEXT NOT NULL," + * " resource TEXT NOT NULL," + * " lockscope TEXT NOT NULL," + * " locktype TEXT NOT NULL," + * " owner TEXT NOT NULL," + * " depth INT NOT NULL," + */ + + sqlite3_reset(stmt); + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(p->tmp_buf), + SQLITE_TRANSIENT); + + sqlite3_bind_text(stmt, 2, + CONST_BUF_LEN(con->uri.path), + SQLITE_TRANSIENT); + + sqlite3_bind_text(stmt, 3, + (const char *)lockscope, + xmlStrlen(lockscope), + SQLITE_TRANSIENT); + + sqlite3_bind_text(stmt, 4, + (const char *)locktype, + xmlStrlen(locktype), + SQLITE_TRANSIENT); + + /* owner */ + sqlite3_bind_text(stmt, 5, + "", + 0, + SQLITE_TRANSIENT); + + /* depth */ + sqlite3_bind_int(stmt, 6, + depth); + + + if (SQLITE_DONE != sqlite3_step(stmt)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "create lock:", sqlite3_errmsg(p->conf.sql)); + } + + /* looks like we survived */ + webdav_lockdiscovery(con, p->tmp_buf, (const char *)lockscope, (const char *)locktype, depth); + + con->http_status = created ? 201 : 200; + con->file_finished = 1; + } + } + } + + xmlFreeDoc(xml); + return HANDLER_FINISHED; + } else { + con->http_status = 400; + return HANDLER_FINISHED; + } + } else { + buffer *b; + if (NULL != (b = http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("If")))) { + buffer *locktoken = b; + sqlite3_stmt *stmt = p->conf.stmt_refresh_lock; + + /* remove the < > around the token */ + if (buffer_string_length(locktoken) < 5) { + con->http_status = 400; + + return HANDLER_FINISHED; + } + + buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 2, buffer_string_length(locktoken) - 4); + + sqlite3_reset(stmt); + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(p->tmp_buf), + SQLITE_TRANSIENT); + + if (SQLITE_DONE != sqlite3_step(stmt)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "refresh lock:", sqlite3_errmsg(p->conf.sql)); + } + + webdav_lockdiscovery(con, p->tmp_buf, "exclusive", "write", 0); + + con->http_status = 200; + con->file_finished = 1; + return HANDLER_FINISHED; + } else { + /* we need a lock-token to refresh */ + con->http_status = 400; + + return HANDLER_FINISHED; + } + } +} +#endif + +#ifdef USE_LOCKS +static handler_t mod_webdav_unlock(server *srv, connection *con, plugin_data *p) { + buffer *b; + if (NULL != (b = http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Lock-Token")))) { + buffer *locktoken = b; + sqlite3_stmt *stmt = p->conf.stmt_remove_lock; + + /* remove the < > around the token */ + if (buffer_string_length(locktoken) < 3) { + con->http_status = 400; + + return HANDLER_FINISHED; + } + + /** + * FIXME: + * + * if the resourse is locked: + * - by us: unlock + * - by someone else: 401 + * if the resource is not locked: + * - 412 + * */ + + buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 1, buffer_string_length(locktoken) - 2); + + sqlite3_reset(stmt); + + sqlite3_bind_text(stmt, 1, + CONST_BUF_LEN(p->tmp_buf), + SQLITE_TRANSIENT); + + if (SQLITE_DONE != sqlite3_step(stmt)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "remove lock:", sqlite3_errmsg(p->conf.sql)); + } + + if (0 == sqlite3_changes(p->conf.sql)) { + con->http_status = 401; + } else { + con->http_status = 204; + } + return HANDLER_FINISHED; + } else { + /* we need a lock-token to unlock */ + con->http_status = 400; + + return HANDLER_FINISHED; + } +} +#endif + +SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) { + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + + if (NULL == hctx) return HANDLER_GO_ON; + if (!hctx->conf.enabled) return HANDLER_GO_ON; + /* physical path is setup */ + if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; + + switch (con->request.http_method) { + case HTTP_METHOD_PROPFIND: + return mod_webdav_propfind(srv, con, p, hctx); + case HTTP_METHOD_MKCOL: + return mod_webdav_mkcol(con, p); + case HTTP_METHOD_DELETE: + return mod_webdav_delete(srv, con, p, hctx); + case HTTP_METHOD_PUT: + return mod_webdav_put(srv, con, p, hctx); + case HTTP_METHOD_MOVE: + case HTTP_METHOD_COPY: + return mod_webdav_copymove(srv, con, p, hctx); + case HTTP_METHOD_PROPPATCH: + return mod_webdav_proppatch(srv, con, p, hctx); + #ifdef USE_LOCKS + case HTTP_METHOD_LOCK: + return mod_webdav_lock(srv, con, p, hctx); + case HTTP_METHOD_UNLOCK: + return mod_webdav_unlock(srv, con, p); + #else + case HTTP_METHOD_LOCK: + case HTTP_METHOD_UNLOCK: + con->http_status = 501; + return HANDLER_FINISHED; + #endif + default: + return HANDLER_GO_ON; /* not found */ + } +} + + +SUBREQUEST_FUNC(mod_webdav_subrequest_handler) { + handler_t r; + plugin_data *p = p_d; + if (con->mode != p->id) return HANDLER_GO_ON; + + r = mod_webdav_subrequest_handler_huge(srv, con, p_d); + if (con->http_status >= 400) con->mode = DIRECT; + return r; +} + + +PHYSICALPATH_FUNC(mod_webdav_physical_handler) { + plugin_data *p = p_d; + if (!p->conf.enabled) return HANDLER_GO_ON; + + /* physical path is setup */ + if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; + + UNUSED(srv); + + switch (con->request.http_method) { + case HTTP_METHOD_PROPFIND: + case HTTP_METHOD_PROPPATCH: + case HTTP_METHOD_PUT: + case HTTP_METHOD_COPY: + case HTTP_METHOD_MOVE: + case HTTP_METHOD_MKCOL: + case HTTP_METHOD_DELETE: + case HTTP_METHOD_LOCK: + case HTTP_METHOD_UNLOCK: { + handler_ctx *hctx = calloc(1, sizeof(*hctx)); + memcpy(&hctx->conf, &p->conf, sizeof(plugin_config)); + con->plugin_ctx[p->id] = hctx; + con->conf.stream_request_body = 0; + con->mode = p->id; + break; + } + default: + break; + } + + return HANDLER_GO_ON; +} + +static handler_t mod_webdav_connection_reset(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (hctx) { + free(hctx); + con->plugin_ctx[p->id] = NULL; + } + + UNUSED(srv); + return HANDLER_GO_ON; +} + + +/* this function is called at dlopen() time and inits the callbacks */ + +int mod_webdav_plugin_init(plugin *p); +int mod_webdav_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("webdav"); + + p->init = mod_webdav_init; + p->handle_uri_clean = mod_webdav_uri_handler; + p->handle_physical = mod_webdav_physical_handler; + p->handle_subrequest = mod_webdav_subrequest_handler; + p->connection_reset = mod_webdav_connection_reset; + p->set_defaults = mod_webdav_set_defaults; + p->cleanup = mod_webdav_free; + + p->data = NULL; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_wstunnel.c b/data/lighttpd/lighttpd-1.4.53/src/mod_wstunnel.c new file mode 100644 index 000000000..6843ace56 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_wstunnel.c @@ -0,0 +1,1363 @@ +/* + * mod_wstunnel originally based off https://github.com/nori0428/mod_websocket + * Portions of this module Copyright(c) 2017, Glenn Strauss, All rights reserved + * Portions of this module Copyright(c) 2010, Norio Kobota, All rights reserved. + */ + +/* + * Copyright(c) 2010, Norio Kobota, All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - 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. + * - Neither the name of the 'incremental' 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ + +/* NOTES: + * + * mod_wstunnel has been largely rewritten from Norio Kobota mod_websocket. + * + * highlighted differences from Norio Kobota mod_websocket + * - re-coded to use lighttpd 1.4.46 buffer, chunkqueue, and gw_backend APIs + * - websocket.server "ext" value is no longer regex; + * operates similar to mod_proxy for either path prefix or extension match + * - validation of "origins" value is no longer regex; operates as suffix match + * (admin could use lighttpd.conf regex on "Origin" or "Sec-WebSocket-Origin" + * and reject non-matches with mod_access if such regex validation required) + * - websocket transparent proxy mode removed; functionality is now in mod_proxy + * Backend server which responds to Connection: upgrade and Upgrade: websocket + * should check "Origin" and/or "Sec-WebSocket-Origin". lighttpd.conf could + * additionally be configured to check + * $REQUEST_HEADER["Sec-WebSocket-Origin"] !~ "..." + * with regex, and mod_access used to reject non-matches, if desired. + * - connections to backend no longer block, but only first address returned + * by getaddrinfo() is used; lighttpd does not cycle through all addresses + * returned by DNS resolution. Note: DNS resolution occurs once at startup. + * - directives renamed from websocket.* to wstunnel.* + * - directive websocket.ping_interval replaced with wstunnel.ping-interval + * (note the '_' changed to '-') + * - directive websocket.timeout should be replaced with server.max-read-idle + * - attribute "type" is an independent directive wstunnel.frame-type + * (default is "text" unless "binary" is specified) + * - attribute "origins" is an independent directive wstunnel.origins + * - attribute "proto" removed; mod_proxy can proxy to backend websocket server + * - attribute "subproto" should be replaced with mod_setenv directive + * setenv.set-response-header = ( "Sec-WebSocket-Protocol" => "..." ) + * if header is required + * + * not reviewed: + * - websocket protocol compliance has not been reviewed + * e.g. when to send 1000 Normal Closure and when to send 1001 Going Away + * - websocket protocol sanity checking has not been reviewed + * + * References: + * https://en.wikipedia.org/wiki/WebSocket + * https://tools.ietf.org/html/rfc6455 + * https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 + */ +#include "first.h" + +#include <sys/types.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include "gw_backend.h" + +#include "base.h" +#include "array.h" +#include "buffer.h" +#include "chunk.h" +#include "fdevent.h" +#include "http_header.h" +#include "joblist.h" +#include "log.h" + +#define MOD_WEBSOCKET_LOG_NONE 0 +#define MOD_WEBSOCKET_LOG_ERR 1 +#define MOD_WEBSOCKET_LOG_WARN 2 +#define MOD_WEBSOCKET_LOG_INFO 3 +#define MOD_WEBSOCKET_LOG_DEBUG 4 + +#define DEBUG_LOG(level, format, ...) \ + if (hctx->gw.conf.debug >= (level)) { \ + log_error_write(hctx->srv, __FILE__, __LINE__, (format), __VA_ARGS__); \ + } + +typedef struct { + gw_plugin_config gw; + buffer *frame_type; + array *origins; + unsigned int ping_interval; +} plugin_config; + +typedef struct plugin_data { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +typedef enum { + MOD_WEBSOCKET_FRAME_STATE_INIT, + + /* _MOD_WEBSOCKET_SPEC_RFC_6455_ */ + MOD_WEBSOCKET_FRAME_STATE_READ_LENGTH, + MOD_WEBSOCKET_FRAME_STATE_READ_EX_LENGTH, + MOD_WEBSOCKET_FRAME_STATE_READ_MASK, + /* _MOD_WEBSOCKET_SPEC_RFC_6455_ */ + + MOD_WEBSOCKET_FRAME_STATE_READ_PAYLOAD +} mod_wstunnel_frame_state_t; + +typedef enum { + MOD_WEBSOCKET_FRAME_TYPE_TEXT, + MOD_WEBSOCKET_FRAME_TYPE_BIN, + MOD_WEBSOCKET_FRAME_TYPE_CLOSE, + + /* _MOD_WEBSOCKET_SPEC_RFC_6455_ */ + MOD_WEBSOCKET_FRAME_TYPE_PING, + MOD_WEBSOCKET_FRAME_TYPE_PONG + /* _MOD_WEBSOCKET_SPEC_RFC_6455_ */ + +} mod_wstunnel_frame_type_t; + +typedef struct { + uint64_t siz; + + /* _MOD_WEBSOCKET_SPEC_RFC_6455_ */ + int siz_cnt; + int mask_cnt; + #define MOD_WEBSOCKET_MASK_CNT 4 + unsigned char mask[MOD_WEBSOCKET_MASK_CNT]; + /* _MOD_WEBSOCKET_SPEC_RFC_6455_ */ + +} mod_wstunnel_frame_control_t; + +typedef struct { + mod_wstunnel_frame_state_t state; + mod_wstunnel_frame_control_t ctl; + mod_wstunnel_frame_type_t type, type_before, type_backend; + buffer *payload; +} mod_wstunnel_frame_t; + +typedef struct { + gw_handler_ctx gw; + mod_wstunnel_frame_t frame; + + int hybivers; + time_t ping_ts; + int subproto; + + server *srv; /*(for mod_wstunnel module-specific DEBUG_LOG() macro)*/ + plugin_config conf; +} handler_ctx; + +/* prototypes */ +static handler_t mod_wstunnel_handshake_create_response(handler_ctx *); +static int mod_wstunnel_frame_send(handler_ctx *, mod_wstunnel_frame_type_t, const char *, size_t); +static int mod_wstunnel_frame_recv(handler_ctx *); +#define _MOD_WEBSOCKET_SPEC_IETF_00_ +#define _MOD_WEBSOCKET_SPEC_RFC_6455_ + +INIT_FUNC(mod_wstunnel_init) { + return calloc(1, sizeof(plugin_data)); +} + +FREE_FUNC(mod_wstunnel_free) { + plugin_data *p = p_d; + if (p->config_storage) { + for (size_t i = 0; i < srv->config_context->used; ++i) { + plugin_config *s = p->config_storage[i]; + if (NULL == s) continue; + buffer_free(s->frame_type); + array_free(s->origins); + /*assert(0 == offsetof(s->gw));*/ + gw_plugin_config_free(&s->gw); + /*free(s);*//*free'd by gw_plugin_config_free()*/ + } + free(p->config_storage); + } + free(p); + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_wstunnel_set_defaults) { + plugin_data *p = p_d; + data_unset *du; + config_values_t cv[] = { + { "wstunnel.server", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, + { "wstunnel.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, + { "wstunnel.balance", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, + { "wstunnel.map-extensions",NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { "wstunnel.frame-type", NULL, T_CONFIG_STRING,T_CONFIG_SCOPE_CONNECTION }, + { "wstunnel.origins", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, + { "wstunnel.ping-interval", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + force_assert(p->config_storage); + for (size_t i = 0; i < srv->config_context->used; ++i) { + array *ca = ((data_config *)(srv->config_context->data[i]))->value; + plugin_config *s = calloc(1, sizeof(plugin_config)); + force_assert(s); + + s->gw.debug = 0; /* MOD_WEBSOCKET_LOG_NONE */ + s->gw.ext_mapping = array_init(); + s->frame_type = buffer_init(); + s->origins = array_init(); + s->ping_interval = 0; /* do not send ping */ + + cv[0].destination = NULL; /* T_CONFIG_LOCAL */ + cv[1].destination = &(s->gw.debug); + cv[2].destination = NULL; /* T_CONFIG_LOCAL */ + cv[3].destination = s->gw.ext_mapping; + cv[4].destination = s->frame_type; + cv[5].destination = s->origins; + cv[6].destination = &(s->ping_interval); + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, ca, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + + du = array_get_element(ca, "wstunnel.server"); + if (!gw_set_defaults_backend(srv, (gw_plugin_data *)p, du, i, 0)) { + return HANDLER_ERROR; + } + + du = array_get_element(ca, "wstunnel.balance"); + if (!gw_set_defaults_balance(srv, &s->gw, du)) { + return HANDLER_ERROR; + } + + /* disable check-local for all exts (default enabled) */ + if (s->gw.exts) { /*(check after gw_set_defaults_backend())*/ + for (size_t j = 0; j < s->gw.exts->used; ++j) { + gw_extension *ex = s->gw.exts->exts[j]; + for (size_t n = 0; n < ex->used; ++n) { + ex->hosts[n]->check_local = 0; + } + } + } + + /* error if "mode" = "authorizer"; wstunnel can not act as authorizer */ + /*(check after gw_set_defaults_backend())*/ + if (s->gw.exts_auth && s->gw.exts_auth->used) { + log_error_write(srv, __FILE__, __LINE__, "s", + "wstunnel.server must not define any hosts " + "with attribute \"mode\" = \"authorizer\""); + return HANDLER_ERROR; + } + + /*(default frame-type to "text" unless "binary" is specified)*/ + if (!buffer_is_empty(s->frame_type) + && !buffer_is_equal_caseless_string(s->frame_type, + CONST_STR_LEN("binary"))) { + buffer_clear(s->frame_type); + } + + if (!array_is_vlist(s->origins)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected value for wstunnel.origins; expected wstunnel.origins = ( \"...\", \"...\" )"); + return HANDLER_ERROR; + } + for (size_t j = 0; j < s->origins->used; ++j) { + if (buffer_string_is_empty(((data_string *)s->origins->data[j])->value)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "unexpected empty string in wstunnel.origins"); + return HANDLER_ERROR; + } + } + } + + /*assert(0 == offsetof(s->gw));*/ + return HANDLER_GO_ON; +} + +static handler_t wstunnel_create_env(server *srv, gw_handler_ctx *gwhctx) { + handler_ctx *hctx = (handler_ctx *)gwhctx; + connection *con = hctx->gw.remote_conn; + handler_t rc; + if (0 == con->request.content_length) { + http_response_upgrade_read_body_unknown(srv, con); + chunkqueue_append_chunkqueue(con->request_content_queue, + con->read_queue); + } + rc = mod_wstunnel_handshake_create_response(hctx); + if (rc != HANDLER_GO_ON) return rc; + + con->http_status = 101; /* Switching Protocols */ + con->file_started = 1; + + hctx->ping_ts = srv->cur_ts; + gw_set_transparent(srv, &hctx->gw); + + return HANDLER_GO_ON; +} + +static handler_t wstunnel_stdin_append(server *srv, gw_handler_ctx *gwhctx) { + /* prepare websocket frames to backend */ + /* (caller should verify con->request_content_queue) */ + /*assert(!chunkqueue_is_empty(con->request_content_queue));*/ + handler_ctx *hctx = (handler_ctx *)gwhctx; + if (0 == mod_wstunnel_frame_recv(hctx)) + return HANDLER_GO_ON; + else { + /*(error)*/ + /* future: might differentiate client close request from client error, + * and then send 1000 or 1001 */ + connection *con = hctx->gw.remote_conn; + DEBUG_LOG(MOD_WEBSOCKET_LOG_INFO, "sds", + "disconnected from client ( fd =", con->fd, ")"); + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sds", + "send close response to client ( fd =", con->fd, ")"); + mod_wstunnel_frame_send(hctx, MOD_WEBSOCKET_FRAME_TYPE_CLOSE, CONST_STR_LEN("1000")); /* 1000 Normal Closure */ + gw_connection_reset(srv, con, hctx->gw.plugin_data); + return HANDLER_FINISHED; + } +} + +static handler_t wstunnel_recv_parse(server *srv, connection *con, http_response_opts *opts, buffer *b, size_t n) { + handler_ctx *hctx = (handler_ctx *)opts->pdata; + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sdsx", + "recv data from backend ( fd =", hctx->gw.fd, "), size =", n); + if (0 == n) return HANDLER_FINISHED; + if (mod_wstunnel_frame_send(hctx,hctx->frame.type_backend,b->ptr,n) < 0) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", "fail to send data to client"); + return HANDLER_ERROR; + } + buffer_clear(b); + UNUSED(srv); + UNUSED(con); + return HANDLER_GO_ON; +} + +#define PATCH(x) p->conf.x = s->x +#define PATCH_GW(x) p->conf.gw.x = s->gw.x +static void mod_wstunnel_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH_GW(exts); + PATCH_GW(exts_auth); + PATCH_GW(exts_resp); + PATCH_GW(debug); + PATCH_GW(balance); + PATCH_GW(ext_mapping); + PATCH(frame_type); + PATCH(origins); + PATCH(ping_interval); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) { + continue; + } + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("wstunnel.server"))) { + PATCH_GW(exts); + /*(wstunnel can not act as authorizer, + * but p->conf.exts_auth must not be NULL)*/ + PATCH_GW(exts_auth); + PATCH_GW(exts_resp); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("wstunnel.debug"))) { + PATCH_GW(debug); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("wstunnel.balance"))) { + PATCH_GW(balance); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("wstunnel.map-extensions"))) { + PATCH_GW(ext_mapping); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("wstunnel.frame-type"))) { + PATCH(frame_type); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("wstunnel.origins"))) { + PATCH(origins); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("wstunnel.ping-interval"))) { + PATCH(ping_interval); + } + } + } +} +#undef PATCH_GW +#undef PATCH + +static int header_contains_token (buffer *b, const char *m, size_t mlen) +{ + for (char *s = b->ptr; s; s = strchr(s, ',')) { + while (*s == ' ' || *s == '\t' || *s == ',') ++s; + if (0 == strncasecmp(s, m, mlen)) { + s += mlen; + if (*s == '\0' || *s == ' ' || *s == '\t' || *s == ',' || *s == ';') + return 1; + } + } + return 0; +} + +static int wstunnel_is_allowed_origin(connection *con, handler_ctx *hctx) { + /* If allowed origins is set (and not empty list), fail closed if no match. + * Note that origin provided in request header has not been normalized, so + * change in case or other non-normal forms might not match allowed list */ + const array * const allowed_origins = hctx->conf.origins; + buffer *origin = NULL; + size_t olen; + + if (0 == allowed_origins->used) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_INFO, "s", "allowed origins not specified"); + return 1; + } + + /* "Origin" header is preferred + * ("Sec-WebSocket-Origin" is from older drafts of websocket spec) */ + origin = http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Origin")); + if (NULL == origin) { + origin = + http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Sec-WebSocket-Origin")); + } + olen = buffer_string_length(origin); + if (0 == olen) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", "Origin header is invalid"); + con->http_status = 400; /* Bad Request */ + return 0; + } + + for (size_t i = 0; i < allowed_origins->used; ++i) { + buffer *b = ((data_string *)allowed_origins->data[i])->value; + size_t blen = buffer_string_length(b); + if ((olen > blen ? origin->ptr[olen-blen-1] == '.' : olen == blen) + && buffer_is_equal_right_len(origin, b, blen)) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_INFO, "bsb", + origin, "matches allowed origin:", b); + return 1; + } + } + DEBUG_LOG(MOD_WEBSOCKET_LOG_INFO, "bs", + origin, "does not match any allowed origins"); + con->http_status = 403; /* Forbidden */ + return 0; +} + +static int wstunnel_check_request(connection *con, handler_ctx *hctx) { + const buffer * const vers = + http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Sec-WebSocket-Version")); + const long hybivers = (NULL != vers) ? strtol(vers->ptr, NULL, 10) : 0; + if (hybivers < 0 || hybivers > INT_MAX) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", "invalid Sec-WebSocket-Version"); + con->http_status = 400; /* Bad Request */ + return -1; + } + + /*(redundant since HTTP/1.1 required in mod_wstunnel_check_extension())*/ + if (buffer_is_empty(con->request.http_host)) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", "Host header does not exist"); + con->http_status = 400; /* Bad Request */ + return -1; + } + + if (!wstunnel_is_allowed_origin(con, hctx)) { + return -1; + } + + return (int)hybivers; +} + +static void wstunnel_backend_error(gw_handler_ctx *gwhctx) { + handler_ctx *hctx = (handler_ctx *)gwhctx; + if (hctx->gw.state == GW_STATE_WRITE || hctx->gw.state == GW_STATE_READ) { + mod_wstunnel_frame_send(hctx, MOD_WEBSOCKET_FRAME_TYPE_CLOSE, CONST_STR_LEN("1001")); /* 1001 Going Away */ + } +} + +static void wstunnel_handler_ctx_free(void *gwhctx) { + handler_ctx *hctx = (handler_ctx *)gwhctx; + chunk_buffer_release(hctx->frame.payload); +} + +static handler_t wstunnel_handler_setup (server *srv, connection *con, plugin_data *p) { + handler_ctx *hctx = con->plugin_ctx[p->id]; + int binary; + int hybivers; + hctx->srv = srv; /*(for mod_wstunnel module-specific DEBUG_LOG() macro)*/ + hctx->conf = p->conf; /*(copies struct)*/ + hybivers = wstunnel_check_request(con, hctx); + if (hybivers < 0) return HANDLER_FINISHED; + hctx->hybivers = hybivers; + if (0 == hybivers) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_INFO,"s","WebSocket Version = hybi-00"); + } + else { + DEBUG_LOG(MOD_WEBSOCKET_LOG_INFO,"sd","WebSocket Version =",hybivers); + } + + hctx->gw.opts.backend = BACKEND_PROXY; /*(act proxy-like; not used)*/ + hctx->gw.opts.pdata = hctx; + hctx->gw.opts.parse = wstunnel_recv_parse; + hctx->gw.stdin_append = wstunnel_stdin_append; + hctx->gw.create_env = wstunnel_create_env; + hctx->gw.handler_ctx_free = wstunnel_handler_ctx_free; + hctx->gw.backend_error = wstunnel_backend_error; + hctx->gw.response = chunk_buffer_acquire(); + + hctx->frame.state = MOD_WEBSOCKET_FRAME_STATE_INIT; + hctx->frame.ctl.siz = 0; + hctx->frame.payload = chunk_buffer_acquire(); + + binary = !buffer_is_empty(hctx->conf.frame_type); /*("binary")*/ + if (!binary) { + buffer *vb = + http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Sec-WebSocket-Protocol")); + if (NULL != vb) { + for (const char *s = vb->ptr; *s; ++s) { + while (*s==' '||*s=='\t'||*s=='\r'||*s=='\n') ++s; + if (0 == strncasecmp(s, "binary", sizeof("binary")-1)) { + s += sizeof("binary")-1; + while (*s==' '||*s=='\t'||*s=='\r'||*s=='\n') ++s; + if (*s==','||*s=='\0') { + hctx->subproto = 1; + binary = 1; + break; + } + } + else if (0 == strncasecmp(s, "base64", sizeof("base64")-1)) { + s += sizeof("base64")-1; + while (*s==' '||*s=='\t'||*s=='\r'||*s=='\n') ++s; + if (*s==','||*s=='\0') { + hctx->subproto = -1; + break; + } + } + s = strchr(s, ','); + if (NULL == s) break; + } + } + } + + if (binary) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_INFO, "s", + "will recv binary data from backend"); + hctx->frame.type = MOD_WEBSOCKET_FRAME_TYPE_BIN; + hctx->frame.type_before = MOD_WEBSOCKET_FRAME_TYPE_BIN; + hctx->frame.type_backend = MOD_WEBSOCKET_FRAME_TYPE_BIN; + } + else { + DEBUG_LOG(MOD_WEBSOCKET_LOG_INFO, "s", + "will recv text data from backend"); + hctx->frame.type = MOD_WEBSOCKET_FRAME_TYPE_TEXT; + hctx->frame.type_before = MOD_WEBSOCKET_FRAME_TYPE_TEXT; + hctx->frame.type_backend = MOD_WEBSOCKET_FRAME_TYPE_TEXT; + } + + return HANDLER_GO_ON; +} + +static handler_t mod_wstunnel_check_extension(server *srv, connection *con, void *p_d) { + plugin_data *p = p_d; + buffer *vb; + handler_t rc; + + if (con->mode != DIRECT) + return HANDLER_GO_ON; + if (con->request.http_method != HTTP_METHOD_GET) + return HANDLER_GO_ON; + if (con->request.http_version != HTTP_VERSION_1_1) + return HANDLER_GO_ON; + + /* + * Connection: upgrade, keep-alive, ... + * Upgrade: WebSocket, ... + */ + vb = http_header_request_get(con, HTTP_HEADER_UPGRADE, CONST_STR_LEN("Upgrade")); + if (NULL == vb + || !header_contains_token(vb, CONST_STR_LEN("websocket"))) + return HANDLER_GO_ON; + vb = http_header_request_get(con, HTTP_HEADER_CONNECTION, CONST_STR_LEN("Connection")); + if (NULL == vb + || !header_contains_token(vb, CONST_STR_LEN("upgrade"))) + return HANDLER_GO_ON; + + mod_wstunnel_patch_connection(srv, con, p); + if (NULL == p->conf.gw.exts) return HANDLER_GO_ON; + + rc = gw_check_extension(srv,con,(gw_plugin_data *)p,1,sizeof(handler_ctx)); + return (HANDLER_GO_ON == rc && con->mode == p->id) + ? wstunnel_handler_setup(srv, con, p) + : rc; +} + +TRIGGER_FUNC(mod_wstunnel_handle_trigger) { + const plugin_data * const p = p_d; + const time_t cur_ts = srv->cur_ts + 1; + + gw_handle_trigger(srv, p_d); + + for (size_t i = 0; i < srv->conns->used; ++i) { + connection *con = srv->conns->ptr[i]; + handler_ctx *hctx = con->plugin_ctx[p->id]; + if (NULL == hctx || con->mode != p->id) + continue; + + if (hctx->gw.state != GW_STATE_WRITE && hctx->gw.state != GW_STATE_READ) + continue; + + if (cur_ts - con->read_idle_ts > con->conf.max_read_idle) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_INFO, "sds", + "timeout client ( fd =", con->fd, ")"); + mod_wstunnel_frame_send(hctx, MOD_WEBSOCKET_FRAME_TYPE_CLOSE, NULL, 0); + gw_connection_reset(srv, con, p_d); + joblist_append(srv, con); + /* avoid server.c closing connection with error due to max_read_idle + * (might instead run joblist after plugins_call_handle_trigger())*/ + con->read_idle_ts = cur_ts; + continue; + } + + if (0 != hctx->hybivers + && hctx->conf.ping_interval > 0 + && (time_t)hctx->conf.ping_interval + hctx->ping_ts < cur_ts) { + hctx->ping_ts = cur_ts; + mod_wstunnel_frame_send(hctx, MOD_WEBSOCKET_FRAME_TYPE_PING, CONST_STR_LEN("ping")); + joblist_append(srv, con); + continue; + } + } + + return HANDLER_GO_ON; +} + +int mod_wstunnel_plugin_init(plugin *p); +int mod_wstunnel_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("wstunnel"); + p->init = mod_wstunnel_init; + p->cleanup = mod_wstunnel_free; + p->set_defaults = mod_wstunnel_set_defaults; + p->connection_reset = gw_connection_reset; + p->handle_uri_clean = mod_wstunnel_check_extension; + p->handle_subrequest = gw_handle_subrequest; + p->handle_trigger = mod_wstunnel_handle_trigger; + p->handle_waitpid = gw_handle_waitpid_cb; + p->data = NULL; + return 0; +} + + + + +/* + * modified from Norio Kobota mod_websocket_handshake.c + */ + +#ifdef _MOD_WEBSOCKET_SPEC_IETF_00_ + +#include "sys-endian.h" /* lighttpd */ +#include "md5.h" /* lighttpd */ + +static int get_key3(connection *con, char *buf) { + /* 8 bytes should have been sent with request + * for draft-ietf-hybi-thewebsocketprotocol-00 */ + chunkqueue *cq = con->request_content_queue; + size_t bytes = 8; + /*(caller should ensure bytes available prior to calling this routine)*/ + /*assert(chunkqueue_length(cq) >= 8);*/ + for (chunk *c = cq->first; NULL != c; c = c->next) { + /*(chunk_remaining_length() on MEM_CHUNK)*/ + size_t n = (size_t)(buffer_string_length(c->mem) - c->offset); + /*(expecting 8 bytes to be in memory directly after headers)*/ + if (c->type != MEM_CHUNK) break; /* FILE_CHUNK not handled here */ + if (n > bytes) n = bytes; + memcpy(buf, c->mem->ptr+c->offset, n); + buf += n; + if (0 == (bytes -= n)) break; + } + if (0 != bytes) return -1; + chunkqueue_mark_written(cq, 8); + return 0; +} + +static int get_key_number(uint32_t *ret, const buffer *b) { + const char * const s = b->ptr; + size_t j = 0; + unsigned long n; + uint32_t sp = 0; + char tmp[10 + 1]; /* #define UINT32_MAX_STRLEN 10 */ + + for (size_t i = 0, used = buffer_string_length(b); i < used; ++i) { + if (light_isdigit(s[i])) { + tmp[j] = s[i]; + if (++j >= sizeof(tmp)) return -1; + } + else if (s[i] == ' ') ++sp; /* count num spaces */ + } + tmp[j] = '\0'; + n = strtoul(tmp, NULL, 10); + if (n > UINT32_MAX || 0 == sp) return -1; + *ret = (uint32_t)n / sp; + return 0; +} + +static int create_MD5_sum(connection *con) { + uint32_t buf[4]; /* MD5 binary hash len */ + li_MD5_CTX ctx; + + const buffer *key1 = + http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Sec-WebSocket-Key1")); + const buffer *key2 = + http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Sec-WebSocket-Key2")); + + if (NULL == key1 || get_key_number(buf+0, key1) < 0 || + NULL == key2 || get_key_number(buf+1, key2) < 0 || + get_key3(con, (char *)(buf+2)) < 0) { + return -1; + } + #ifdef __BIG_ENDIAN__ + #define ws_htole32(s,u)\ + (s)[0]=((u)>>24); \ + (s)[1]=((u)>>16); \ + (s)[2]=((u)>>8); \ + (s)[3]=((u)) + ws_htole32((unsigned char *)(buf+0), buf[0]); + ws_htole32((unsigned char *)(buf+1), buf[1]); + #endif + li_MD5_Init(&ctx); + li_MD5_Update(&ctx, buf, sizeof(buf)); + li_MD5_Final((unsigned char *)buf, &ctx); /*(overwrite buf[] with result)*/ + chunkqueue_append_mem(con->write_queue, (char *)buf, sizeof(buf)); + return 0; +} + +static int create_response_ietf_00(handler_ctx *hctx) { + connection *con = hctx->gw.remote_conn; + buffer *value = hctx->srv->tmp_buf; + + /* "Origin" header is preferred + * ("Sec-WebSocket-Origin" is from older drafts of websocket spec) */ + buffer *origin = http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Origin")); + if (NULL == origin) { + origin = + http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Sec-WebSocket-Origin")); + } + if (NULL == origin) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", "Origin header is invalid"); + return -1; + } + if (buffer_is_empty(con->request.http_host)) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", "Host header does not exist"); + return -1; + } + + /* calc MD5 sum from keys */ + if (create_MD5_sum(con) < 0) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", "Sec-WebSocket-Key is invalid"); + return -1; + } + + http_header_response_set(con, HTTP_HEADER_UPGRADE, + CONST_STR_LEN("Upgrade"), + CONST_STR_LEN("websocket")); + #if 0 /*(added later in http_response_write_header())*/ + http_header_response_append(con, HTTP_HEADER_CONNECTION, + CONST_STR_LEN("Connection"), + CONST_STR_LEN("upgrade")); + #endif + #if 0 /*(Sec-WebSocket-Origin header is not required for hybi-00)*/ + /* Note: it is insecure to simply reflect back origin provided by client + * (if admin did not configure restricted list of valid origins) + * (see wstunnel_check_request()) */ + http_header_response_set(con, HTTP_HEADER_OTHER, + CONST_STR_LEN("Sec-WebSocket-Origin"), + CONST_BUF_LEN(origin)); + #endif + + if (buffer_is_equal_string(con->uri.scheme, CONST_STR_LEN("https"))) + buffer_copy_string_len(value, CONST_STR_LEN("wss://")); + else + buffer_copy_string_len(value, CONST_STR_LEN("ws://")); + buffer_append_string_buffer(value, con->request.http_host); + buffer_append_string_buffer(value, con->uri.path); + http_header_response_set(con, HTTP_HEADER_OTHER, + CONST_STR_LEN("Sec-WebSocket-Location"), + CONST_BUF_LEN(value)); + + return 0; +} + +#endif /* _MOD_WEBSOCKET_SPEC_IETF_00_ */ + + +#ifdef _MOD_WEBSOCKET_SPEC_RFC_6455_ + +#include "algo_sha1.h" /* lighttpd */ +#include "base64.h" /* lighttpd */ + +static int create_response_rfc_6455(handler_ctx *hctx) { + connection *con = hctx->gw.remote_conn; + SHA_CTX sha; + unsigned char sha_digest[SHA_DIGEST_LENGTH]; + + buffer *value = + http_header_request_get(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Sec-WebSocket-Key")); + if (NULL == value) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", "Sec-WebSocket-Key is invalid"); + return -1; + } + + /* get SHA1 hash of key */ + /* refer: RFC-6455 Sec.1.3 Opening Handshake */ + SHA1_Init(&sha); + SHA1_Update(&sha, (const unsigned char *)CONST_BUF_LEN(value)); + SHA1_Update(&sha, (const unsigned char *)CONST_STR_LEN("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")); + SHA1_Final(sha_digest, &sha); + + http_header_response_set(con, HTTP_HEADER_UPGRADE, + CONST_STR_LEN("Upgrade"), + CONST_STR_LEN("websocket")); + #if 0 /*(added later in http_response_write_header())*/ + http_header_response_append(con, HTTP_HEADER_CONNECTION, + CONST_STR_LEN("Connection"), + CONST_STR_LEN("upgrade")); + #endif + + value = hctx->srv->tmp_buf; + buffer_clear(value); + buffer_append_base64_encode(value, sha_digest, SHA_DIGEST_LENGTH, BASE64_STANDARD); + http_header_response_set(con, HTTP_HEADER_OTHER, + CONST_STR_LEN("Sec-WebSocket-Accept"), + CONST_BUF_LEN(value)); + + if (hctx->frame.type == MOD_WEBSOCKET_FRAME_TYPE_BIN) + http_header_response_set(con, HTTP_HEADER_OTHER, + CONST_STR_LEN("Sec-WebSocket-Protocol"), + CONST_STR_LEN("binary")); + else if (-1 == hctx->subproto) + http_header_response_set(con, HTTP_HEADER_OTHER, + CONST_STR_LEN("Sec-WebSocket-Protocol"), + CONST_STR_LEN("base64")); + + return 0; +} + +#endif /* _MOD_WEBSOCKET_SPEC_RFC_6455_ */ + + +handler_t mod_wstunnel_handshake_create_response(handler_ctx *hctx) { + connection *con = hctx->gw.remote_conn; + #ifdef _MOD_WEBSOCKET_SPEC_RFC_6455_ + if (hctx->hybivers >= 8) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "send handshake response"); + if (0 != create_response_rfc_6455(hctx)) { + con->http_status = 400; /* Bad Request */ + return HANDLER_ERROR; + } + return HANDLER_GO_ON; + } + #endif /* _MOD_WEBSOCKET_SPEC_RFC_6455_ */ + + #ifdef _MOD_WEBSOCKET_SPEC_IETF_00_ + if (hctx->hybivers == 0) { + #ifdef _MOD_WEBSOCKET_SPEC_IETF_00_ + /* 8 bytes should have been sent with request + * for draft-ietf-hybi-thewebsocketprotocol-00 */ + chunkqueue *cq = con->request_content_queue; + if (0 == hctx->hybivers && chunkqueue_length(cq) < 8) + return HANDLER_WAIT_FOR_EVENT; + #endif /* _MOD_WEBSOCKET_SPEC_IETF_00_ */ + + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "send handshake response"); + if (0 != create_response_ietf_00(hctx)) { + con->http_status = 400; /* Bad Request */ + return HANDLER_ERROR; + } + return HANDLER_GO_ON; + } + #endif /* _MOD_WEBSOCKET_SPEC_IETF_00_ */ + + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", "not supported WebSocket Version"); + con->http_status = 503; /* Service Unavailable */ + return HANDLER_ERROR; +} + + + + +/* + * modified from Norio Kobota mod_websocket_frame.c + */ + +#include "base64.h" /* lighttpd */ +#include "http_chunk.h" /* lighttpd */ + +#define MOD_WEBSOCKET_BUFMAX (0x0fffff) + +#ifdef _MOD_WEBSOCKET_SPEC_IETF_00_ + +#include <stdlib.h> +static int send_ietf_00(handler_ctx *hctx, mod_wstunnel_frame_type_t type, const char *payload, size_t siz) { + static const char head = 0; /* 0x00 */ + static const char tail = ~0; /* 0xff */ + server *srv = hctx->srv; + connection *con = hctx->gw.remote_conn; + char *mem; + size_t len; + + switch (type) { + case MOD_WEBSOCKET_FRAME_TYPE_TEXT: + if (0 == siz) return 0; + http_chunk_append_mem(srv, con, &head, 1); + http_chunk_append_mem(srv, con, payload, siz); + http_chunk_append_mem(srv, con, &tail, 1); + len = siz+2; + break; + case MOD_WEBSOCKET_FRAME_TYPE_BIN: + if (0 == siz) return 0; + http_chunk_append_mem(srv, con, &head, 1); + len = 4*(siz/3)+4+1; + /* avoid accumulating too much data in memory; send to tmpfile */ + mem = malloc(len); + force_assert(mem); + len=li_to_base64(mem,len,(unsigned char *)payload,siz,BASE64_STANDARD); + http_chunk_append_mem(srv, con, mem, len); + free(mem); + http_chunk_append_mem(srv, con, &tail, 1); + len += 2; + break; + case MOD_WEBSOCKET_FRAME_TYPE_CLOSE: + http_chunk_append_mem(srv, con, &tail, 1); + http_chunk_append_mem(srv, con, &head, 1); + len = 2; + break; + default: + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", "invalid frame type"); + return -1; + } + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sdsx", + "send data to client ( fd =", con->fd, "), frame size =", len); + return 0; +} + +static int recv_ietf_00(handler_ctx *hctx) { + connection *con = hctx->gw.remote_conn; + chunkqueue *cq = con->request_content_queue; + buffer *payload = hctx->frame.payload; + char *mem; + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sdsx", + "recv data from client ( fd =", con->fd, + "), size =", chunkqueue_length(cq)); + for (chunk *c = cq->first; c; c = c->next) { + char *frame = c->mem->ptr+c->offset; + /*(chunk_remaining_length() on MEM_CHUNK)*/ + size_t flen = (size_t)(buffer_string_length(c->mem) - c->offset); + /*(FILE_CHUNK not handled, but might need to add support)*/ + force_assert(c->type == MEM_CHUNK); + for (size_t i = 0; i < flen; ) { + switch (hctx->frame.state) { + case MOD_WEBSOCKET_FRAME_STATE_INIT: + hctx->frame.ctl.siz = 0; + if (frame[i] == 0x00) { + hctx->frame.state = MOD_WEBSOCKET_FRAME_STATE_READ_PAYLOAD; + i++; + } + else if (((unsigned char *)frame)[i] == 0xff) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG,"s","recv close frame"); + return -1; + } + else { + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG,"s","recv invalid frame"); + return -1; + } + break; + case MOD_WEBSOCKET_FRAME_STATE_READ_PAYLOAD: + mem = (char *)memchr(frame+i, 0xff, flen - i); + if (mem == NULL) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sx", + "got continuous payload, size =", flen - i); + hctx->frame.ctl.siz += flen - i; + if (hctx->frame.ctl.siz > MOD_WEBSOCKET_BUFMAX) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_WARN, "sx", + "frame size has been exceeded:", + MOD_WEBSOCKET_BUFMAX); + return -1; + } + buffer_append_string_len(payload, frame+i, flen - i); + i += flen - i; + } + else { + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sx", + "got final payload, size =", (mem - frame+i)); + hctx->frame.ctl.siz += (mem - frame+i); + if (hctx->frame.ctl.siz > MOD_WEBSOCKET_BUFMAX) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_WARN, "sx", + "frame size has been exceeded:", + MOD_WEBSOCKET_BUFMAX); + return -1; + } + buffer_append_string_len(payload, frame+i, mem - frame+i); + i += (mem - frame+i); + hctx->frame.state = MOD_WEBSOCKET_FRAME_STATE_INIT; + } + i++; + if (hctx->frame.type == MOD_WEBSOCKET_FRAME_TYPE_TEXT + && !buffer_is_empty(payload)) { + hctx->frame.ctl.siz = 0; + chunkqueue_append_buffer(hctx->gw.wb, payload); + buffer_clear(payload); + } + else { + if (hctx->frame.state == MOD_WEBSOCKET_FRAME_STATE_INIT + && !buffer_is_empty(payload)) { + buffer *b; + size_t len = buffer_string_length(payload); + len = (len+3)/4*3+1; + chunkqueue_get_memory(hctx->gw.wb, &len); + b = hctx->gw.wb->last->mem; + len = buffer_string_length(b); + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "ss", + "try to base64 decode:", payload->ptr); + if (NULL == buffer_append_base64_decode(b, CONST_BUF_LEN(payload), BASE64_STANDARD)) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", + "fail to base64-decode"); + return -1; + } + buffer_clear(payload); + /*chunkqueue_use_memory()*/ + hctx->gw.wb->bytes_in += buffer_string_length(b)-len; + } + } + break; + default: /* never reach */ + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR,"s", "BUG: unknown state"); + return -1; + } + } + } + /* XXX: should add ability to handle and preserve partial frames above */ + /*(not chunkqueue_reset(); do not reset cq->bytes_in, cq->bytes_out)*/ + chunkqueue_mark_written(cq, chunkqueue_length(cq)); + return 0; +} + +#endif /* _MOD_WEBSOCKET_SPEC_IETF_00_ */ + + +#ifdef _MOD_WEBSOCKET_SPEC_RFC_6455_ + +#define MOD_WEBSOCKET_OPCODE_CONT 0x00 +#define MOD_WEBSOCKET_OPCODE_TEXT 0x01 +#define MOD_WEBSOCKET_OPCODE_BIN 0x02 +#define MOD_WEBSOCKET_OPCODE_CLOSE 0x08 +#define MOD_WEBSOCKET_OPCODE_PING 0x09 +#define MOD_WEBSOCKET_OPCODE_PONG 0x0A + +#define MOD_WEBSOCKET_FRAME_LEN16 0x7E +#define MOD_WEBSOCKET_FRAME_LEN63 0x7F +#define MOD_WEBSOCKET_FRAME_LEN16_CNT 2 +#define MOD_WEBSOCKET_FRAME_LEN63_CNT 8 + +static int send_rfc_6455(handler_ctx *hctx, mod_wstunnel_frame_type_t type, const char *payload, size_t siz) { + server *srv = hctx->srv; + connection *con = hctx->gw.remote_conn; + char mem[10]; + size_t len; + + /* allowed null payload for ping, pong, close frame */ + if (payload == NULL && ( type == MOD_WEBSOCKET_FRAME_TYPE_TEXT + || type == MOD_WEBSOCKET_FRAME_TYPE_BIN )) { + return -1; + } + + switch (type) { + case MOD_WEBSOCKET_FRAME_TYPE_TEXT: + mem[0] = (char)(0x80 | MOD_WEBSOCKET_OPCODE_TEXT); + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "type = text"); + break; + case MOD_WEBSOCKET_FRAME_TYPE_BIN: + mem[0] = (char)(0x80 | MOD_WEBSOCKET_OPCODE_BIN); + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "type = binary"); + break; + case MOD_WEBSOCKET_FRAME_TYPE_PING: + mem[0] = (char) (0x80 | MOD_WEBSOCKET_OPCODE_PING); + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "type = ping"); + break; + case MOD_WEBSOCKET_FRAME_TYPE_PONG: + mem[0] = (char)(0x80 | MOD_WEBSOCKET_OPCODE_PONG); + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "type = pong"); + break; + case MOD_WEBSOCKET_FRAME_TYPE_CLOSE: + default: + mem[0] = (char)(0x80 | MOD_WEBSOCKET_OPCODE_CLOSE); + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "type = close"); + break; + } + + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sx", "payload size =", siz); + if (siz < MOD_WEBSOCKET_FRAME_LEN16) { + mem[1] = siz; + len = 2; + } + else if (siz <= UINT16_MAX) { + mem[1] = MOD_WEBSOCKET_FRAME_LEN16; + mem[2] = (siz >> 8) & 0xff; + mem[3] = siz & 0xff; + len = 1+MOD_WEBSOCKET_FRAME_LEN16_CNT+1; + } + else { + mem[1] = MOD_WEBSOCKET_FRAME_LEN63; + mem[2] = 0; + mem[3] = 0; + mem[4] = 0; + mem[5] = 0; + mem[6] = (siz >> 24) & 0xff; + mem[7] = (siz >> 16) & 0xff; + mem[8] = (siz >> 8) & 0xff; + mem[9] = siz & 0xff; + len = 1+MOD_WEBSOCKET_FRAME_LEN63_CNT+1; + } + http_chunk_append_mem(srv, con, mem, len); + if (siz) http_chunk_append_mem(srv, con, payload, siz); + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sdsx", + "send data to client ( fd =",con->fd,"), frame size =",len+siz); + return 0; +} + +static void unmask_payload(handler_ctx *hctx) { + buffer * const b = hctx->frame.payload; + for (size_t i = 0, used = buffer_string_length(b); i < used; ++i) { + b->ptr[i] ^= hctx->frame.ctl.mask[hctx->frame.ctl.mask_cnt]; + hctx->frame.ctl.mask_cnt = (hctx->frame.ctl.mask_cnt + 1) % 4; + } +} + +static int recv_rfc_6455(handler_ctx *hctx) { + connection *con = hctx->gw.remote_conn; + chunkqueue *cq = con->request_content_queue; + buffer *payload = hctx->frame.payload; + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sdsx", + "recv data from client ( fd =", con->fd, + "), size =", chunkqueue_length(cq)); + for (chunk *c = cq->first; c; c = c->next) { + char *frame = c->mem->ptr+c->offset; + /*(chunk_remaining_length() on MEM_CHUNK)*/ + size_t flen = (size_t)(buffer_string_length(c->mem) - c->offset); + /*(FILE_CHUNK not handled, but might need to add support)*/ + force_assert(c->type == MEM_CHUNK); + for (size_t i = 0; i < flen; ) { + switch (hctx->frame.state) { + case MOD_WEBSOCKET_FRAME_STATE_INIT: + switch (frame[i] & 0x0f) { + case MOD_WEBSOCKET_OPCODE_CONT: + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "type = continue"); + hctx->frame.type = hctx->frame.type_before; + break; + case MOD_WEBSOCKET_OPCODE_TEXT: + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "type = text"); + hctx->frame.type = MOD_WEBSOCKET_FRAME_TYPE_TEXT; + hctx->frame.type_before = hctx->frame.type; + break; + case MOD_WEBSOCKET_OPCODE_BIN: + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "type = binary"); + hctx->frame.type = MOD_WEBSOCKET_FRAME_TYPE_BIN; + hctx->frame.type_before = hctx->frame.type; + break; + case MOD_WEBSOCKET_OPCODE_PING: + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "type = ping"); + hctx->frame.type = MOD_WEBSOCKET_FRAME_TYPE_PING; + break; + case MOD_WEBSOCKET_OPCODE_PONG: + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "type = pong"); + hctx->frame.type = MOD_WEBSOCKET_FRAME_TYPE_PONG; + break; + case MOD_WEBSOCKET_OPCODE_CLOSE: + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "s", "type = close"); + hctx->frame.type = MOD_WEBSOCKET_FRAME_TYPE_CLOSE; + return -1; + break; + default: + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", "type is invalid"); + return -1; + break; + } + i++; + hctx->frame.state = MOD_WEBSOCKET_FRAME_STATE_READ_LENGTH; + break; + case MOD_WEBSOCKET_FRAME_STATE_READ_LENGTH: + if ((frame[i] & 0x80) != 0x80) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", + "payload was not masked"); + return -1; + } + hctx->frame.ctl.mask_cnt = 0; + hctx->frame.ctl.siz = (uint64_t)(frame[i] & 0x7f); + if (hctx->frame.ctl.siz == 0) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sx", + "specified payload size =", hctx->frame.ctl.siz); + hctx->frame.state = MOD_WEBSOCKET_FRAME_STATE_READ_MASK; + } + else if (hctx->frame.ctl.siz == MOD_WEBSOCKET_FRAME_LEN16) { + hctx->frame.ctl.siz = 0; + hctx->frame.ctl.siz_cnt = MOD_WEBSOCKET_FRAME_LEN16_CNT; + hctx->frame.state = + MOD_WEBSOCKET_FRAME_STATE_READ_EX_LENGTH; + } + else if (hctx->frame.ctl.siz == MOD_WEBSOCKET_FRAME_LEN63) { + hctx->frame.ctl.siz = 0; + hctx->frame.ctl.siz_cnt = MOD_WEBSOCKET_FRAME_LEN63_CNT; + hctx->frame.state = + MOD_WEBSOCKET_FRAME_STATE_READ_EX_LENGTH; + } + else { + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sx", + "specified payload size =", hctx->frame.ctl.siz); + hctx->frame.state = MOD_WEBSOCKET_FRAME_STATE_READ_MASK; + } + i++; + break; + case MOD_WEBSOCKET_FRAME_STATE_READ_EX_LENGTH: + hctx->frame.ctl.siz = + (hctx->frame.ctl.siz << 8) + (frame[i] & 0xff); + hctx->frame.ctl.siz_cnt--; + if (hctx->frame.ctl.siz_cnt <= 0) { + if (hctx->frame.type == MOD_WEBSOCKET_FRAME_TYPE_PING && + hctx->frame.ctl.siz > MOD_WEBSOCKET_BUFMAX) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_WARN, "sx", + "frame size has been exceeded:", + MOD_WEBSOCKET_BUFMAX); + return -1; + } + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sx", + "specified payload size =", hctx->frame.ctl.siz); + hctx->frame.state = MOD_WEBSOCKET_FRAME_STATE_READ_MASK; + } + i++; + break; + case MOD_WEBSOCKET_FRAME_STATE_READ_MASK: + hctx->frame.ctl.mask[hctx->frame.ctl.mask_cnt] = frame[i]; + hctx->frame.ctl.mask_cnt++; + if (hctx->frame.ctl.mask_cnt >= MOD_WEBSOCKET_MASK_CNT) { + hctx->frame.ctl.mask_cnt = 0; + if (hctx->frame.type == MOD_WEBSOCKET_FRAME_TYPE_PING && + hctx->frame.ctl.siz == 0) { + mod_wstunnel_frame_send(hctx, + MOD_WEBSOCKET_FRAME_TYPE_PONG, + NULL, 0); + } + if (hctx->frame.ctl.siz == 0) { + hctx->frame.state = MOD_WEBSOCKET_FRAME_STATE_INIT; + } + else { + hctx->frame.state = + MOD_WEBSOCKET_FRAME_STATE_READ_PAYLOAD; + } + } + i++; + break; + case MOD_WEBSOCKET_FRAME_STATE_READ_PAYLOAD: + /* hctx->frame.ctl.siz <= SIZE_MAX */ + if (hctx->frame.ctl.siz <= flen - i) { + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sx", + "read payload, size =", hctx->frame.ctl.siz); + buffer_append_string_len(payload, frame+i, (size_t) + (hctx->frame.ctl.siz & SIZE_MAX)); + i += (size_t)(hctx->frame.ctl.siz & SIZE_MAX); + hctx->frame.ctl.siz = 0; + hctx->frame.state = MOD_WEBSOCKET_FRAME_STATE_INIT; + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sx", + "rest of frame size =", flen - i); + /* SIZE_MAX < hctx->frame.ctl.siz */ + } + else { + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sx", + "read payload, size =", flen - i); + buffer_append_string_len(payload, frame+i, flen - i); + hctx->frame.ctl.siz -= flen - i; + i += flen - i; + DEBUG_LOG(MOD_WEBSOCKET_LOG_DEBUG, "sx", + "rest of payload size =", hctx->frame.ctl.siz); + } + switch (hctx->frame.type) { + case MOD_WEBSOCKET_FRAME_TYPE_TEXT: + case MOD_WEBSOCKET_FRAME_TYPE_BIN: + { + unmask_payload(hctx); + chunkqueue_append_buffer(hctx->gw.wb, payload); + buffer_clear(payload); + break; + } + case MOD_WEBSOCKET_FRAME_TYPE_PING: + if (hctx->frame.ctl.siz == 0) { + unmask_payload(hctx); + mod_wstunnel_frame_send(hctx, + MOD_WEBSOCKET_FRAME_TYPE_PONG, + payload->ptr, buffer_string_length(payload)); + buffer_clear(payload); + } + break; + case MOD_WEBSOCKET_FRAME_TYPE_PONG: + buffer_clear(payload); + break; + case MOD_WEBSOCKET_FRAME_TYPE_CLOSE: + default: + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", + "BUG: invalid frame type"); + return -1; + } + break; + default: + DEBUG_LOG(MOD_WEBSOCKET_LOG_ERR, "s", "BUG: invalid state"); + return -1; + } + } + } + /* XXX: should add ability to handle and preserve partial frames above */ + /*(not chunkqueue_reset(); do not reset cq->bytes_in, cq->bytes_out)*/ + chunkqueue_mark_written(cq, chunkqueue_length(cq)); + return 0; +} + +#endif /* _MOD_WEBSOCKET_SPEC_RFC_6455_ */ + + +int mod_wstunnel_frame_send(handler_ctx *hctx, mod_wstunnel_frame_type_t type, + const char *payload, size_t siz) { + #ifdef _MOD_WEBSOCKET_SPEC_RFC_6455_ + if (hctx->hybivers >= 8) return send_rfc_6455(hctx, type, payload, siz); + #endif /* _MOD_WEBSOCKET_SPEC_RFC_6455_ */ + #ifdef _MOD_WEBSOCKET_SPEC_IETF_00_ + if (0 == hctx->hybivers) return send_ietf_00(hctx, type, payload, siz); + #endif /* _MOD_WEBSOCKET_SPEC_IETF_00_ */ + return -1; +} + +int mod_wstunnel_frame_recv(handler_ctx *hctx) { + #ifdef _MOD_WEBSOCKET_SPEC_RFC_6455_ + if (hctx->hybivers >= 8) return recv_rfc_6455(hctx); + #endif /* _MOD_WEBSOCKET_SPEC_RFC_6455_ */ + #ifdef _MOD_WEBSOCKET_SPEC_IETF_00_ + if (0 == hctx->hybivers) return recv_ietf_00(hctx); + #endif /* _MOD_WEBSOCKET_SPEC_IETF_00_ */ + return -1; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/network.c b/data/lighttpd/lighttpd-1.4.53/src/network.c new file mode 100644 index 000000000..016d0d5bb --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/network.c @@ -0,0 +1,540 @@ +#include "first.h" + +#include "network.h" +#include "base.h" +#include "fdevent.h" +#include "log.h" +#include "connections.h" +#include "configfile.h" +#include "sock_addr.h" + +#include "network_write.h" +#include "sys-socket.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> + +void +network_accept_tcp_nagle_disable (const int fd) +{ + static int noinherit_tcpnodelay = -1; + int opt; + + if (!noinherit_tcpnodelay) /* TCP_NODELAY inherited from listen socket */ + return; + + if (noinherit_tcpnodelay < 0) { + socklen_t optlen = sizeof(opt); + if (0 == getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen)) { + noinherit_tcpnodelay = !opt; + if (opt) /* TCP_NODELAY inherited from listen socket */ + return; + } + } + + opt = 1; + (void)setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); +} + +static handler_t network_server_handle_fdevent(server *srv, void *context, int revents) { + server_socket *srv_socket = (server_socket *)context; + connection *con; + int loops = 0; + + UNUSED(context); + + if (0 == (revents & FDEVENT_IN)) { + log_error_write(srv, __FILE__, __LINE__, "sdd", + "strange event for server socket", + srv_socket->fd, + revents); + return HANDLER_ERROR; + } + + /* accept()s at most 100 connections directly + * + * we jump out after 100 to give the waiting connections a chance */ + for (loops = 0; loops < 100 && NULL != (con = connection_accept(srv, srv_socket)); loops++) { + connection_state_machine(srv, con); + } + return HANDLER_GO_ON; +} + +static void network_host_normalize_addr_str(buffer *host, sock_addr *addr) { + buffer_clear(host); + sock_addr_stringify_append_buffer(host, addr); +} + +static int network_host_parse_addr(server *srv, sock_addr *addr, socklen_t *addr_len, buffer *host, int use_ipv6) { + char *h; + char *colon = NULL; + const char *chost; + sa_family_t family = use_ipv6 ? AF_INET6 : AF_INET; + unsigned int port = srv->srvconf.port; + if (buffer_string_is_empty(host)) { + log_error_write(srv, __FILE__, __LINE__, "s", "value of $SERVER[\"socket\"] must not be empty"); + return -1; + } + h = host->ptr; + if (h[0] == '/') { + #ifdef HAVE_SYS_UN_H + return (1 == sock_addr_from_str_hints(srv,addr,addr_len,h,AF_UNIX,0)) + ? 0 + : -1; + #else + log_error_write(srv, __FILE__, __LINE__, "s", + "ERROR: Unix Domain sockets are not supported."); + return -1; + #endif + } + buffer_copy_buffer(srv->tmp_buf, host); + h = srv->tmp_buf->ptr; + if (h[0] == '[') { + family = AF_INET6; + if ((h = strchr(h, ']'))) { + *h++ = '\0'; + if (*h == ':') colon = h; + } /*(else should not happen; validated in configparser.y)*/ + h = srv->tmp_buf->ptr+1; + } + else { + colon = strrchr(h, ':'); + } + if (colon) { + *colon++ = '\0'; + port = strtol(colon, NULL, 10); + if (port == 0 || port > 65535) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "port not set or out of range:", port); + return -1; + } + } + chost = *h ? h : family == AF_INET ? "0.0.0.0" : "::"; + if (1 != sock_addr_from_str_hints(srv,addr,addr_len,chost,family,port)) { + return -1; + } + return 0; +} + +static void network_srv_sockets_append(server *srv, server_socket *srv_socket) { + if (srv->srv_sockets.size == 0) { + srv->srv_sockets.size = 4; + srv->srv_sockets.used = 0; + srv->srv_sockets.ptr = malloc(srv->srv_sockets.size * sizeof(server_socket*)); + force_assert(NULL != srv->srv_sockets.ptr); + } else if (srv->srv_sockets.used == srv->srv_sockets.size) { + srv->srv_sockets.size += 4; + srv->srv_sockets.ptr = realloc(srv->srv_sockets.ptr, srv->srv_sockets.size * sizeof(server_socket*)); + force_assert(NULL != srv->srv_sockets.ptr); + } + srv->srv_sockets.ptr[srv->srv_sockets.used++] = srv_socket; +} + +static int network_server_init(server *srv, buffer *host_token, size_t sidx, int stdin_fd) { + server_socket *srv_socket; + const char *host; + specific_config *s = srv->config_storage[sidx]; + socklen_t addr_len = sizeof(sock_addr); + sock_addr addr; + int family = 0; + int set_v6only = 0; + + if (buffer_string_is_empty(host_token)) { + log_error_write(srv, __FILE__, __LINE__, "s", "value of $SERVER[\"socket\"] must not be empty"); + return -1; + } + + /* check if we already know this socket, and if yes, don't init it + * (optimization: check strings here to filter out exact matches; + * binary addresses are matched further below) */ + for (size_t i = 0; i < srv->srv_sockets.used; ++i) { + if (buffer_is_equal(srv->srv_sockets.ptr[i]->srv_token, host_token)) { + buffer_copy_buffer(host_token, srv->srv_sockets.ptr[i]->srv_token); + return 0; + } + } + + host = host_token->ptr; + if ((s->use_ipv6 && (*host == '\0' || *host == ':')) || (host[0] == '[' && host[1] == ']')) { + log_error_write(srv, __FILE__, __LINE__, "s", "warning: please use server.use-ipv6 only for hostnames, not without server.bind / empty address; your config will break if the kernel default for IPV6_V6ONLY changes"); + } + if (*host == '[') s->use_ipv6 = 1; + + memset(&addr, 0, sizeof(addr)); + if (-1 != stdin_fd) { + if (-1 == getsockname(stdin_fd, (struct sockaddr *)&addr, &addr_len)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "getsockname()", strerror(errno)); + return -1; + } + } else if (0 != network_host_parse_addr(srv, &addr, &addr_len, host_token, s->use_ipv6)) { + return -1; + } + + family = sock_addr_get_family(&addr); + + #ifdef HAVE_IPV6 + if (*host != '\0' && AF_INET6 == family) { + if (s->set_v6only) { + set_v6only = 1; + } else { + log_error_write(srv, __FILE__, __LINE__, "s", "warning: server.set-v6only will be removed soon, update your config to have different sockets for ipv4 and ipv6"); + } + } + #endif + + network_host_normalize_addr_str(host_token, &addr); + host = host_token->ptr; + + if (srv->srvconf.preflight_check) { + return 0; + } + + /* check if we already know this socket (after potential DNS resolution), and if yes, don't init it */ + for (size_t i = 0; i < srv->srv_sockets.used; ++i) { + if (0 == memcmp(&srv->srv_sockets.ptr[i]->addr, &addr, sizeof(addr))) { + return 0; + } + } + + srv_socket = calloc(1, sizeof(*srv_socket)); + force_assert(NULL != srv_socket); + memcpy(&srv_socket->addr, &addr, addr_len); + srv_socket->fd = -1; + srv_socket->fde_ndx = -1; + srv_socket->sidx = sidx; + srv_socket->is_ssl = s->ssl_enabled; + srv_socket->srv_token = buffer_init_buffer(host_token); + + network_srv_sockets_append(srv, srv_socket); + + if (srv->sockets_disabled) { /* lighttpd -1 (one-shot mode) */ + return 0; + } + + if (srv->srvconf.systemd_socket_activation) { + for (size_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { + if (0 != memcmp(&srv->srv_sockets_inherited.ptr[i]->addr, &srv_socket->addr, addr_len)) continue; + if ((unsigned short)~0u == srv->srv_sockets_inherited.ptr[i]->sidx) { + srv->srv_sockets_inherited.ptr[i]->sidx = sidx; + } + stdin_fd = srv->srv_sockets_inherited.ptr[i]->fd; + break; + } + } + + if (-1 != stdin_fd) { + srv_socket->fd = stdin_fd; + if (-1 == fdevent_fcntl_set_nb_cloexec(srv->ev, stdin_fd)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl:", strerror(errno)); + return -1; + } + } else +#ifdef HAVE_SYS_UN_H + if (AF_UNIX == family) { + /* check if the socket exists and try to connect to it. */ + force_assert(host); /*(static analysis hint)*/ + if (-1 == (srv_socket->fd = fdevent_socket_cloexec(AF_UNIX, SOCK_STREAM, 0))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno)); + return -1; + } + if (0 == connect(srv_socket->fd, (struct sockaddr *) &(srv_socket->addr), addr_len)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "server socket is still in use:", + host); + + + return -1; + } + + /* connect failed */ + switch(errno) { + case ECONNREFUSED: + unlink(host); + break; + case ENOENT: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sds", + "testing socket failed:", + host, strerror(errno)); + + return -1; + } + + fdevent_fcntl_set_nb(srv->ev, srv_socket->fd); + } else +#endif + { + if (-1 == (srv_socket->fd = fdevent_socket_nb_cloexec(family, SOCK_STREAM, IPPROTO_TCP))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno)); + return -1; + } + } + +#ifdef HAVE_IPV6 + if (set_v6only && -1 == stdin_fd) { + int val = 1; + if (-1 == setsockopt(srv_socket->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "setsockopt(IPV6_V6ONLY) failed:", strerror(errno)); + return -1; + } + } +#endif + + /* */ + srv->cur_fds = srv_socket->fd; + + if (fdevent_set_so_reuseaddr(srv_socket->fd, 1) < 0) { + log_error_write(srv, __FILE__, __LINE__, "ss", "setsockopt(SO_REUSEADDR) failed:", strerror(errno)); + return -1; + } + + if (family != AF_UNIX) { + if (fdevent_set_tcp_nodelay(srv_socket->fd, 1) < 0) { + log_error_write(srv, __FILE__, __LINE__, "ss", "setsockopt(TCP_NODELAY) failed:", strerror(errno)); + return -1; + } + } + + if (-1 != stdin_fd) { } else + if (0 != bind(srv_socket->fd, (struct sockaddr *) &(srv_socket->addr), addr_len)) { + log_error_write(srv, __FILE__, __LINE__, "sss", + "can't bind to socket:", host, strerror(errno)); + return -1; + } + + if (-1 != stdin_fd) { } else + if (AF_UNIX == family && !buffer_string_is_empty(s->socket_perms)) { + mode_t m = 0; + for (char *str = s->socket_perms->ptr; *str; ++str) { + m <<= 3; + m |= (*str - '0'); + } + if (0 != m && -1 == chmod(host, m)) { + log_error_write(srv, __FILE__, __LINE__, "sssbss", "chmod(\"", host, "\", ", s->socket_perms, "):", strerror(errno)); + } + } + + if (-1 != stdin_fd) { } else + if (-1 == listen(srv_socket->fd, s->listen_backlog)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "listen failed: ", strerror(errno)); + return -1; + } + + if (s->ssl_enabled) { +#ifdef TCP_DEFER_ACCEPT + } else if (s->defer_accept) { + int v = s->defer_accept; + if (-1 == setsockopt(srv_socket->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &v, sizeof(v))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "can't set TCP_DEFER_ACCEPT: ", strerror(errno)); + } +#endif +#if defined(__FreeBSD__) || defined(__NetBSD__) \ + || defined(__OpenBSD__) || defined(__DragonFly__) + } else if (!buffer_is_empty(s->bsd_accept_filter) + && (buffer_is_equal_string(s->bsd_accept_filter, CONST_STR_LEN("httpready")) + || buffer_is_equal_string(s->bsd_accept_filter, CONST_STR_LEN("dataready")))) { +#ifdef SO_ACCEPTFILTER + /* FreeBSD accf_http filter */ + struct accept_filter_arg afa; + memset(&afa, 0, sizeof(afa)); + strncpy(afa.af_name, s->bsd_accept_filter->ptr, sizeof(afa.af_name)); + if (setsockopt(srv_socket->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0) { + if (errno != ENOENT) { + log_error_write(srv, __FILE__, __LINE__, "SBss", "can't set accept-filter '", s->bsd_accept_filter, "':", strerror(errno)); + } + } +#endif +#endif + } + + return 0; +} + +int network_close(server *srv) { + size_t i; + for (i = 0; i < srv->srv_sockets.used; i++) { + server_socket *srv_socket = srv->srv_sockets.ptr[i]; + if (srv_socket->fd != -1) { + network_unregister_sock(srv, srv_socket); + close(srv_socket->fd); + } + + buffer_free(srv_socket->srv_token); + + free(srv_socket); + } + + free(srv->srv_sockets.ptr); + srv->srv_sockets.ptr = NULL; + srv->srv_sockets.used = 0; + srv->srv_sockets.size = 0; + + for (i = 0; i < srv->srv_sockets_inherited.used; i++) { + server_socket *srv_socket = srv->srv_sockets_inherited.ptr[i]; + if (srv_socket->fd != -1 && srv_socket->sidx != (unsigned short)~0u) { + close(srv_socket->fd); + } + + buffer_free(srv_socket->srv_token); + + free(srv_socket); + } + + free(srv->srv_sockets_inherited.ptr); + srv->srv_sockets_inherited.ptr = NULL; + srv->srv_sockets_inherited.used = 0; + srv->srv_sockets_inherited.size = 0; + + return 0; +} + +static int network_socket_activation_nfds(server *srv, int nfds) { + buffer *host = buffer_init(); + socklen_t addr_len; + sock_addr addr; + int rc = 0; + nfds += 3; /* #define SD_LISTEN_FDS_START 3 */ + for (int fd = 3; fd < nfds; ++fd) { + addr_len = sizeof(sock_addr); + if (-1 == (rc = getsockname(fd, (struct sockaddr *)&addr, &addr_len))) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "socket activation getsockname()", strerror(errno)); + break; + } + network_host_normalize_addr_str(host, &addr); + rc = network_server_init(srv, host, 0, fd); + if (0 != rc) break; + srv->srv_sockets.ptr[srv->srv_sockets.used-1]->sidx = (unsigned short)~0u; + } + buffer_free(host); + memcpy(&srv->srv_sockets_inherited, &srv->srv_sockets, sizeof(server_socket_array)); + memset(&srv->srv_sockets, 0, sizeof(server_socket_array)); + return rc; +} + +static int network_socket_activation_from_env(server *srv) { + char *listen_pid = getenv("LISTEN_PID"); + char *listen_fds = getenv("LISTEN_FDS"); + pid_t lpid = listen_pid ? (pid_t)strtoul(listen_pid,NULL,10) : 0; + int nfds = listen_fds ? atoi(listen_fds) : 0; + int rc = (lpid == getpid() && nfds > 0) + ? network_socket_activation_nfds(srv, nfds) + : 0; + unsetenv("LISTEN_PID"); + unsetenv("LISTEN_FDS"); + unsetenv("LISTEN_FDNAMES"); + /*(upon graceful restart, unsetenv will result in no-op above)*/ + return rc; +} + +int network_init(server *srv, int stdin_fd) { + #ifdef __WIN32 + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD(2, 2); + if (0 != WSAStartup(wVersionRequested, &wsaData)) { + /* Tell the user that we could not find a usable WinSock DLL */ + return -1; + } + #endif + + if (0 != network_write_init(srv)) return -1; + + if (srv->srvconf.systemd_socket_activation) { + for (size_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { + srv->srv_sockets_inherited.ptr[i]->sidx = (unsigned short)~0u; + } + if (0 != network_socket_activation_from_env(srv)) return -1; + if (0 == srv->srv_sockets_inherited.used) { + srv->srvconf.systemd_socket_activation = 0; + } + } + + /* process srv->srvconf.bindhost + * (skip if systemd socket activation is enabled and bindhost is empty; do not additionally listen on "*") */ + if (!srv->srvconf.systemd_socket_activation || !buffer_string_is_empty(srv->srvconf.bindhost)) { + int rc; + buffer *b = buffer_init(); + buffer_copy_buffer(b, srv->srvconf.bindhost); + if (b->ptr[0] != '/') { /*(skip adding port if unix socket path)*/ + buffer_append_string_len(b, CONST_STR_LEN(":")); + buffer_append_int(b, srv->srvconf.port); + } + + rc = (-1 == stdin_fd || 0 == srv->srv_sockets.used) + ? network_server_init(srv, b, 0, stdin_fd) + : close(stdin_fd);/*(graceful restart listening to "/dev/stdin")*/ + buffer_free(b); + if (0 != rc) return -1; + } + + /* check for $SERVER["socket"] */ + for (size_t i = 1; i < srv->config_context->used; ++i) { + data_config *dc = (data_config *)srv->config_context->data[i]; + + /* not our stage */ + if (COMP_SERVER_SOCKET != dc->comp) continue; + + if (dc->cond == CONFIG_COND_NE) { + socklen_t addr_len = sizeof(sock_addr); + sock_addr addr; + if (0 != network_host_parse_addr(srv, &addr, &addr_len, dc->string, srv->config_storage[i]->use_ipv6)) { + return -1; + } + network_host_normalize_addr_str(dc->string, &addr); + continue; + } + + if (dc->cond != CONFIG_COND_EQ) continue; + + if (0 != network_server_init(srv, dc->string, i, -1)) return -1; + } + + if (srv->srvconf.systemd_socket_activation) { + /* activate any inherited sockets not explicitly listed in config file */ + server_socket *srv_socket; + for (size_t i = 0; i < srv->srv_sockets_inherited.used; ++i) { + if ((unsigned short)~0u != srv->srv_sockets_inherited.ptr[i]->sidx) continue; + srv->srv_sockets_inherited.ptr[i]->sidx = 0; + srv_socket = calloc(1, sizeof(server_socket)); + force_assert(NULL != srv_socket); + memcpy(srv_socket, srv->srv_sockets_inherited.ptr[i], sizeof(server_socket)); + network_srv_sockets_append(srv, srv_socket); + } + } + + return 0; +} + +void network_unregister_sock(server *srv, server_socket *srv_socket) { + if (-1 == srv_socket->fd || -1 == srv_socket->fde_ndx) return; + fdevent_event_del(srv->ev, &srv_socket->fde_ndx, srv_socket->fd); + fdevent_unregister(srv->ev, srv_socket->fd); +} + +int network_register_fdevents(server *srv) { + size_t i; + + if (-1 == fdevent_reset(srv->ev)) { + return -1; + } + + if (srv->sockets_disabled) return 0; /* lighttpd -1 (one-shot mode) */ + + /* register fdevents after reset */ + for (i = 0; i < srv->srv_sockets.used; i++) { + server_socket *srv_socket = srv->srv_sockets.ptr[i]; + + fdevent_register(srv->ev, srv_socket->fd, network_server_handle_fdevent, srv_socket); + fdevent_event_set(srv->ev, &(srv_socket->fde_ndx), srv_socket->fd, FDEVENT_IN); + } + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/network.h b/data/lighttpd/lighttpd-1.4.53/src/network.h new file mode 100644 index 000000000..f6f9f3fe3 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/network.h @@ -0,0 +1,17 @@ +#ifndef _NETWORK_H_ +#define _NETWORK_H_ +#include "first.h" + +#include "base_decls.h" + +struct server_socket; /* declaration */ + +void network_accept_tcp_nagle_disable(int fd); + +int network_init(server *srv, int stdin_fd); +int network_close(server *srv); + +int network_register_fdevents(server *srv); +void network_unregister_sock(server *srv, struct server_socket *srv_socket); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/network_write.c b/data/lighttpd/lighttpd-1.4.53/src/network_write.c new file mode 100644 index 000000000..325546bbf --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/network_write.c @@ -0,0 +1,738 @@ +#include "first.h" + +#include "network_write.h" + +#include "base.h" +#include "log.h" + +#include <sys/types.h> +#include "sys-socket.h" + +#include <errno.h> +#include <string.h> +#include <unistd.h> + + +/* on linux 2.4.x you get either sendfile or LFS */ +#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILE \ + && (!defined _LARGEFILE_SOURCE || defined HAVE_SENDFILE64) \ + && defined(__linux__) && !defined HAVE_SENDFILE_BROKEN +# ifdef NETWORK_WRITE_USE_SENDFILE +# error "can't have more than one sendfile implementation" +# endif +# define NETWORK_WRITE_USE_SENDFILE "linux-sendfile" +# define NETWORK_WRITE_USE_LINUX_SENDFILE +#endif + +#if defined HAVE_SENDFILE && (defined(__FreeBSD__) || defined(__DragonFly__)) +# ifdef NETWORK_WRITE_USE_SENDFILE +# error "can't have more than one sendfile implementation" +# endif +# define NETWORK_WRITE_USE_SENDFILE "freebsd-sendfile" +# define NETWORK_WRITE_USE_FREEBSD_SENDFILE +#endif + +#if defined HAVE_SENDFILE && defined(__APPLE__) +# ifdef NETWORK_WRITE_USE_SENDFILE +# error "can't have more than one sendfile implementation" +# endif +# define NETWORK_WRITE_USE_SENDFILE "darwin-sendfile" +# define NETWORK_WRITE_USE_DARWIN_SENDFILE +#endif + +#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILEV && defined(__sun) +# ifdef NETWORK_WRITE_USE_SENDFILE +# error "can't have more than one sendfile implementation" +# endif +# define NETWORK_WRITE_USE_SENDFILE "solaris-sendfilev" +# define NETWORK_WRITE_USE_SOLARIS_SENDFILEV +#endif + +/* not supported so far +#if defined HAVE_SEND_FILE && defined(__aix) +# ifdef NETWORK_WRITE_USE_SENDFILE +# error "can't have more than one sendfile implementation" +# endif +# define NETWORK_WRITE_USE_SENDFILE "aix-sendfile" +# define NETWORK_WRITE_USE_AIX_SENDFILE +#endif +*/ + +#if defined HAVE_SYS_UIO_H && defined HAVE_WRITEV +# define NETWORK_WRITE_USE_WRITEV +#endif + +#if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP && defined ENABLE_MMAP +# define NETWORK_WRITE_USE_MMAP +#endif + + +static int network_write_error(server *srv, int fd) { + #if defined(__WIN32) + int lastError = WSAGetLastError(); + switch (lastError) { + case WSAEINTR: + case WSAEWOULDBLOCK: + return -3; + case WSAECONNRESET: + case WSAETIMEDOUT: + case WSAECONNABORTED: + return -2; + default: + log_error_write(srv, __FILE__, __LINE__, "sdd", + "send failed: ", lastError, fd); + return -1; + } + #else /* __WIN32 */ + switch (errno) { + case EAGAIN: + case EINTR: + return -3; + case EPIPE: + case ECONNRESET: + return -2; + default: + log_error_write(srv, __FILE__, __LINE__, "ssd", + "write failed:", strerror(errno), fd); + return -1; + } + #endif /* __WIN32 */ +} + +inline +static ssize_t network_write_data_len(int fd, const char *data, off_t len) { + #if defined(__WIN32) + return send(fd, data, len, 0); + #else /* __WIN32 */ + return write(fd, data, len); + #endif /* __WIN32 */ +} + + + + +/* write next chunk(s); finished chunks are removed afterwards after successful writes. + * return values: similar as backends (0 succes, -1 error, -2 remote close, -3 try again later (EINTR/EAGAIN)) */ +/* next chunk must be MEM_CHUNK. use write()/send() */ +static int network_write_mem_chunk(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) { + chunk* const c = cq->first; + ssize_t wr; + off_t c_len = (off_t)buffer_string_length(c->mem); + force_assert(c->offset >= 0 && c->offset <= c_len); + c_len -= c->offset; + if (c_len > *p_max_bytes) c_len = *p_max_bytes; + + if (0 == c_len) { + chunkqueue_remove_finished_chunks(cq); + return 0; + } + + wr = network_write_data_len(fd, c->mem->ptr + c->offset, c_len); + if (wr >= 0) { + *p_max_bytes -= wr; + chunkqueue_mark_written(cq, wr); + return (wr > 0 && wr == c_len) ? 0 : -3; + } else { + return network_write_error(srv, fd); + } +} + + + + +#if !defined(NETWORK_WRITE_USE_MMAP) + +static int network_write_file_chunk_no_mmap(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) { + chunk* const c = cq->first; + off_t offset, toSend; + ssize_t wr; + + force_assert(c->offset >= 0 && c->offset <= c->file.length); + + offset = c->file.start + c->offset; + toSend = c->file.length - c->offset; + if (toSend > *p_max_bytes) toSend = *p_max_bytes; + + if (0 == toSend) { + chunkqueue_remove_finished_chunks(cq); + return 0; + } + + if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; + + if (toSend > 64*1024) toSend = 64*1024; /* max read 64kb in one step */ + buffer_string_prepare_copy(srv->tmp_buf, toSend); + + if (-1 == lseek(c->file.fd, offset, SEEK_SET)) { + log_error_write(srv, __FILE__, __LINE__, "ss","lseek:",strerror(errno)); + return -1; + } + if (-1 == (toSend = read(c->file.fd, srv->tmp_buf->ptr, toSend))) { + log_error_write(srv, __FILE__, __LINE__, "ss","read:",strerror(errno)); + return -1; + } + + wr = network_write_data_len(fd, srv->tmp_buf->ptr, toSend); + if (wr >= 0) { + *p_max_bytes -= wr; + chunkqueue_mark_written(cq, wr); + return (wr > 0 && wr == toSend) ? 0 : -3; + } else { + return network_write_error(srv, fd); + } +} + +#endif + + + + +#if defined(NETWORK_WRITE_USE_MMAP) + +#include "sys-mmap.h" + +#include <setjmp.h> +#include <signal.h> + +#define MMAP_CHUNK_SIZE (512*1024) + +static off_t mmap_align_offset(off_t start) { + static long pagesize = 0; + if (0 == pagesize) { + pagesize = sysconf(_SC_PAGESIZE); + force_assert(pagesize < MMAP_CHUNK_SIZE); + } + force_assert(start >= (start % pagesize)); + return start - (start % pagesize); +} + +static volatile int sigbus_jmp_valid; +static sigjmp_buf sigbus_jmp; + +static void sigbus_handler(int sig) { + UNUSED(sig); + if (sigbus_jmp_valid) siglongjmp(sigbus_jmp, 1); + log_failed_assert(__FILE__, __LINE__, "SIGBUS"); +} + +/* next chunk must be FILE_CHUNK. send mmap()ed file with write() */ +static int network_write_file_chunk_mmap(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) { + chunk* const c = cq->first; + off_t offset, toSend, file_end; + ssize_t r; + size_t mmap_offset, mmap_avail; + const char *data; + + force_assert(c->offset >= 0 && c->offset <= c->file.length); + + offset = c->file.start + c->offset; + toSend = c->file.length - c->offset; + if (toSend > *p_max_bytes) toSend = *p_max_bytes; + file_end = c->file.start + c->file.length; /*file end offset in this chunk*/ + + if (0 == toSend) { + chunkqueue_remove_finished_chunks(cq); + return 0; + } + + if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; + + /* mmap buffer if offset is outside old mmap area or not mapped at all */ + if (MAP_FAILED == c->file.mmap.start + || offset < c->file.mmap.offset + || offset >= (off_t)(c->file.mmap.offset + c->file.mmap.length)) { + + if (MAP_FAILED != c->file.mmap.start) { + munmap(c->file.mmap.start, c->file.mmap.length); + c->file.mmap.start = MAP_FAILED; + } + + /* Optimizations for the future: + * + * adaptive mem-mapping + * the problem: + * we mmap() the whole file. If someone has alot large files and + * 32-bit machine the virtual address area will be unrun and we + * will have a failing mmap() call. + * solution: + * only mmap 16M in one chunk and move the window as soon as we have + * finished the first 8M + * + * read-ahead buffering + * the problem: + * sending out several large files in parallel trashes read-ahead + * of the kernel leading to long wait-for-seek times. + * solutions: (increasing complexity) + * 1. use madvise + * 2. use a internal read-ahead buffer in the chunk-structure + * 3. use non-blocking IO for file-transfers + * */ + + c->file.mmap.offset = mmap_align_offset(offset); + + /* all mmap()ed areas are MMAP_CHUNK_SIZE + * except the last which might be smaller */ + c->file.mmap.length = MMAP_CHUNK_SIZE; + if (c->file.mmap.offset > file_end - (off_t)c->file.mmap.length) { + c->file.mmap.length = file_end - c->file.mmap.offset; + } + + c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, + MAP_SHARED, c->file.fd, c->file.mmap.offset); + if (MAP_FAILED == c->file.mmap.start) { + log_error_write(srv, __FILE__, __LINE__, "ssbdoo", "mmap failed:", + strerror(errno), c->mem, c->file.fd, + c->file.mmap.offset, (off_t) c->file.mmap.length); + return -1; + } + + #if defined(HAVE_MADVISE) + /* don't advise files < 64Kb */ + if (c->file.mmap.length > (64*1024)) { + /* darwin 7 is returning EINVAL all the time and I don't know how to + * detect this at runtime. + * + * ignore the return value for now */ + madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED); + } + #endif + } + + force_assert(offset >= c->file.mmap.offset); + mmap_offset = offset - c->file.mmap.offset; + force_assert(c->file.mmap.length > mmap_offset); + mmap_avail = c->file.mmap.length - mmap_offset; + if (toSend > (off_t) mmap_avail) toSend = mmap_avail; + + data = c->file.mmap.start + mmap_offset; + + /* setup SIGBUS handler, but don't activate sigbus_jmp_valid yet */ + if (0 == sigsetjmp(sigbus_jmp, 1)) { + signal(SIGBUS, sigbus_handler); + + sigbus_jmp_valid = 1; + r = network_write_data_len(fd, data, toSend); + sigbus_jmp_valid = 0; + } else { + sigbus_jmp_valid = 0; + + log_error_write(srv, __FILE__, __LINE__, "sbd", "SIGBUS in mmap:", + c->mem, c->file.fd); + + munmap(c->file.mmap.start, c->file.mmap.length); + c->file.mmap.start = MAP_FAILED; + return -1; + } + + if (r >= 0) { + *p_max_bytes -= r; + chunkqueue_mark_written(cq, r); + return (r > 0 && r == toSend) ? 0 : -3; + } else { + return network_write_error(srv, fd); + } +} + +#endif /* NETWORK_WRITE_USE_MMAP */ + + + + +#if defined(NETWORK_WRITE_USE_WRITEV) + +#if defined(HAVE_SYS_UIO_H) +# include <sys/uio.h> +#endif + +#if defined(UIO_MAXIOV) +# define SYS_MAX_CHUNKS UIO_MAXIOV +#elif defined(IOV_MAX) +/* new name for UIO_MAXIOV since IEEE Std 1003.1-2001 */ +# define SYS_MAX_CHUNKS IOV_MAX +#elif defined(_XOPEN_IOV_MAX) +/* minimum value for sysconf(_SC_IOV_MAX); posix requires this to be at least 16, which is good enough - no need to call sysconf() */ +# define SYS_MAX_CHUNKS _XOPEN_IOV_MAX +#else +# error neither UIO_MAXIOV nor IOV_MAX nor _XOPEN_IOV_MAX are defined +#endif + +/* allocate iovec[MAX_CHUNKS] on stack, so pick a sane limit: + * - each entry will use 1 pointer + 1 size_t + * - 32 chunks -> 256 / 512 bytes (32-bit/64-bit pointers) + */ +#define STACK_MAX_ALLOC_CHUNKS 32 +#if SYS_MAX_CHUNKS > STACK_MAX_ALLOC_CHUNKS +# define MAX_CHUNKS STACK_MAX_ALLOC_CHUNKS +#else +# define MAX_CHUNKS SYS_MAX_CHUNKS +#endif + +/* next chunk must be MEM_CHUNK. send multiple mem chunks using writev() */ +static int network_writev_mem_chunks(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) { + struct iovec chunks[MAX_CHUNKS]; + size_t num_chunks = 0; + off_t max_bytes = *p_max_bytes; + off_t toSend = 0; + ssize_t r; + + for (const chunk *c = cq->first; + NULL != c && MEM_CHUNK == c->type + && num_chunks < MAX_CHUNKS && toSend < max_bytes; + c = c->next) { + size_t c_len = buffer_string_length(c->mem); + force_assert(c->offset >= 0 && c->offset <= (off_t)c_len); + c_len -= c->offset; + if (c_len > 0) { + toSend += c_len; + + chunks[num_chunks].iov_base = c->mem->ptr + c->offset; + chunks[num_chunks].iov_len = c_len; + + ++num_chunks; + } + } + + if (0 == num_chunks) { + chunkqueue_remove_finished_chunks(cq); + return 0; + } + + r = writev(fd, chunks, num_chunks); + + if (r < 0) switch (errno) { + case EAGAIN: + case EINTR: + break; + case EPIPE: + case ECONNRESET: + return -2; + default: + log_error_write(srv, __FILE__, __LINE__, "ssd", + "writev failed:", strerror(errno), fd); + return -1; + } + + if (r >= 0) { + *p_max_bytes -= r; + chunkqueue_mark_written(cq, r); + } + + return (r > 0 && r == toSend) ? 0 : -3; +} + +#endif /* NETWORK_WRITE_USE_WRITEV */ + + + + +#if defined(NETWORK_WRITE_USE_SENDFILE) + +#if defined(NETWORK_WRITE_USE_LINUX_SENDFILE) \ + || defined(NETWORK_WRITE_USE_SOLARIS_SENDFILEV) +#include <sys/sendfile.h> +#endif + +#if defined(NETWORK_WRITE_USE_FREEBSD_SENDFILE) \ + || defined(NETWORK_WRITE_USE_DARWIN_SENDFILE) +#include <sys/uio.h> +#endif + +static int network_write_file_chunk_sendfile(server *srv, int fd, chunkqueue *cq, off_t *p_max_bytes) { + chunk * const c = cq->first; + ssize_t r; + off_t offset; + off_t toSend; + off_t written = 0; + + force_assert(c->offset >= 0 && c->offset <= c->file.length); + + offset = c->file.start + c->offset; + toSend = c->file.length - c->offset; + if (toSend > *p_max_bytes) toSend = *p_max_bytes; + + if (0 == toSend) { + chunkqueue_remove_finished_chunks(cq); + return 0; + } + + if (0 != chunkqueue_open_file_chunk(srv, cq)) return -1; + + /* Darwin, FreeBSD, and Solaris variants support iovecs and could + * be optimized to send more than just file in single syscall */ + + #if defined(NETWORK_WRITE_USE_LINUX_SENDFILE) + + r = sendfile(fd, c->file.fd, &offset, toSend); + if (r > 0) written = (off_t)r; + + #elif defined(NETWORK_WRITE_USE_DARWIN_SENDFILE) + + written = toSend; + r = sendfile(c->file.fd, fd, offset, &written, NULL, 0); + /* (for EAGAIN/EINTR written still contains the sent bytes) */ + + #elif defined(NETWORK_WRITE_USE_FREEBSD_SENDFILE) + + r = sendfile(c->file.fd, fd, offset, toSend, NULL, &written, 0); + /* (for EAGAIN/EINTR written still contains the sent bytes) */ + + #elif defined(NETWORK_WRITE_USE_SOLARIS_SENDFILEV) + { + sendfilevec_t fvec; + fvec.sfv_fd = c->file.fd; + fvec.sfv_flag = 0; + fvec.sfv_off = offset; + fvec.sfv_len = toSend; + + /* Solaris sendfilev() */ + r = sendfilev(fd, &fvec, 1, (size_t *)&written); + /* (for EAGAIN/EINTR written still contains the sent bytes) */ + } + #else + + r = -1; + errno = ENOSYS; + + #endif + + if (-1 == r) { + switch(errno) { + case EAGAIN: + case EINTR: + break; /* try again later */ + case EPIPE: + case ECONNRESET: + case ENOTCONN: + return -2; + case EINVAL: + case ENOSYS: + #if defined(ENOTSUP) && (!defined(EOPNOTSUPP) || EOPNOTSUPP != ENOTSUP) + case ENOTSUP: + #endif + #ifdef EOPNOTSUPP + case EOPNOTSUPP: + #endif + #ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: + #endif + #ifdef EAFNOSUPPORT + case EAFNOSUPPORT: + #endif + #ifdef NETWORK_WRITE_USE_MMAP + return network_write_file_chunk_mmap(srv, fd, cq, p_max_bytes); + #else + return network_write_file_chunk_no_mmap(srv, fd, cq, p_max_bytes); + #endif + default: + log_error_write(srv, __FILE__, __LINE__, "ssdSd", + "sendfile():", strerror(errno), errno, "fd:", fd); + return -1; + } + } + + if (written >= 0) { /*(always true)*/ + chunkqueue_mark_written(cq, written); + *p_max_bytes -= written; + } + + return (r >= 0 && written == toSend) ? 0 : -3; +} + +#endif + + + + +/* return values: + * >= 0 : no error + * -1 : error (on our side) + * -2 : remote close + */ + +static int network_write_chunkqueue_write(server *srv, int fd, chunkqueue *cq, off_t max_bytes) { + while (max_bytes > 0 && NULL != cq->first) { + int r = -1; + + switch (cq->first->type) { + case MEM_CHUNK: + r = network_write_mem_chunk(srv, fd, cq, &max_bytes); + break; + case FILE_CHUNK: + #ifdef NETWORK_WRITE_USE_MMAP + r = network_write_file_chunk_mmap(srv, fd, cq, &max_bytes); + #else + r = network_write_file_chunk_no_mmap(srv, fd, cq, &max_bytes); + #endif + break; + } + + if (-3 == r) return 0; + if (0 != r) return r; + } + + return 0; +} + +#if defined(NETWORK_WRITE_USE_WRITEV) +static int network_write_chunkqueue_writev(server *srv, int fd, chunkqueue *cq, off_t max_bytes) { + while (max_bytes > 0 && NULL != cq->first) { + int r = -1; + + switch (cq->first->type) { + case MEM_CHUNK: + #if defined(NETWORK_WRITE_USE_WRITEV) + r = network_writev_mem_chunks(srv, fd, cq, &max_bytes); + #else + r = network_write_mem_chunk(srv, fd, cq, &max_bytes); + #endif + break; + case FILE_CHUNK: + #ifdef NETWORK_WRITE_USE_MMAP + r = network_write_file_chunk_mmap(srv, fd, cq, &max_bytes); + #else + r = network_write_file_chunk_no_mmap(srv, fd, cq, &max_bytes); + #endif + break; + } + + if (-3 == r) return 0; + if (0 != r) return r; + } + + return 0; +} +#endif + +#if defined(NETWORK_WRITE_USE_SENDFILE) +static int network_write_chunkqueue_sendfile(server *srv, int fd, chunkqueue *cq, off_t max_bytes) { + while (max_bytes > 0 && NULL != cq->first) { + int r = -1; + + switch (cq->first->type) { + case MEM_CHUNK: + #if defined(NETWORK_WRITE_USE_WRITEV) + r = network_writev_mem_chunks(srv, fd, cq, &max_bytes); + #else + r = network_write_mem_chunk(srv, fd, cq, &max_bytes); + #endif + break; + case FILE_CHUNK: + #if defined(NETWORK_WRITE_USE_SENDFILE) + r = network_write_file_chunk_sendfile(srv, fd, cq, &max_bytes); + #elif defined(NETWORK_WRITE_USE_MMAP) + r = network_write_file_chunk_mmap(srv, fd, cq, &max_bytes); + #else + r = network_write_file_chunk_no_mmap(srv, fd, cq, &max_bytes); + #endif + break; + } + + if (-3 == r) return 0; + if (0 != r) return r; + } + + return 0; +} +#endif + +int network_write_init(server *srv) { + typedef enum { + NETWORK_BACKEND_UNSET, + NETWORK_BACKEND_WRITE, + NETWORK_BACKEND_WRITEV, + NETWORK_BACKEND_SENDFILE, + } network_backend_t; + + network_backend_t backend; + + struct nb_map { + network_backend_t nb; + const char *name; + } network_backends[] = { + /* lowest id wins */ + { NETWORK_BACKEND_SENDFILE, "sendfile" }, + { NETWORK_BACKEND_SENDFILE, "linux-sendfile" }, + { NETWORK_BACKEND_SENDFILE, "freebsd-sendfile" }, + { NETWORK_BACKEND_SENDFILE, "solaris-sendfilev" }, + { NETWORK_BACKEND_WRITEV, "writev" }, + { NETWORK_BACKEND_WRITE, "write" }, + { NETWORK_BACKEND_UNSET, NULL } + }; + + /* get a useful default */ + backend = network_backends[0].nb; + + /* match name against known types */ + if (!buffer_string_is_empty(srv->srvconf.network_backend)) { + const char *name; + for (size_t i = 0; NULL != (name = network_backends[i].name); ++i) { + if (0 == strcmp(srv->srvconf.network_backend->ptr, name)) { + backend = network_backends[i].nb; + break; + } + } + if (NULL == name) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "server.network-backend has an unknown value:", + srv->srvconf.network_backend); + return -1; + } + } + + switch(backend) { + case NETWORK_BACKEND_SENDFILE: + #if defined(NETWORK_WRITE_USE_SENDFILE) + srv->network_backend_write = network_write_chunkqueue_sendfile; + break; + #endif + case NETWORK_BACKEND_WRITEV: + #if defined(NETWORK_WRITE_USE_WRITEV) + srv->network_backend_write = network_write_chunkqueue_writev; + break; + #endif + case NETWORK_BACKEND_WRITE: + srv->network_backend_write = network_write_chunkqueue_write; + break; + default: + return -1; + } + + return 0; +} + +const char * network_write_show_handlers(void) { + return + "\nNetwork handler:\n\n" + #if defined NETWORK_WRITE_USE_LINUX_SENDFILE + "\t+ linux-sendfile\n" + #else + "\t- linux-sendfile\n" + #endif + #if defined NETWORK_WRITE_USE_FREEBSD_SENDFILE + "\t+ freebsd-sendfile\n" + #else + "\t- freebsd-sendfile\n" + #endif + #if defined NETWORK_WRITE_USE_DARWIN_SENDFILE + "\t+ darwin-sendfile\n" + #else + "\t- darwin-sendfile\n" + #endif + #if defined NETWORK_WRITE_USE_SOLARIS_SENDFILEV + "\t+ solaris-sendfilev\n" + #else + "\t- solaris-sendfilev\n" + #endif + #if defined NETWORK_WRITE_USE_WRITEV + "\t+ writev\n" + #else + "\t- writev\n" + #endif + "\t+ write\n" + #ifdef NETWORK_WRITE_USE_MMAP + "\t+ mmap support\n" + #else + "\t- mmap support\n" + #endif + ; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/network_write.h b/data/lighttpd/lighttpd-1.4.53/src/network_write.h new file mode 100644 index 000000000..a80cbcf89 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/network_write.h @@ -0,0 +1,9 @@ +#ifndef INCLUDED_NETWORK_WRITE_H +#define INCLUDED_NETWORK_WRITE_H +#include "first.h" +#include "base_decls.h" + +int network_write_init(server *srv); +const char * network_write_show_handlers(void); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/plugin.c b/data/lighttpd/lighttpd-1.4.53/src/plugin.c new file mode 100644 index 000000000..a509e4e65 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/plugin.c @@ -0,0 +1,544 @@ +#include "first.h" + +#include "plugin.h" +#include "base.h" +#include "log.h" + +#include <string.h> +#include <stdlib.h> + +#ifdef HAVE_VALGRIND_VALGRIND_H +# include <valgrind/valgrind.h> +#endif + +#if !defined(__WIN32) && !defined(LIGHTTPD_STATIC) +# include <dlfcn.h> +#endif +/* + * + * if you change this enum to add a new callback, be sure + * - that PLUGIN_FUNC_SIZEOF is the last entry + * - that you add PLUGIN_TO_SLOT twice: + * 1. as callback-dispatcher + * 2. in plugins_call_init() + * + */ + +typedef struct { + PLUGIN_DATA; +} plugin_data; + +typedef enum { + PLUGIN_FUNC_UNSET, + + PLUGIN_FUNC_HANDLE_URI_CLEAN, + PLUGIN_FUNC_HANDLE_URI_RAW, + PLUGIN_FUNC_HANDLE_REQUEST_ENV, + PLUGIN_FUNC_HANDLE_REQUEST_DONE, + PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT, + PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR, + PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, + PLUGIN_FUNC_HANDLE_TRIGGER, + PLUGIN_FUNC_HANDLE_SIGHUP, + PLUGIN_FUNC_HANDLE_WAITPID, + PLUGIN_FUNC_HANDLE_SUBREQUEST, + PLUGIN_FUNC_HANDLE_SUBREQUEST_START, + PLUGIN_FUNC_HANDLE_RESPONSE_START, + PLUGIN_FUNC_HANDLE_DOCROOT, + PLUGIN_FUNC_HANDLE_PHYSICAL, + PLUGIN_FUNC_CONNECTION_RESET, + PLUGIN_FUNC_INIT, + PLUGIN_FUNC_CLEANUP, + PLUGIN_FUNC_SET_DEFAULTS, + + PLUGIN_FUNC_SIZEOF +} plugin_t; + +static plugin *plugin_init(void) { + plugin *p; + + p = calloc(1, sizeof(*p)); + force_assert(NULL != p); + + return p; +} + +static void plugin_free(plugin *p) { +#if !defined(LIGHTTPD_STATIC) + int use_dlclose = 1; +#endif + + if (p->name) buffer_free(p->name); +#if defined(HAVE_VALGRIND_VALGRIND_H) && !defined(LIGHTTPD_STATIC) + /*if (RUNNING_ON_VALGRIND) use_dlclose = 0;*/ +#endif + +#if !defined(LIGHTTPD_STATIC) + if (use_dlclose && p->lib) { +#if defined(__WIN32) +) FreeLibrary(p->lib); +#else + dlclose(p->lib); +#endif + } +#endif + + free(p); +} + +static int plugins_register(server *srv, plugin *p) { + plugin **ps; + if (0 == srv->plugins.size) { + srv->plugins.size = 4; + srv->plugins.ptr = malloc(srv->plugins.size * sizeof(*ps)); + force_assert(NULL != srv->plugins.ptr); + srv->plugins.used = 0; + } else if (srv->plugins.used == srv->plugins.size) { + srv->plugins.size += 4; + srv->plugins.ptr = realloc(srv->plugins.ptr, srv->plugins.size * sizeof(*ps)); + force_assert(NULL != srv->plugins.ptr); + } + + ps = srv->plugins.ptr; + ps[srv->plugins.used++] = p; + + return 0; +} + +/** + * + * + * + */ + +#if defined(LIGHTTPD_STATIC) + +/* pre-declare functions, as there is no header for them */ +#define PLUGIN_INIT(x)\ + int x ## _plugin_init(plugin *p); + +#include "plugin-static.h" + +#undef PLUGIN_INIT + +/* build NULL-terminated table of name + init-function */ + +typedef struct { + const char* name; + int (*plugin_init)(plugin *p); +} plugin_load_functions; + +static const plugin_load_functions load_functions[] = { +#define PLUGIN_INIT(x) \ + { #x, &x ## _plugin_init }, + +#include "plugin-static.h" + + { NULL, NULL } +#undef PLUGIN_INIT +}; + +int plugins_load(server *srv) { + plugin *p; + size_t i, j; + + for (i = 0; i < srv->srvconf.modules->used; i++) { + data_string *d = (data_string *)srv->srvconf.modules->data[i]; + char *module = d->value->ptr; + + for (j = 0; j < i; j++) { + if (buffer_is_equal(d->value, ((data_string *) srv->srvconf.modules->data[j])->value)) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "Cannot load plugin", d->value, + "more than once, please fix your config (lighttpd may not accept such configs in future releases)"); + continue; + } + } + + for (j = 0; load_functions[j].name; ++j) { + if (0 == strcmp(load_functions[j].name, module)) { + p = plugin_init(); + if ((*load_functions[j].plugin_init)(p)) { + log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin init failed" ); + plugin_free(p); + return -1; + } + plugins_register(srv, p); + break; + } + } + if (!load_functions[j].name) { + log_error_write(srv, __FILE__, __LINE__, "ss", module, " plugin not found" ); + return -1; + } + } + + return 0; +} +#else /* defined(LIGHTTPD_STATIC) */ +int plugins_load(server *srv) { + plugin *p; + int (*init)(plugin *pl); + size_t i, j; + + for (i = 0; i < srv->srvconf.modules->used; i++) { + data_string *d = (data_string *)srv->srvconf.modules->data[i]; + char *module = d->value->ptr; + + for (j = 0; j < i; j++) { + if (buffer_is_equal(d->value, ((data_string *) srv->srvconf.modules->data[j])->value)) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "Cannot load plugin", d->value, + "more than once, please fix your config (lighttpd may not accept such configs in future releases)"); + continue; + } + } + + buffer_copy_buffer(srv->tmp_buf, srv->srvconf.modules_dir); + + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("/")); + buffer_append_string(srv->tmp_buf, module); +#if defined(__WIN32) || defined(__CYGWIN__) + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".dll")); +#else + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN(".so")); +#endif + + p = plugin_init(); +#ifdef __WIN32 + if (NULL == (p->lib = LoadLibrary(srv->tmp_buf->ptr))) { + LPVOID lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL); + + log_error_write(srv, __FILE__, __LINE__, "ssb", "LoadLibrary() failed", + lpMsgBuf, srv->tmp_buf); + + plugin_free(p); + + return -1; + + } +#else + if (NULL == (p->lib = dlopen(srv->tmp_buf->ptr, RTLD_NOW|RTLD_GLOBAL))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", "dlopen() failed for:", + srv->tmp_buf, dlerror()); + + plugin_free(p); + + return -1; + } + +#endif + buffer_copy_string(srv->tmp_buf, module); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("_plugin_init")); + +#ifdef __WIN32 + init = GetProcAddress(p->lib, srv->tmp_buf->ptr); + + if (init == NULL) { + LPVOID lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL); + + log_error_write(srv, __FILE__, __LINE__, "sbs", "getprocaddress failed:", srv->tmp_buf, lpMsgBuf); + + plugin_free(p); + return -1; + } + +#else +#if 1 + init = (int (*)(plugin *))(intptr_t)dlsym(p->lib, srv->tmp_buf->ptr); +#else + *(void **)(&init) = dlsym(p->lib, srv->tmp_buf->ptr); +#endif + if (NULL == init) { + const char *error = dlerror(); + if (error != NULL) { + log_error_write(srv, __FILE__, __LINE__, "ss", "dlsym:", error); + } else { + log_error_write(srv, __FILE__, __LINE__, "ss", "dlsym symbol not found:", srv->tmp_buf->ptr); + } + + plugin_free(p); + return -1; + } + +#endif + if ((*init)(p)) { + log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin init failed" ); + + plugin_free(p); + return -1; + } +#if 0 + log_error_write(srv, __FILE__, __LINE__, "ss", module, "plugin loaded" ); +#endif + plugins_register(srv, p); + } + + return 0; +} +#endif /* defined(LIGHTTPD_STATIC) */ + +#define PLUGIN_TO_SLOT(x, y) \ + handler_t plugins_call_##y(server *srv, connection *con) {\ + plugin **slot;\ + size_t j;\ + slot = ((plugin ***)(srv->plugin_slots))[x];\ + if (!slot) return HANDLER_GO_ON;\ + for (j = 0; j < srv->plugins.used && slot[j]; j++) { \ + plugin *p = slot[j];\ + handler_t r;\ + switch(r = p->y(srv, con, p->data)) {\ + case HANDLER_GO_ON:\ + break;\ + case HANDLER_FINISHED:\ + case HANDLER_COMEBACK:\ + case HANDLER_WAIT_FOR_EVENT:\ + case HANDLER_WAIT_FOR_FD:\ + case HANDLER_ERROR:\ + return r;\ + default:\ + log_error_write(srv, __FILE__, __LINE__, "sbs", #x, p->name, "unknown state");\ + return HANDLER_ERROR;\ + }\ + }\ + return HANDLER_GO_ON;\ + } + +/** + * plugins that use + * + * - server *srv + * - connection *con + * - void *p_d (plugin_data *) + */ + +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean) +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw) +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_ENV, handle_request_env) +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done) +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT, handle_connection_accept) +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR, handle_connection_shut_wr) +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close) +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest) +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start) +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_START, handle_response_start) +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot) +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical) +PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset) + +#undef PLUGIN_TO_SLOT + +#define PLUGIN_TO_SLOT(x, y) \ + handler_t plugins_call_##y(server *srv) {\ + plugin **slot;\ + size_t j;\ + if (!srv->plugin_slots) return HANDLER_GO_ON;\ + slot = ((plugin ***)(srv->plugin_slots))[x];\ + if (!slot) return HANDLER_GO_ON;\ + for (j = 0; j < srv->plugins.used && slot[j]; j++) { \ + plugin *p = slot[j];\ + handler_t r;\ + switch(r = p->y(srv, p->data)) {\ + case HANDLER_GO_ON:\ + break;\ + case HANDLER_FINISHED:\ + case HANDLER_COMEBACK:\ + case HANDLER_WAIT_FOR_EVENT:\ + case HANDLER_WAIT_FOR_FD:\ + case HANDLER_ERROR:\ + return r;\ + default:\ + log_error_write(srv, __FILE__, __LINE__, "sbsd", #x, p->name, "unknown state:", r);\ + return HANDLER_ERROR;\ + }\ + }\ + return HANDLER_GO_ON;\ + } + +/** + * plugins that use + * + * - server *srv + * - void *p_d (plugin_data *) + */ + +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger) +PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup) +PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup) +PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults) + +#undef PLUGIN_TO_SLOT + +handler_t plugins_call_handle_waitpid(server *srv, pid_t pid, int status) { + plugin ** const slot = + ((plugin ***)(srv->plugin_slots))[PLUGIN_FUNC_HANDLE_WAITPID]; + if (!slot) return HANDLER_GO_ON; + for (size_t i = 0; i < srv->plugins.used && slot[i]; ++i) { + plugin *p = slot[i]; + handler_t r = p->handle_waitpid(srv, p->data, pid, status); + if (r != HANDLER_GO_ON) return r; + } + return HANDLER_GO_ON; +} + +#if 0 +/** + * + * special handler + * + */ +handler_t plugins_call_handle_fdevent(server *srv, const fd_conn *fdc) { + size_t i; + plugin **ps; + + ps = srv->plugins.ptr; + + for (i = 0; i < srv->plugins.used; i++) { + plugin *p = ps[i]; + if (p->handle_fdevent) { + handler_t r; + switch(r = p->handle_fdevent(srv, fdc, p->data)) { + case HANDLER_GO_ON: + break; + case HANDLER_FINISHED: + case HANDLER_COMEBACK: + case HANDLER_WAIT_FOR_EVENT: + case HANDLER_ERROR: + return r; + default: + log_error_write(srv, __FILE__, __LINE__, "d", r); + break; + } + } + } + + return HANDLER_GO_ON; +} +#endif +/** + * + * - call init function of all plugins to init the plugin-internals + * - added each plugin that supports has callback to the corresponding slot + * + * - is only called once. + */ + +handler_t plugins_call_init(server *srv) { + size_t i; + plugin **ps; + + ps = srv->plugins.ptr; + + /* fill slots */ + + srv->plugin_slots = calloc(PLUGIN_FUNC_SIZEOF, sizeof(ps)); + force_assert(NULL != srv->plugin_slots); + + for (i = 0; i < srv->plugins.used; i++) { + size_t j; + /* check which calls are supported */ + + plugin *p = ps[i]; + +#define PLUGIN_TO_SLOT(x, y) \ + if (p->y) { \ + plugin **slot = ((plugin ***)(srv->plugin_slots))[x]; \ + if (!slot) { \ + slot = calloc(srv->plugins.used, sizeof(*slot));\ + force_assert(NULL != slot); \ + ((plugin ***)(srv->plugin_slots))[x] = slot; \ + } \ + for (j = 0; j < srv->plugins.used; j++) { \ + if (slot[j]) continue;\ + slot[j] = p;\ + break;\ + }\ + } + + + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_CLEAN, handle_uri_clean); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_URI_RAW, handle_uri_raw); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_ENV, handle_request_env); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_REQUEST_DONE, handle_request_done); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_ACCEPT, handle_connection_accept); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_SHUT_WR, handle_connection_shut_wr); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_WAITPID, handle_waitpid); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST, handle_subrequest); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SUBREQUEST_START, handle_subrequest_start); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_START, handle_response_start); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_DOCROOT, handle_docroot); + PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical); + PLUGIN_TO_SLOT(PLUGIN_FUNC_CONNECTION_RESET, connection_reset); + PLUGIN_TO_SLOT(PLUGIN_FUNC_CLEANUP, cleanup); + PLUGIN_TO_SLOT(PLUGIN_FUNC_SET_DEFAULTS, set_defaults); +#undef PLUGIN_TO_SLOT + + if (p->init) { + if (NULL == (p->data = p->init())) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "plugin-init failed for module", p->name); + return HANDLER_ERROR; + } + + /* used for con->mode, DIRECT == 0, plugins above that */ + ((plugin_data *)(p->data))->id = i + 1; + + if (p->version != LIGHTTPD_VERSION_ID) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "plugin-version doesn't match lighttpd-version for", p->name); + return HANDLER_ERROR; + } + } else { + p->data = NULL; + } + + if (p->priv_defaults && HANDLER_ERROR==p->priv_defaults(srv, p->data)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +void plugins_free(server *srv) { + size_t i; + plugins_call_cleanup(srv); + + for (i = 0; i < srv->plugins.used; i++) { + plugin *p = ((plugin **)srv->plugins.ptr)[i]; + + plugin_free(p); + } + + for (i = 0; srv->plugin_slots && i < PLUGIN_FUNC_SIZEOF; i++) { + plugin **slot = ((plugin ***)(srv->plugin_slots))[i]; + + if (slot) free(slot); + } + + free(srv->plugin_slots); + srv->plugin_slots = NULL; + + free(srv->plugins.ptr); + srv->plugins.ptr = NULL; + srv->plugins.used = 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/plugin.h b/data/lighttpd/lighttpd-1.4.53/src/plugin.h new file mode 100644 index 000000000..97961a9b6 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/plugin.h @@ -0,0 +1,96 @@ +#ifndef _PLUGIN_H_ +#define _PLUGIN_H_ +#include "first.h" + +#include "base_decls.h" +#include "buffer.h" +#include "array.h" +#include "configfile.h" + +#define SERVER_FUNC(x) \ + static handler_t x(server *srv, void *p_d) + +#define CONNECTION_FUNC(x) \ + static handler_t x(server *srv, connection *con, void *p_d) + +#define INIT_FUNC(x) \ + static void *x(void) + +#define FREE_FUNC SERVER_FUNC +#define TRIGGER_FUNC SERVER_FUNC +#define SETDEFAULTS_FUNC SERVER_FUNC +#define SIGHUP_FUNC SERVER_FUNC + +#define SUBREQUEST_FUNC CONNECTION_FUNC +#define PHYSICALPATH_FUNC CONNECTION_FUNC +#define REQUESTDONE_FUNC CONNECTION_FUNC +#define URIHANDLER_FUNC CONNECTION_FUNC + +#define PLUGIN_DATA size_t id + +typedef struct { + size_t version; + + buffer *name; /* name of the plugin */ + + void *(* init) (); + handler_t (* priv_defaults) (server *srv, void *p_d); + handler_t (* set_defaults) (server *srv, void *p_d); + handler_t (* cleanup) (server *srv, void *p_d); + /* is called ... */ + handler_t (* handle_trigger) (server *srv, void *p_d); /* once a second */ + handler_t (* handle_sighup) (server *srv, void *p_d); /* at a sighup */ + handler_t (* handle_waitpid) (server *srv, void *p_d, pid_t pid, int status); /* upon a child process exit */ + + handler_t (* handle_uri_raw) (server *srv, connection *con, void *p_d); /* after uri_raw is set */ + handler_t (* handle_uri_clean) (server *srv, connection *con, void *p_d); /* after uri is set */ + handler_t (* handle_docroot) (server *srv, connection *con, void *p_d); /* getting the document-root */ + handler_t (* handle_physical) (server *srv, connection *con, void *p_d); /* mapping url to physical path */ + handler_t (* handle_request_env) (server *srv, connection *con, void *p_d); /* (deferred env populate) */ + handler_t (* handle_request_done) (server *srv, connection *con, void *p_d); /* at the end of a request */ + handler_t (* handle_connection_accept) (server *srv, connection *con, void *p_d); /* after accept() socket */ + handler_t (* handle_connection_shut_wr)(server *srv, connection *con, void *p_d); /* done writing to socket */ + handler_t (* handle_connection_close) (server *srv, connection *con, void *p_d); /* before close() of socket */ + + + + handler_t (* handle_subrequest_start)(server *srv, connection *con, void *p_d); + + /* when a handler for the request + * has to be found + */ + handler_t (* handle_subrequest) (server *srv, connection *con, void *p_d); /* */ + handler_t (* handle_response_start) (server *srv, connection *con, void *p_d); /* before response headers are written */ + handler_t (* connection_reset) (server *srv, connection *con, void *p_d); /* after request done or request abort */ + void *data; + + /* dlopen handle */ + void *lib; +} plugin; + +int plugins_load(server *srv); +void plugins_free(server *srv); + +handler_t plugins_call_handle_uri_raw(server *srv, connection *con); +handler_t plugins_call_handle_uri_clean(server *srv, connection *con); +handler_t plugins_call_handle_subrequest_start(server *srv, connection *con); +handler_t plugins_call_handle_subrequest(server *srv, connection *con); +handler_t plugins_call_handle_response_start(server *srv, connection *con); +handler_t plugins_call_handle_request_env(server *srv, connection *con); +handler_t plugins_call_handle_request_done(server *srv, connection *con); +handler_t plugins_call_handle_docroot(server *srv, connection *con); +handler_t plugins_call_handle_physical(server *srv, connection *con); +handler_t plugins_call_handle_connection_accept(server *srv, connection *con); +handler_t plugins_call_handle_connection_shut_wr(server *srv, connection *con); +handler_t plugins_call_handle_connection_close(server *srv, connection *con); +handler_t plugins_call_connection_reset(server *srv, connection *con); + +handler_t plugins_call_handle_trigger(server *srv); +handler_t plugins_call_handle_sighup(server *srv); +handler_t plugins_call_handle_waitpid(server *srv, pid_t pid, int status); + +handler_t plugins_call_init(server *srv); +handler_t plugins_call_set_defaults(server *srv); +handler_t plugins_call_cleanup(server *srv); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/rand.c b/data/lighttpd/lighttpd-1.4.53/src/rand.c new file mode 100644 index 000000000..af24d46d7 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/rand.c @@ -0,0 +1,232 @@ +#include "first.h" + +#include "rand.h" +#include "base.h" +#include "fdevent.h" +#include "safe_memclear.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "sys-crypto.h" +#ifdef USE_OPENSSL_CRYPTO +#include <openssl/opensslv.h> /* OPENSSL_VERSION_NUMBER */ +#include <openssl/rand.h> +#endif +#ifdef HAVE_GETENTROPY +#include <sys/random.h> +#endif +#ifdef HAVE_LINUX_RANDOM_H +#include <sys/syscall.h> +#include <linux/random.h> +#endif +#ifdef RNDGETENTCNT +#include <sys/ioctl.h> +#endif + +/* Take some reasonable steps to attempt to *seed* random number generators with + * cryptographically random data. Some of these initialization routines may + * block, and are intended to be called only at startup in lighttpd, or + * immediately after fork() to start lighttpd workers. + * + * Update: li_rand_init() is now deferred until first use so that installations + * that do not use modules which use these routines do need to potentially block + * at startup. Current use by core lighttpd modules is in mod_auth HTTP Digest + * auth and in mod_usertrack. Deferring collection of random data until first + * use may allow sufficient entropy to be collected by kernel before first use, + * helping reduce or avoid situations in low-entropy-generating embedded devices + * which might otherwise block lighttpd for minutes at device startup. + * Further discussion in https://redmine.lighttpd.net/boards/2/topics/6981 + * + * Note: results from li_rand_pseudo_bytes() are not necessarily + * cryptographically random and must not be used for purposes such + * as key generation which require cryptographic randomness. + * + * https://wiki.openssl.org/index.php/Random_Numbers + * https://wiki.openssl.org/index.php/Random_fork-safety + * + * openssl random number generators are not thread-safe by default + * https://wiki.openssl.org/index.php/Manual:Threads(3) + * + * RFE: add more paranoid checks from the following to improve confidence: + * http://insanecoding.blogspot.co.uk/2014/05/a-good-idea-with-bad-usage-devurandom.html + * RFE: retry on EINTR + * RFE: check RAND_status() + */ + +static int li_getentropy (void *buf, size_t buflen) +{ + #ifdef HAVE_GETENTROPY + return getentropy(buf, buflen); + #else + /*(see NOTES section in 'man getrandom' on Linux)*/ + #if defined(HAVE_GETRANDOM) || defined(SYS_getrandom) + if (buflen <= 256) { + #ifdef HAVE_GETRANDOM /*(not implemented in glibc yet)*/ + int num = getrandom(buf, buflen, 0); + #elif defined(SYS_getrandom) + /* https://lwn.net/Articles/605828/ */ + /* https://bbs.archlinux.org/viewtopic.php?id=200039 */ + int num = (int)syscall(SYS_getrandom, buf, buflen, 0); + #endif + if (num == (int)buflen) return 0; + if (num < 0) return num; /* -1 */ + } + #else + UNUSED(buf); + UNUSED(buflen); + #endif + errno = EIO; + return -1; + #endif +} + +static int li_rand_device_bytes (unsigned char *buf, int num) +{ + /* randomness from these devices is cryptographically strong, + * unless /dev/urandom is low on entropy */ + + static const char * const devices[] = { + #ifdef __OpenBSD__ + "/dev/arandom", + #endif + "/dev/urandom", + "/dev/random" + }; + + /* device files might not be available in chroot environment, + * so prefer syscall, if available */ + if (0 == li_getentropy(buf, (size_t)num)) return 1; + + for (unsigned int u = 0; u < sizeof(devices)/sizeof(devices[0]); ++u) { + /*(some systems might have symlink to another device; omit O_NOFOLLOW)*/ + int fd = fdevent_open_cloexec(devices[u], O_RDONLY, 0); + if (fd >= 0) { + ssize_t rd = 0; + #ifdef RNDGETENTCNT + int entropy; + if (0 == ioctl(fd, (unsigned long)(RNDGETENTCNT), &entropy) + && entropy >= num*8) + #endif + rd = read(fd, buf, (size_t)num); + close(fd); + if (rd == num) { + return 1; + } + } + } + + return 0; +} + +static int li_rand_inited; +static unsigned short xsubi[3]; + +static void li_rand_init (void) +{ + /* (intended to be called at init and after fork() in order to re-seed PRNG + * so that forked children, grandchildren, etc do not share PRNG seed) + * https://github.com/ramsey/uuid/issues/80 + * https://www.agwa.name/blog/post/libressls_prng_is_unsafe_on_linux + * (issue in early version of libressl has since been fixed) + * https://github.com/libressl-portable/portable/commit/32d9eeeecf4e951e1566d5f4a42b36ea37b60f35 + */ + unsigned int u; + li_rand_inited = 1; + if (1 == li_rand_device_bytes((unsigned char *)xsubi, (int)sizeof(xsubi))) { + u = ((unsigned int)xsubi[0] << 16) | xsubi[1]; + } + else { + #ifdef HAVE_ARC4RANDOM_BUF + u = arc4random(); + arc4random_buf(xsubi, sizeof(xsubi)); + #else + /* NOTE: not cryptographically random !!! */ + srand((unsigned int)(time(NULL) ^ getpid())); + for (u = 0; u < sizeof(unsigned short); ++u) + /* coverity[dont_call : FALSE] */ + xsubi[u] = (unsigned short)(rand() & 0xFFFF); + u = ((unsigned int)xsubi[0] << 16) | xsubi[1]; + #endif + } + srand(u); /*(initialize just in case rand() used elsewhere)*/ + #ifdef HAVE_SRANDOM + srandom(u); /*(initialize just in case random() used elsewhere)*/ + #endif + #ifdef USE_OPENSSL_CRYPTO + RAND_poll(); + RAND_seed(xsubi, (int)sizeof(xsubi)); + #endif +} + +void li_rand_reseed (void) +{ + if (li_rand_inited) li_rand_init(); +} + +int li_rand_pseudo (void) +{ + /* randomness *is not* cryptographically strong */ + /* (attempt to use better mechanisms to replace the more portable rand()) */ + #ifdef USE_OPENSSL_CRYPTO /* (openssl 1.1.0 deprecates RAND_pseudo_bytes()) */ + #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + int i; + if (-1 != RAND_pseudo_bytes((unsigned char *)&i, sizeof(i))) return i; + #endif + #endif + if (!li_rand_inited) li_rand_init(); + #ifdef HAVE_ARC4RANDOM_BUF + return (int)arc4random(); + #elif defined(HAVE_SRANDOM) + /* coverity[dont_call : FALSE] */ + return (int)random(); + #elif defined(HAVE_JRAND48) + /*(FYI: jrand48() reentrant, but use of file-scoped static xsubi[] is not)*/ + /* coverity[dont_call : FALSE] */ + return (int)jrand48(xsubi); + #else + /* coverity[dont_call : FALSE] */ + return rand(); + #endif +} + +void li_rand_pseudo_bytes (unsigned char *buf, int num) +{ + for (int i = 0; i < num; ++i) + buf[i] = li_rand_pseudo() & 0xFF; +} + +int li_rand_bytes (unsigned char *buf, int num) +{ + #ifdef USE_OPENSSL_CRYPTO + int rc = RAND_bytes(buf, num); + if (-1 != rc) { + return rc; + } + #endif + if (1 == li_rand_device_bytes(buf, num)) { + return 1; + } + else { + /* NOTE: not cryptographically random !!! */ + li_rand_pseudo_bytes(buf, num); + /*(openssl RAND_pseudo_bytes rc for non-cryptographically random data)*/ + return 0; + } +} + +void li_rand_cleanup (void) +{ + #ifdef USE_OPENSSL_CRYPTO + #if OPENSSL_VERSION_NUMBER < 0x10100000L + RAND_cleanup(); + #endif + #endif + safe_memclear(xsubi, sizeof(xsubi)); +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/rand.h b/data/lighttpd/lighttpd-1.4.53/src/rand.h new file mode 100644 index 000000000..d80f6a12a --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/rand.h @@ -0,0 +1,11 @@ +#ifndef LI_RAND_H_ +#define LI_RAND_H_ +#include "first.h" + +int li_rand_pseudo (void); +void li_rand_pseudo_bytes (unsigned char *buf, int num); +void li_rand_reseed (void); +int li_rand_bytes (unsigned char *buf, int num); +void li_rand_cleanup (void); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/request.c b/data/lighttpd/lighttpd-1.4.53/src/request.c new file mode 100644 index 000000000..f2d3c5276 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/request.c @@ -0,0 +1,1201 @@ +#include "first.h" + +#include "request.h" +#include "base.h" +#include "burl.h" +#include "http_header.h" +#include "http_kv.h" +#include "log.h" +#include "sock_addr.h" + +#include <sys/stat.h> + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <sys-strings.h> + +static int request_check_hostname(buffer *host) { + enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL; + size_t i; + int label_len = 0; + size_t host_len, hostport_len; + char *colon; + int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */ + int level = 0; + + /* + * hostport = host [ ":" port ] + * host = hostname | IPv4address | IPv6address + * hostname = *( domainlabel "." ) toplabel [ "." ] + * domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum + * toplabel = alpha | alpha *( alphanum | "-" ) alphanum + * IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit + * IPv6address = "[" ... "]" + * port = *digit + */ + + /* IPv6 adress */ + if (host->ptr[0] == '[') { + char *c = host->ptr + 1; + int colon_cnt = 0; + + /* check the address inside [...] */ + for (; *c && *c != ']'; c++) { + if (*c == ':') { + if (++colon_cnt > 7) { + return -1; + } + } else if (!light_isxdigit(*c) && '.' != *c) { + return -1; + } + } + + /* missing ] */ + if (!*c) { + return -1; + } + + /* check port */ + if (*(c+1) == ':') { + for (c += 2; *c; c++) { + if (!light_isdigit(*c)) { + return -1; + } + } + } + else if ('\0' != *(c+1)) { + /* only a port is allowed to follow [...] */ + return -1; + } + return 0; + } + + hostport_len = host_len = buffer_string_length(host); + + if (NULL != (colon = memchr(host->ptr, ':', host_len))) { + char *c = colon + 1; + + /* check portnumber */ + for (; *c; c++) { + if (!light_isdigit(*c)) return -1; + } + + /* remove the port from the host-len */ + host_len = colon - host->ptr; + } + + /* Host is empty */ + if (host_len == 0) return -1; + + /* if the hostname ends in a "." strip it */ + if (host->ptr[host_len-1] == '.') { + /* shift port info one left */ + if (NULL != colon) memmove(colon-1, colon, hostport_len - host_len); + buffer_string_set_length(host, --hostport_len); + if (--host_len == 0) return -1; + } + + + /* scan from the right and skip the \0 */ + for (i = host_len; i-- > 0; ) { + const char c = host->ptr[i]; + + switch (stage) { + case TOPLABEL: + if (c == '.') { + /* only switch stage, if this is not the last character */ + if (i != host_len - 1) { + if (label_len == 0) { + return -1; + } + + /* check the first character at right of the dot */ + if (is_ip == 0) { + if (!light_isalnum(host->ptr[i+1])) { + return -1; + } + } else if (!light_isdigit(host->ptr[i+1])) { + is_ip = 0; + } else if ('-' == host->ptr[i+1]) { + return -1; + } else { + /* just digits */ + is_ip = 1; + } + + stage = DOMAINLABEL; + + label_len = 0; + level++; + } else if (i == 0) { + /* just a dot and nothing else is evil */ + return -1; + } + } else if (i == 0) { + /* the first character of the hostname */ + if (!light_isalnum(c)) { + return -1; + } + label_len++; + } else { + if (c != '-' && !light_isalnum(c)) { + return -1; + } + if (is_ip == -1) { + if (!light_isdigit(c)) is_ip = 0; + } + label_len++; + } + + break; + case DOMAINLABEL: + if (is_ip == 1) { + if (c == '.') { + if (label_len == 0) { + return -1; + } + + label_len = 0; + level++; + } else if (!light_isdigit(c)) { + return -1; + } else { + label_len++; + } + } else { + if (c == '.') { + if (label_len == 0) { + return -1; + } + + /* c is either - or alphanum here */ + if ('-' == host->ptr[i+1]) { + return -1; + } + + label_len = 0; + level++; + } else if (i == 0) { + if (!light_isalnum(c)) { + return -1; + } + label_len++; + } else { + if (c != '-' && !light_isalnum(c)) { + return -1; + } + label_len++; + } + } + + break; + } + } + + /* a IP has to consist of 4 parts */ + if (is_ip == 1 && level != 3) { + return -1; + } + + if (label_len == 0) { + return -1; + } + + return 0; +} + +int http_request_host_normalize(buffer *b, int scheme_port) { + /* + * check for and canonicalize numeric IP address and portnum (optional) + * (IP address may be followed by ":portnum" (optional)) + * - IPv6: "[...]" + * - IPv4: "x.x.x.x" + * - IPv4: 12345678 (32-bit decimal number) + * - IPv4: 012345678 (32-bit octal number) + * - IPv4: 0x12345678 (32-bit hex number) + * + * allow any chars (except ':' and '\0' and stray '[' or ']') + * (other code may check chars more strictly or more pedantically) + * ':' delimits (optional) port at end of string + * "[]" wraps IPv6 address literal + * '\0' should have been rejected earlier were it present + * + * any chars includes, but is not limited to: + * - allow '-' any where, even at beginning of word + * (security caution: might be confused for cmd flag if passed to shell) + * - allow all-digit TLDs + * (might be mistaken for IPv4 addr by inet_aton() + * unless non-digits appear in subdomain) + */ + + /* Note: not using getaddrinfo() since it does not support "[]" around IPv6 + * and is not as lenient as inet_aton() and inet_addr() for IPv4 strings. + * Not using inet_pton() (when available) on IPv4 for similar reasons. */ + + const char * const p = b->ptr; + const size_t blen = buffer_string_length(b); + long port = 0; + + if (*p != '[') { + char * const colon = (char *)memchr(p, ':', blen); + if (colon) { + if (*p == ':') return -1; /*(empty host then port, or naked IPv6)*/ + if (colon[1] != '\0') { + char *e; + port = strtol(colon+1, &e, 0); /*(allow decimal, octal, hex)*/ + if (0 < port && port <= USHRT_MAX && *e == '\0') { + /* valid port */ + } else { + return -1; + } + } /*(else ignore stray colon at string end)*/ + buffer_string_set_length(b, (size_t)(colon - p)); /*(remove port str)*/ + } + + if (light_isdigit(*p)) do { + /* (IPv4 address literal or domain starting w/ digit (e.g. 3com))*/ + /* (check one-element cache of normalized IPv4 address string) */ + static struct { char s[INET_ADDRSTRLEN]; size_t n; } laddr; + size_t n = colon ? (size_t)(colon - p) : blen; + sock_addr addr; + if (n == laddr.n && 0 == memcmp(p, laddr.s, n)) break; + if (1 == sock_addr_inet_pton(&addr, p, AF_INET, 0)) { + sock_addr_inet_ntop_copy_buffer(b, &addr); + n = buffer_string_length(b); + if (n < sizeof(laddr.s)) memcpy(laddr.s, b->ptr, (laddr.n = n)); + } + } while (0); + } else do { /* IPv6 addr */ + #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) + + /* (check one-element cache of normalized IPv4 address string) */ + static struct { char s[INET6_ADDRSTRLEN]; size_t n; } laddr; + sock_addr addr; + char *bracket = b->ptr+blen-1; + char *percent = strchr(b->ptr+1, '%'); + size_t len; + int rc; + char buf[INET6_ADDRSTRLEN+16]; /*(+16 for potential %interface name)*/ + if (blen <= 2) return -1; /*(invalid "[]")*/ + if (*bracket != ']') { + bracket = (char *)memchr(b->ptr+1, ']', blen-1); + if (NULL == bracket || bracket[1] != ':' || bracket - b->ptr == 1){ + return -1; + } + if (bracket[2] != '\0') { /*(ignore stray colon at string end)*/ + char *e; + port = strtol(bracket+2, &e, 0); /*(allow decimal, octal, hex)*/ + if (0 < port && port <= USHRT_MAX && *e == '\0') { + /* valid port */ + } else { + return -1; + } + } + } + + len = (size_t)((percent ? percent : bracket) - (b->ptr+1)); + if (laddr.n == len && 0 == memcmp(laddr.s, b->ptr+1, len)) { + /* truncate after ']' and re-add normalized port, if needed */ + buffer_string_set_length(b, (size_t)(bracket - b->ptr + 1)); + break; + } + + *bracket = '\0';/*(terminate IPv6 string)*/ + if (percent) *percent = '\0'; /*(remove %interface from address)*/ + rc = sock_addr_inet_pton(&addr, b->ptr+1, AF_INET6, 0); + if (percent) *percent = '%'; /*(restore %interface)*/ + *bracket = ']'; /*(restore bracket)*/ + if (1 != rc) return -1; + + sock_addr_inet_ntop(&addr, buf, sizeof(buf)); + len = strlen(buf); + if (percent) { + if (percent > bracket) return -1; + if (len + (size_t)(bracket - percent) >= sizeof(buf)) return -1; + if (len < sizeof(laddr.s)) memcpy(laddr.s, buf, (laddr.n = len)); + memcpy(buf+len, percent, (size_t)(bracket - percent)); + len += (size_t)(bracket - percent); + } + buffer_string_set_length(b, 1); /* truncate after '[' */ + buffer_append_string_len(b, buf, len); + buffer_append_string_len(b, CONST_STR_LEN("]")); + + #else + + return -1; + + #endif + } while (0); + + if (0 != port && port != scheme_port) { + buffer_append_string_len(b, CONST_STR_LEN(":")); + buffer_append_int(b, (int)port); + } + + return 0; +} + +static int scheme_port (const buffer *scheme) +{ + return buffer_is_equal_string(scheme, CONST_STR_LEN("https")) ? 443 : 80; +} + +int http_request_host_policy (connection *con, buffer *b, const buffer *scheme) { + return (((con->conf.http_parseopts & HTTP_PARSEOPT_HOST_STRICT) + && 0 != request_check_hostname(b)) + || ((con->conf.http_parseopts & HTTP_PARSEOPT_HOST_NORMALIZE) + && 0 != http_request_host_normalize(b, scheme_port(scheme)))); +} + +static int http_request_split_value(array *vals, const char *current, size_t len) { + int state = 0; + const char *token_start = NULL, *token_end = NULL; + /* + * parse + * + * val1, val2, val3, val4 + * + * into a array (more or less a explode() incl. stripping of whitespaces + */ + + for (size_t i = 0; i <= len; ++i, ++current) { + switch (state) { + case 0: /* find start of a token */ + switch (*current) { + case ' ': + case '\t': /* skip white space */ + case ',': /* skip empty token */ + break; + case '\0': /* end of string */ + return 0; + default: + /* found real data, switch to state 1 to find the end of the token */ + token_start = token_end = current; + state = 1; + break; + } + break; + case 1: /* find end of token and last non white space character */ + switch (*current) { + case ' ': + case '\t': + /* space - don't update token_end */ + break; + case ',': + case '\0': /* end of string also marks the end of a token */ + array_insert_value(vals, token_start, token_end-token_start+1); + state = 0; + break; + default: + /* no white space, update token_end to include current character */ + token_end = current; + break; + } + break; + } + } + + return 0; +} + +static int request_uri_is_valid_char(unsigned char c) { + if (c <= 32) return 0; + if (c == 127) return 0; + if (c == 255) return 0; + + return 1; +} + +static void http_request_missing_CR_before_LF(server *srv, connection *con) { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "missing CR before LF in header -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); + } +} + +enum keep_alive_set { + HTTP_CONNECTION_UNSET, + HTTP_CONNECTION_KEEPALIVE, + HTTP_CONNECTION_CLOSE, +}; + +typedef struct { + enum keep_alive_set keep_alive_set; + char con_length_set; + char *reqline_host; + int reqline_hostlen; +} parse_header_state; + +static void init_parse_header_state(parse_header_state* state) { + state->keep_alive_set = HTTP_CONNECTION_UNSET; + state->con_length_set = 0; + state->reqline_host = NULL; + state->reqline_hostlen = 0; +} + +/* add header to list of headers + * certain headers are also parsed + * might drop a header if deemed unnecessary/broken + * + * returns 0 on error + */ +static int parse_single_header(server *srv, connection *con, parse_header_state *state, char *k, size_t klen, char *v, size_t vlen) { + const enum http_header_e id = http_header_hkey_get(k, klen); + buffer **saveb = NULL; + + /* strip leading whitespace */ + for (; vlen > 0 && (v[0] == ' ' || v[0] == '\t'); ++v, --vlen) ; + + /* strip trailing whitespace */ + while (vlen > 0 && (v[vlen - 1] == ' ' || v[vlen - 1] == '\t')) --vlen; + + /* empty header-fields are not allowed by HTTP-RFC, we just ignore them */ + if (0 == vlen) return 1; /* ignore header */ + + /* + * Note: k might not be '\0'-terminated + */ + + switch (id) { + /*case HTTP_HEADER_OTHER:*/ + default: + break; + case HTTP_HEADER_HOST: + if (!(con->request.htags & HTTP_HEADER_HOST)) { + saveb = &con->request.http_host; + if (vlen >= 1024) { /*(expecting < 256)*/ + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "uri-authority too long -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", con->request.request); + } + return 0; /* invalid header */ + } + } + else if (state->reqline_host) { + /* ignore all Host: headers as we got Host in request line */ + return 1; /* ignore header */ + } + else { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate Host-header -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", con->request.request); + } + return 0; /* invalid header */ + } + break; + case HTTP_HEADER_CONNECTION: + { + array * const vals = srv->split_vals; + array_reset_data_strings(vals); + http_request_split_value(vals, v, vlen); /* split on , */ + for (size_t vi = 0; vi < vals->used; ++vi) { + data_string *dsv = (data_string *)vals->data[vi]; + if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), + CONST_STR_LEN("keep-alive"))) { + state->keep_alive_set = HTTP_CONNECTION_KEEPALIVE; + break; + } + else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), + CONST_STR_LEN("close"))) { + state->keep_alive_set = HTTP_CONNECTION_CLOSE; + break; + } + } + } + break; + case HTTP_HEADER_CONTENT_TYPE: + if (con->request.htags & HTTP_HEADER_CONTENT_TYPE) { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate Content-Type-header -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", con->request.request); + } + return 0; /* invalid header */ + } + break; + case HTTP_HEADER_IF_NONE_MATCH: + /* if dup, only the first one will survive */ + if (con->request.htags & HTTP_HEADER_IF_NONE_MATCH) { + return 1; /* ignore header */ + } + break; + case HTTP_HEADER_CONTENT_LENGTH: + if (!(con->request.htags & HTTP_HEADER_CONTENT_LENGTH)) { + char *err; + off_t r = strtoll(v, &err, 10); + + if (*err == '\0' && r >= 0) { + con->request.content_length = r; + } + else { + log_error_write(srv, __FILE__, __LINE__, "sss", + "content-length broken:", v, "-> 400"); + return 0; /* invalid header */ + } + } + else { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate Content-Length-header -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", con->request.request); + } + return 0; /* invalid header */ + } + break; + case HTTP_HEADER_IF_MODIFIED_SINCE: + if (con->request.htags & HTTP_HEADER_IF_MODIFIED_SINCE) { + /* Proxies sometimes send dup headers + * if they are the same we ignore the second + * if not, we raise an error */ + buffer *vb = + http_header_request_get(con, HTTP_HEADER_IF_MODIFIED_SINCE, + CONST_STR_LEN("If-Modified-Since")); + if (vb && buffer_is_equal_caseless_string(vb, v, vlen)) { + /* ignore it if they are the same */ + return 1; /* ignore header */ + } + else { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", + "duplicate If-Modified-Since header -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", con->request.request); + } + return 0; /* invalid header */ + } + } + break; + } + + con->request.htags |= id; + http_header_request_append(con, id, k, klen, v, vlen); + + if (saveb) { + *saveb = http_header_request_get(con, id, k, klen); + } + + return 1; +} + +static size_t http_request_parse_reqline(server *srv, connection *con, parse_header_state *state) { + char *uri = NULL, *proto = NULL, *method = NULL; + int line = 0; + + int request_line_stage = 0; + size_t i, first, ilen; + const unsigned int http_header_strict = (con->conf.http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); + + /* + * Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$" + * Option : "^([-a-zA-Z]+): (.+)$" + * End : "^$" + */ + + if (con->conf.log_request_header) { + log_error_write(srv, __FILE__, __LINE__, "sdsdSb", + "fd:", con->fd, + "request-len:", buffer_string_length(con->request.request), + "\n", con->request.request); + } + + if (con->request_count > 1 && + con->request.request->ptr[0] == '\r' && + con->request.request->ptr[1] == '\n') { + /* we are in keep-alive and might get \r\n after a previous POST request.*/ + + #ifdef __COVERITY__ + if (buffer_string_length(con->request.request) < 2) { + return 0; + } + #endif + /* coverity[overflow_sink : FALSE] */ + buffer_copy_string_len(con->parse_request, con->request.request->ptr + 2, buffer_string_length(con->request.request) - 2); + } else if (con->request_count > 0 && + con->request.request->ptr[1] == '\n') { + /* we are in keep-alive and might get \n after a previous POST request.*/ + if (http_header_strict) { + http_request_missing_CR_before_LF(srv, con); + return 0; + } + #ifdef __COVERITY__ + if (buffer_string_length(con->request.request) < 1) { + return 0; + } + #endif + /* coverity[overflow_sink : FALSE] */ + buffer_copy_string_len(con->parse_request, con->request.request->ptr + 1, buffer_string_length(con->request.request) - 1); + } else { + /* fill the local request buffer */ + buffer_copy_buffer(con->parse_request, con->request.request); + } + + /* parse the first line of the request + * + * should be: + * + * <method> <uri> <protocol>\r\n + * */ + ilen = buffer_string_length(con->parse_request); + for (i = 0, first = 0; i < ilen && line == 0; i++) { + switch(con->parse_request->ptr[i]) { + case '\r': + if (con->parse_request->ptr[i+1] != '\n') break; + /* fall through */ + case '\n': + { + http_method_t r; + char *nuri = NULL; + size_t j, jlen; + + buffer_copy_string_len(con->request.request_line, con->parse_request->ptr, i); + + /* \r\n -> \0\0 */ + if (con->parse_request->ptr[i] == '\r') { + con->parse_request->ptr[i] = '\0'; + ++i; + } else if (http_header_strict) { /* '\n' */ + http_request_missing_CR_before_LF(srv, con); + return 0; + } + con->parse_request->ptr[i] = '\0'; + + if (request_line_stage != 2) { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "incomplete request line -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; + } + + proto = con->parse_request->ptr + first; + + *(uri - 1) = '\0'; + *(proto - 1) = '\0'; + + /* we got the first one :) */ + if (HTTP_METHOD_UNSET == (r = get_http_method_key(method))) { + con->http_status = 501; + + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "unknown http-method -> 501"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + + return 0; + } + + con->request.http_method = r; + + /* + * RFC2616 says: + * + * HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT + * + * */ + if (0 == strncmp(proto, "HTTP/", sizeof("HTTP/") - 1)) { + char * major = proto + sizeof("HTTP/") - 1; + char * minor = strchr(major, '.'); + char *err = NULL; + int major_num = 0, minor_num = 0; + + int invalid_version = 0; + + if (NULL == minor || /* no dot */ + minor == major || /* no major */ + *(minor + 1) == '\0' /* no minor */) { + invalid_version = 1; + } else { + *minor = '\0'; + major_num = strtol(major, &err, 10); + + if (*err != '\0') invalid_version = 1; + + *minor++ = '.'; + minor_num = strtol(minor, &err, 10); + + if (*err != '\0') invalid_version = 1; + } + + if (invalid_version) { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; + } + + if (major_num == 1 && minor_num == 1) { + con->request.http_version = con->conf.allow_http11 ? HTTP_VERSION_1_1 : HTTP_VERSION_1_0; + } else if (major_num == 1 && minor_num == 0) { + con->request.http_version = HTTP_VERSION_1_0; + } else { + con->http_status = 505; + + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "unknown HTTP version -> 505"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; + } + } else { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; + } + + if (*uri == '/') { + /* (common case) */ + buffer_copy_string_len(con->request.uri, uri, proto - uri - 1); + } else if (0 == buffer_caseless_compare(uri, 7, "http://", 7) && + NULL != (nuri = strchr(uri + 7, '/'))) { + state->reqline_host = uri + 7; + state->reqline_hostlen = nuri - state->reqline_host; + + buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1); + } else if (0 == buffer_caseless_compare(uri, 8, "https://", 8) && + NULL != (nuri = strchr(uri + 8, '/'))) { + state->reqline_host = uri + 8; + state->reqline_hostlen = nuri - state->reqline_host; + + buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1); + } else if (!http_header_strict + || (HTTP_METHOD_CONNECT == con->request.http_method && (uri[0] == ':' || light_isdigit(uri[0]))) + || (HTTP_METHOD_OPTIONS == con->request.http_method && uri[0] == '*' && uri[1] == '\0')) { + /* everything looks good so far */ + buffer_copy_string_len(con->request.uri, uri, proto - uri - 1); + } else { + log_error_write(srv, __FILE__, __LINE__, "ss", "request-URI parse error -> 400 for:", uri); + return 0; + } + + /* check uri for invalid characters */ + jlen = buffer_string_length(con->request.uri); + if ((con->conf.http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT)) { + j = jlen; /* URI will be checked in http_response_prepare() */ + } else if (http_header_strict) { + for (j = 0; j < jlen && request_uri_is_valid_char(con->request.uri->ptr[j]); j++) ; + } else { + char *z = memchr(con->request.uri->ptr, '\0', jlen); + j = (NULL == z) ? jlen : (size_t)(z - con->request.uri->ptr); + } + if (j < jlen) { + if (srv->srvconf.log_request_header_on_error) { + unsigned char buf[2]; + buf[0] = con->request.uri->ptr[j]; + buf[1] = '\0'; + + if (con->request.uri->ptr[j] > 32 && + con->request.uri->ptr[j] != 127) { + /* the character is printable -> print it */ + log_error_write(srv, __FILE__, __LINE__, "ss", + "invalid character in URI -> 400", + buf); + } else { + /* a control-character, print ascii-code */ + log_error_write(srv, __FILE__, __LINE__, "sd", + "invalid character in URI -> 400", + con->request.uri->ptr[j]); + } + + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + + return 0; + } + + buffer_copy_buffer(con->request.orig_uri, con->request.uri); + + con->http_status = 0; + + line++; + first = i+1; + } + break; + case ' ': + switch(request_line_stage) { + case 0: + /* GET|POST|... */ + method = con->parse_request->ptr + first; + first = i + 1; + break; + case 1: + /* /foobar/... */ + uri = con->parse_request->ptr + first; + first = i + 1; + break; + default: + /* ERROR, one space to much */ + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "overlong request line -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; + } + + request_line_stage++; + break; + } + } + + if (buffer_string_is_empty(con->request.uri)) { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "no uri specified -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + return 0; + } + + if (state->reqline_host) { + /* Insert as host header */ + if (state->reqline_hostlen >= 1024) { /*(expecting < 256)*/ + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "uri-authority too long -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", con->request.request); + } + return 0; + } + http_header_request_set(con, HTTP_HEADER_HOST, CONST_STR_LEN("Host"), state->reqline_host, state->reqline_hostlen); + con->request.http_host = http_header_request_get(con, HTTP_HEADER_HOST, CONST_STR_LEN("Host")); + } + + return i; +} + +int http_request_parse(server *srv, connection *con) { + char *value = NULL; + size_t i, first, ilen; + const unsigned int http_header_strict = (con->conf.http_parseopts & HTTP_PARSEOPT_HEADER_STRICT); + + parse_header_state state; + init_parse_header_state(&state); + + i = first = http_request_parse_reqline(srv, con, &state); + if (0 == i) goto failure; + + if (con->parse_request->ptr[i] == ' ' || con->parse_request->ptr[i] == '\t') { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "WS at the start of first line -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", "request-header:\n", con->request.request); + } + goto failure; + } + + ilen = buffer_string_length(con->parse_request); + for (int is_key = 1, key_len = 0, done = 0; i <= ilen && !done; ++i) { + char *cur = con->parse_request->ptr + i; + + if (is_key) { + /** + * 1*<any CHAR except CTLs or separators> + * CTLs == 0-31 + 127, CHAR = 7-bit ascii (0..127) + * + */ + switch(*cur) { + case ' ': + case '\t': + /* skip every thing up to the : */ + do { ++cur; } while (*cur == ' ' || *cur == '\t'); + if (*cur != ':') { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "WS character in key -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + + goto failure; + } + /* fall through */ + case ':': + is_key = 0; + key_len = i - first; + value = cur + 1; + i = cur - con->parse_request->ptr; + break; + case '(': + case ')': + case '<': + case '>': + case '@': + case ',': + case ';': + case '\\': + case '\"': + case '/': + case '[': + case ']': + case '?': + case '=': + case '{': + case '}': + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "sbsds", + "invalid character in key", con->request.request, cur, *cur, "-> 400"); + + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + goto failure; + case '\r': + if (con->parse_request->ptr[i+1] == '\n' && i == first) { + /* End of Header */ + con->parse_request->ptr[i] = '\0'; + con->parse_request->ptr[i+1] = '\0'; + + i++; + + done = 1; + } else { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "CR without LF -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + + goto failure; + } + break; + case '\n': + if (http_header_strict) { + http_request_missing_CR_before_LF(srv, con); + goto failure; + } else if (i == first) { + con->parse_request->ptr[i] = '\0'; + done = 1; + break; + } + /* fall through */ + default: + if (http_header_strict ? (*cur < 32 || ((unsigned char)*cur) >= 127) : *cur == '\0') { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "sbsds", + "invalid character in key", con->request.request, cur, *cur, "-> 400"); + + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + + goto failure; + } + /* ok */ + break; + } + } else { + switch(*cur) { + case '\r': + if (cur[1] != '\n') { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "CR without LF", con->request.request, "-> 400"); + } + + goto failure; + } + if (cur[2] == ' ' || cur[2] == '\t') { /* header line folding */ + cur[0] = ' '; + cur[1] = ' '; + i += 2; + continue; + } + ++i; + /* fall through */ + case '\n': + if (*cur == '\n') { + if (http_header_strict) { + http_request_missing_CR_before_LF(srv, con); + goto failure; + } + if (cur[1] == ' ' || cur[1] == '\t') { /* header line folding */ + cur[0] = ' '; + i += 1; + continue; + } + } + + /* End of Headerline */ + *cur = '\0'; /*(for if value is further parsed and '\0' is expected at end of string)*/ + + if (!parse_single_header(srv, con, &state, con->parse_request->ptr + first, key_len, value, cur - value)) { + /* parse_single_header should already have logged it */ + goto failure; + } + + first = i+1; + is_key = 1; + value = NULL; + break; + case ' ': + case '\t': + break; + default: + if (http_header_strict ? (*cur >= 0 && *cur < 32) : *cur == '\0') { + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "invalid char in header", (int)*cur, "-> 400"); + } + + goto failure; + } + break; + } + } + } + + con->header_len = i; + + /* do some post-processing */ + + if (con->request.http_version == HTTP_VERSION_1_1) { + if (state.keep_alive_set != HTTP_CONNECTION_CLOSE) { + /* no Connection-Header sent */ + + /* HTTP/1.1 -> keep-alive default TRUE */ + con->keep_alive = 1; + } else { + con->keep_alive = 0; + } + + /* RFC 2616, 14.23 */ + if (con->request.http_host == NULL || + buffer_string_is_empty(con->request.http_host)) { + + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", "HTTP/1.1 but Host missing -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + goto failure; + } + } else { + if (state.keep_alive_set == HTTP_CONNECTION_KEEPALIVE) { + /* no Connection-Header sent */ + + /* HTTP/1.0 -> keep-alive default FALSE */ + con->keep_alive = 1; + } else { + con->keep_alive = 0; + } + } + + /* check hostname field if it is set */ + if (!buffer_is_empty(con->request.http_host) && + 0 != http_request_host_policy(con, con->request.http_host, con->proto)) { + + if (srv->srvconf.log_request_header_on_error) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Invalid Hostname -> 400"); + log_error_write(srv, __FILE__, __LINE__, "Sb", + "request-header:\n", + con->request.request); + } + + goto failure; + } + + if (con->request.htags & HTTP_HEADER_TRANSFER_ENCODING) { + buffer *vb = http_header_request_get(con, HTTP_HEADER_TRANSFER_ENCODING, CONST_STR_LEN("Transfer-Encoding")); + if (NULL != vb) { + if (con->request.http_version == HTTP_VERSION_1_0) { + log_error_write(srv, __FILE__, __LINE__, "s", + "HTTP/1.0 with Transfer-Encoding (bad HTTP/1.0 proxy?) -> 400"); + goto failure; + } + + if (0 != strcasecmp(vb->ptr, "chunked")) { + /* Transfer-Encoding might contain additional encodings, + * which are not currently supported by lighttpd */ + con->http_status = 501; /* Not Implemented */ + goto failure; + } + + /* reset value for Transfer-Encoding, a hop-by-hop header, + * which must not be blindly forwarded to backends */ + http_header_request_unset(con, HTTP_HEADER_TRANSFER_ENCODING, CONST_STR_LEN("Transfer-Encoding")); + + /*(note: ignore whether or not Content-Length was provided)*/ + if (con->request.htags & HTTP_HEADER_CONTENT_LENGTH) { + http_header_request_unset(con, HTTP_HEADER_CONTENT_LENGTH, CONST_STR_LEN("Content-Length")); + } + + state.con_length_set = 1; + con->request.content_length = -1; + } + } + else if (con->request.htags & HTTP_HEADER_CONTENT_LENGTH) { + state.con_length_set = 1; + } + + switch(con->request.http_method) { + case HTTP_METHOD_GET: + case HTTP_METHOD_HEAD: + /* content-length is forbidden for those */ + if (state.con_length_set && con->request.content_length != 0) { + /* content-length is missing */ + log_error_write(srv, __FILE__, __LINE__, "s", + "GET/HEAD with content-length -> 400"); + + goto failure; + } + break; + case HTTP_METHOD_POST: + /* content-length is required for them */ + if (!state.con_length_set) { + /* content-length is missing */ + log_error_write(srv, __FILE__, __LINE__, "s", + "POST-request, but content-length missing -> 411"); + + con->http_status = 411; + goto failure; + } + break; + default: + break; + } + + + /* check if we have read post data */ + if (state.con_length_set) { + /* we have content */ + if (con->request.content_length != 0) { + return 1; + } + } + + return 0; + +failure: + con->keep_alive = 0; + con->response.keep_alive = 0; + if (!con->http_status) con->http_status = 400; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/request.h b/data/lighttpd/lighttpd-1.4.53/src/request.h new file mode 100644 index 000000000..ac5f1a14f --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/request.h @@ -0,0 +1,12 @@ +#ifndef _REQUEST_H_ +#define _REQUEST_H_ +#include "first.h" + +#include "base_decls.h" +#include "buffer.h" + +int http_request_parse(server *srv, connection *con); +int http_request_host_normalize(buffer *b, int scheme_port); +int http_request_host_policy(connection *con, buffer *b, const buffer *scheme); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/response.c b/data/lighttpd/lighttpd-1.4.53/src/response.c new file mode 100644 index 000000000..09ab8d23b --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/response.c @@ -0,0 +1,702 @@ +#include "first.h" + +#include "response.h" +#include "base.h" +#include "burl.h" +#include "fdevent.h" +#include "http_header.h" +#include "http_kv.h" +#include "log.h" +#include "stat_cache.h" +#include "chunk.h" + +#include "configfile.h" + +#include "plugin.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <limits.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +int http_response_write_header(server *srv, connection *con) { + buffer * const b = chunkqueue_prepend_buffer_open(con->write_queue); + + if (con->request.http_version == HTTP_VERSION_1_1) { + buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.1 ")); + } else { + buffer_copy_string_len(b, CONST_STR_LEN("HTTP/1.0 ")); + } + http_status_append(b, con->http_status); + + /* disable keep-alive if requested */ + if (con->request_count > con->conf.max_keep_alive_requests || 0 == con->conf.max_keep_alive_idle) { + con->keep_alive = 0; + } else if (0 != con->request.content_length + && con->request.content_length != con->request_content_queue->bytes_in + && (con->mode == DIRECT || 0 == con->conf.stream_request_body)) { + con->keep_alive = 0; + } else { + con->keep_alive_idle = con->conf.max_keep_alive_idle; + } + + if ((con->response.htags & HTTP_HEADER_UPGRADE) && con->request.http_version == HTTP_VERSION_1_1) { + http_header_response_set(con, HTTP_HEADER_CONNECTION, CONST_STR_LEN("Connection"), CONST_STR_LEN("upgrade")); + } else if (0 == con->keep_alive) { + http_header_response_set(con, HTTP_HEADER_CONNECTION, CONST_STR_LEN("Connection"), CONST_STR_LEN("close")); + } else if (con->request.http_version == HTTP_VERSION_1_0) {/*(&& con->keep_alive != 0)*/ + http_header_response_set(con, HTTP_HEADER_CONNECTION, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive")); + } + + if (304 == con->http_status && (con->response.htags & HTTP_HEADER_CONTENT_ENCODING)) { + http_header_response_unset(con, HTTP_HEADER_CONTENT_ENCODING, CONST_STR_LEN("Content-Encoding")); + } + + /* add all headers */ + for (size_t i = 0; i < con->response.headers->used; ++i) { + const data_string * const ds = (data_string *)con->response.headers->data[i]; + + if (buffer_string_is_empty(ds->value) || buffer_string_is_empty(ds->key)) continue; + if ((ds->key->ptr[0] & 0xdf) == 'X') { + if (0 == strncasecmp(ds->key->ptr, CONST_STR_LEN("X-Sendfile"))) continue; + if (0 == strncasecmp(ds->key->ptr, CONST_STR_LEN("X-LIGHTTPD-"))) { + if (0 == strncasecmp(ds->key->ptr+sizeof("X-LIGHTTPD-")-1, CONST_STR_LEN("KBytes-per-second"))) { + /* "X-LIGHTTPD-KBytes-per-second" */ + long limit = strtol(ds->value->ptr, NULL, 10); + if (limit > 0 + && (limit < con->conf.kbytes_per_second + || 0 == con->conf.kbytes_per_second)) { + if (limit > USHRT_MAX) limit= USHRT_MAX; + con->conf.kbytes_per_second = limit; + } + } + continue; + } + } + + buffer_append_string_len(b, CONST_STR_LEN("\r\n")); + buffer_append_string_buffer(b, ds->key); + buffer_append_string_len(b, CONST_STR_LEN(": ")); + buffer_append_string_buffer(b, ds->value); + } + + if (!(con->response.htags & HTTP_HEADER_DATE)) { + /* HTTP/1.1 requires a Date: header */ + buffer_append_string_len(b, CONST_STR_LEN("\r\nDate: ")); + + /* cache the generated timestamp */ + if (srv->cur_ts != srv->last_generated_date_ts) { + buffer_clear(srv->ts_date_str); + buffer_append_strftime(srv->ts_date_str, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv->cur_ts))); + srv->last_generated_date_ts = srv->cur_ts; + } + + buffer_append_string_buffer(b, srv->ts_date_str); + } + + if (!(con->response.htags & HTTP_HEADER_SERVER)) { + if (!buffer_string_is_empty(con->conf.server_tag)) { + buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: ")); + buffer_append_string_len(b, CONST_BUF_LEN(con->conf.server_tag)); + } + } + + buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); + + con->bytes_header = buffer_string_length(b); + + if (con->conf.log_response_header) { + log_error_write(srv, __FILE__, __LINE__, "sSb", "Response-Header:", "\n", b); + } + + chunkqueue_prepend_buffer_commit(con->write_queue); + return 0; +} + +static handler_t http_response_physical_path_check(server *srv, connection *con) { + stat_cache_entry *sce = NULL; + + if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + /* file exists */ + } else { + char *pathinfo = NULL; + switch (errno) { + case EACCES: + con->http_status = 403; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + + buffer_reset(con->physical.path); + return HANDLER_FINISHED; + case ENAMETOOLONG: + /* file name to be read was too long. return 404 */ + case ENOENT: + con->http_status = 404; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- file not found"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + + buffer_reset(con->physical.path); + return HANDLER_FINISHED; + case ENOTDIR: + /* PATH_INFO ! :) */ + break; + default: + /* we have no idea what happend. let's tell the user so. */ + con->http_status = 500; + buffer_reset(con->physical.path); + + log_error_write(srv, __FILE__, __LINE__, "ssbsb", + "file not found ... or so: ", strerror(errno), + con->uri.path, + "->", con->physical.path); + + return HANDLER_FINISHED; + } + + /* not found, perhaps PATHINFO */ + + { + /*(might check at startup that s->document_root does not end in '/')*/ + size_t len = buffer_string_length(con->physical.basedir); + if (len > 0 && '/' == con->physical.basedir->ptr[len-1]) --len; + pathinfo = con->physical.path->ptr + len; + if ('/' != *pathinfo) { + pathinfo = NULL; + } + else if (pathinfo == con->physical.path->ptr) { /*(basedir is "/")*/ + pathinfo = strchr(pathinfo+1, '/'); + } + } + + for (char *pprev = pathinfo; pathinfo; pprev = pathinfo, pathinfo = strchr(pathinfo+1, '/')) { + stat_cache_entry *nsce = NULL; + buffer_copy_string_len(srv->tmp_buf, con->physical.path->ptr, pathinfo - con->physical.path->ptr); + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, srv->tmp_buf, &nsce)) { + pathinfo = pathinfo != pprev ? pprev : NULL; + break; + } + sce = nsce; + if (!S_ISDIR(sce->st.st_mode)) break; + } + + if (NULL == pathinfo || !S_ISREG(sce->st.st_mode)) { + /* no it really doesn't exists */ + con->http_status = 404; + + if (con->conf.log_file_not_found) { + log_error_write(srv, __FILE__, __LINE__, "sbsb", + "file not found:", con->uri.path, + "->", con->physical.path); + } + + buffer_reset(con->physical.path); + + return HANDLER_FINISHED; + } + + /* we have a PATHINFO */ + if (pathinfo) { + size_t len = strlen(pathinfo), reqlen; + if (con->conf.force_lowercase_filenames + && len <= (reqlen = buffer_string_length(con->request.uri)) + && 0 == strncasecmp(con->request.uri->ptr + reqlen - len, pathinfo, len)) { + /* attempt to preserve case-insensitive PATH_INFO + * (works in common case where mod_alias, mod_magnet, and other modules + * have not modified the PATH_INFO portion of request URI, or did so + * with exactly the PATH_INFO desired) */ + buffer_copy_string_len(con->request.pathinfo, con->request.uri->ptr + reqlen - len, len); + } else { + buffer_copy_string_len(con->request.pathinfo, pathinfo, len); + } + + /* + * shorten uri.path + */ + + buffer_string_set_length(con->uri.path, buffer_string_length(con->uri.path) - len); + buffer_string_set_length(con->physical.path, (size_t)(pathinfo - con->physical.path->ptr)); + } + } + +#ifdef HAVE_LSTAT + if ((sce->is_symlink != 0) && !con->conf.follow_symlink) { + con->http_status = 403; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + + buffer_reset(con->physical.path); + return HANDLER_FINISHED; + }; +#endif + if (S_ISDIR(sce->st.st_mode)) { + if (con->uri.path->ptr[buffer_string_length(con->uri.path) - 1] != '/') { + /* redirect to .../ */ + + http_response_redirect_to_directory(srv, con); + + return HANDLER_FINISHED; + } +#ifdef HAVE_LSTAT + } else if (!S_ISREG(sce->st.st_mode) && !sce->is_symlink) { +#else + } else if (!S_ISREG(sce->st.st_mode)) { +#endif + /* any special handling of non-reg files ?*/ + } + + return HANDLER_GO_ON; +} + +handler_t http_response_prepare(server *srv, connection *con) { + handler_t r; + + /* looks like someone has already done a decision */ + if (con->mode == DIRECT && + (con->http_status != 0 && con->http_status != 200)) { + /* remove a packets in the queue */ + if (con->file_finished == 0) { + http_response_body_clear(con, 0); + } + + return HANDLER_FINISHED; + } + + /* no decision yet, build conf->filename */ + if (con->mode == DIRECT && buffer_is_empty(con->physical.path)) { + + /* we only come here when we have the parse the full request again + * + * a HANDLER_COMEBACK from mod_rewrite and mod_fastcgi might be a + * problem here as mod_setenv might get called multiple times + * + * fastcgi-auth might lead to a COMEBACK too + * fastcgi again dead server too + * + * mod_compress might add headers twice too + * + * */ + + if (!con->async_callback) { + + config_cond_cache_reset(srv, con); + config_setup_connection(srv, con); /* Perhaps this could be removed at other places. */ + + if (con->conf.log_condition_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "run condition"); + } + + /** + * prepare strings + * + * - uri.path_raw + * - uri.path + * - uri.query + * + */ + + /** + * Name according to RFC 2396 + * + * - scheme + * - authority + * - path + * - query + * + * (scheme)://(authority)(path)?(query)#fragment + * + * + */ + + /* take initial scheme value from connection-level state + * (request con->uri.scheme can be overwritten for later, + * for example by mod_extforward or mod_magnet) */ + buffer_copy_buffer(con->uri.scheme, con->proto); + buffer_copy_buffer(con->uri.authority, con->request.http_host); + buffer_to_lower(con->uri.authority); + + if (con->request.http_method == HTTP_METHOD_CONNECT + || (con->request.http_method == HTTP_METHOD_OPTIONS + && con->request.uri->ptr[0] == '*' + && con->request.uri->ptr[1] == '\0')) { + /* CONNECT ... (or) OPTIONS * ... */ + buffer_copy_buffer(con->uri.path_raw, con->request.uri); + buffer_copy_buffer(con->uri.path, con->uri.path_raw); + buffer_reset(con->uri.query); + } else { + char *qstr; + if (con->conf.http_parseopts & HTTP_PARSEOPT_URL_NORMALIZE) { + /*size_t len = buffer_string_length(con->request.uri);*/ + int qs = burl_normalize(con->request.uri, srv->tmp_buf, con->conf.http_parseopts); + if (-2 == qs) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "invalid character in URI -> 400", + con->request.uri); + con->keep_alive = 0; + con->http_status = 400; /* Bad Request */ + con->file_finished = 1; + return HANDLER_FINISHED; + } + qstr = (-1 == qs) ? NULL : con->request.uri->ptr+qs; + #if 0 /* future: might enable here, or below for all requests */ + /* (Note: total header size not recalculated on HANDLER_COMEBACK + * even if other request headers changed during processing) + * (If (0 != con->loops_per_request), then the generated request + * is too large. Should a different error be returned?) */ + con->header_len -= len; + len = buffer_string_length(con->request.uri); + con->header_len += len; + if (len > MAX_HTTP_REQUEST_URI) { + con->keep_alive = 0; + con->http_status = 414; /* Request-URI Too Long */ + con->file_finished = 1; + return HANDLER_FINISHED; + } + if (con->header_len > MAX_HTTP_REQUEST_HEADER) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "request header fields too large:", con->header_len, "-> 431"); + con->keep_alive = 0; + con->http_status = 431; /* Request Header Fields Too Large */ + con->file_finished = 1; + return HANDLER_FINISHED; + } + #endif + } else { + qstr = strchr(con->request.uri->ptr, '#');/* discard fragment */ + if (qstr) buffer_string_set_length(con->request.uri, qstr - con->request.uri->ptr); + qstr = strchr(con->request.uri->ptr, '?'); + } + + /** extract query string from request.uri */ + if (NULL != qstr) { + const char * const pstr = con->request.uri->ptr; + const size_t plen = (size_t)(qstr - pstr); + const size_t rlen = buffer_string_length(con->request.uri); + buffer_copy_string_len(con->uri.query, qstr + 1, rlen - plen - 1); + buffer_copy_string_len(con->uri.path_raw, pstr, plen); + } else { + buffer_reset(con->uri.query); + buffer_copy_buffer(con->uri.path_raw, con->request.uri); + } + + /* decode url to path + * + * - decode url-encodings (e.g. %20 -> ' ') + * - remove path-modifiers (e.g. /../) + */ + + buffer_copy_buffer(con->uri.path, con->uri.path_raw); + buffer_urldecode_path(con->uri.path); + buffer_path_simplify(con->uri.path, con->uri.path); + if (buffer_string_is_empty(con->uri.path) || con->uri.path->ptr[0] != '/') { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "uri-path does not begin with '/':", con->uri.path, "-> 400"); + con->keep_alive = 0; + con->http_status = 400; + con->file_finished = 1; + return HANDLER_FINISHED; + } + } + + con->conditional_is_valid[COMP_SERVER_SOCKET] = 1; /* SERVERsocket */ + con->conditional_is_valid[COMP_HTTP_SCHEME] = 1; /* Scheme: */ + con->conditional_is_valid[COMP_HTTP_HOST] = 1; /* Host: */ + con->conditional_is_valid[COMP_HTTP_REMOTE_IP] = 1; /* Client-IP */ + con->conditional_is_valid[COMP_HTTP_REQUEST_METHOD] = 1; /* REQUEST_METHOD */ + con->conditional_is_valid[COMP_HTTP_URL] = 1; /* HTTPurl */ + con->conditional_is_valid[COMP_HTTP_QUERY_STRING] = 1; /* HTTPqs */ + con->conditional_is_valid[COMP_HTTP_REQUEST_HEADER] = 1; /* HTTP request header */ + config_patch_connection(srv, con); + + /* do we have to downgrade to 1.0 ? */ + if (!con->conf.allow_http11) { + con->request.http_version = HTTP_VERSION_1_0; + } + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- splitting Request-URI"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Request-URI : ", con->request.uri); + log_error_write(srv, __FILE__, __LINE__, "sb", "URI-scheme : ", con->uri.scheme); + log_error_write(srv, __FILE__, __LINE__, "sb", "URI-authority : ", con->uri.authority); + log_error_write(srv, __FILE__, __LINE__, "sb", "URI-path (raw) : ", con->uri.path_raw); + log_error_write(srv, __FILE__, __LINE__, "sb", "URI-path (clean): ", con->uri.path); + log_error_write(srv, __FILE__, __LINE__, "sb", "URI-query : ", con->uri.query); + } + + /* con->conf.max_request_size is in kBytes */ + if (0 != con->conf.max_request_size && + (off_t)con->request.content_length > ((off_t)con->conf.max_request_size << 10)) { + log_error_write(srv, __FILE__, __LINE__, "sos", + "request-size too long:", (off_t) con->request.content_length, "-> 413"); + con->keep_alive = 0; + con->http_status = 413; + con->file_finished = 1; + + return HANDLER_FINISHED; + } + + + } + con->async_callback = 0; /* reset */ + + + /** + * + * call plugins + * + * - based on the raw URL + * + */ + + switch(r = plugins_call_handle_uri_raw(srv, con)) { + case HANDLER_GO_ON: + break; + case HANDLER_FINISHED: + case HANDLER_COMEBACK: + case HANDLER_WAIT_FOR_EVENT: + case HANDLER_ERROR: + return r; + default: + log_error_write(srv, __FILE__, __LINE__, "sd", "handle_uri_raw: unknown return value", r); + break; + } + + /** + * + * call plugins + * + * - based on the clean URL + * + */ + + switch(r = plugins_call_handle_uri_clean(srv, con)) { + case HANDLER_GO_ON: + break; + case HANDLER_FINISHED: + case HANDLER_COMEBACK: + case HANDLER_WAIT_FOR_EVENT: + case HANDLER_ERROR: + return r; + default: + log_error_write(srv, __FILE__, __LINE__, ""); + break; + } + + if (con->request.http_method == HTTP_METHOD_OPTIONS && + con->uri.path->ptr[0] == '*' && con->uri.path->ptr[1] == '\0') { + /* option requests are handled directly without checking of the path */ + + http_header_response_append(con, HTTP_HEADER_OTHER, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); + + con->http_status = 200; + con->file_finished = 1; + + return HANDLER_FINISHED; + } + + if (con->request.http_method == HTTP_METHOD_CONNECT && con->mode == DIRECT) { + con->keep_alive = 0; + con->http_status = 405; /* Method Not Allowed */ + con->file_finished = 1; + return HANDLER_FINISHED; + } + + /*** + * + * border + * + * logical filename (URI) becomes a physical filename here + * + * + * + */ + + + + + /* 1. stat() + * ... ISREG() -> ok, go on + * ... ISDIR() -> index-file -> redirect + * + * 2. pathinfo() + * ... ISREG() + * + * 3. -> 404 + * + */ + + /* + * SEARCH DOCUMENT ROOT + */ + + /* set a default */ + + buffer_copy_buffer(con->physical.doc_root, con->conf.document_root); + buffer_copy_buffer(con->physical.rel_path, con->uri.path); + +#if defined(__WIN32) || defined(__CYGWIN__) + /* strip dots from the end and spaces + * + * windows/dos handle those filenames as the same file + * + * foo == foo. == foo..... == "foo... " == "foo.. ./" + * + * This will affect in some cases PATHINFO + * + * on native windows we could prepend the filename with \\?\ to circumvent + * this behaviour. I have no idea how to push this through cygwin + * + * */ + + if (con->physical.rel_path->used > 1) { + buffer *b = con->physical.rel_path; + size_t len = buffer_string_length(b); + + /* strip trailing " /" or "./" once */ + if (len > 1 && + b->ptr[len - 1] == '/' && + (b->ptr[len - 2] == ' ' || b->ptr[len - 2] == '.')) { + len -= 2; + } + /* strip all trailing " " and "." */ + while (len > 0 && ( ' ' == b->ptr[len-1] || '.' == b->ptr[len-1] ) ) --len; + buffer_string_set_length(b, len); + } +#endif + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- before doc_root"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root); + log_error_write(srv, __FILE__, __LINE__, "sb", "Rel-Path :", con->physical.rel_path); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + /* the docroot plugin should set the doc_root and might also set the physical.path + * for us (all vhost-plugins are supposed to set the doc_root) + * */ + switch(r = plugins_call_handle_docroot(srv, con)) { + case HANDLER_GO_ON: + break; + case HANDLER_FINISHED: + case HANDLER_COMEBACK: + case HANDLER_WAIT_FOR_EVENT: + case HANDLER_ERROR: + return r; + default: + log_error_write(srv, __FILE__, __LINE__, ""); + break; + } + + /* MacOS X and Windows can't distiguish between upper and lower-case + * + * convert to lower-case + */ + if (con->conf.force_lowercase_filenames) { + buffer_to_lower(con->physical.rel_path); + } + + /* the docroot plugins might set the servername, if they don't we take http-host */ + if (buffer_string_is_empty(con->server_name)) { + buffer_copy_buffer(con->server_name, con->uri.authority); + } + + /** + * create physical filename + * -> physical.path = docroot + rel_path + * + */ + + buffer_copy_buffer(con->physical.basedir, con->physical.doc_root); + buffer_copy_buffer(con->physical.path, con->physical.doc_root); + buffer_append_path_len(con->physical.path, CONST_BUF_LEN(con->physical.rel_path)); + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- after doc_root"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root); + log_error_write(srv, __FILE__, __LINE__, "sb", "Rel-Path :", con->physical.rel_path); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + + if (con->request.http_method == HTTP_METHOD_CONNECT) { + /* do not permit CONNECT requests to hit filesystem hooks + * since the CONNECT URI bypassed path normalization */ + /* (This check is located here so that con->physical.path + * is filled in above to avoid repeating work next time + * http_response_prepare() is called while processing request) */ + } else + switch(r = plugins_call_handle_physical(srv, con)) { + case HANDLER_GO_ON: + break; + case HANDLER_FINISHED: + case HANDLER_COMEBACK: + case HANDLER_WAIT_FOR_EVENT: + case HANDLER_ERROR: + return r; + default: + log_error_write(srv, __FILE__, __LINE__, ""); + break; + } + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- logical -> physical"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Doc-Root :", con->physical.doc_root); + log_error_write(srv, __FILE__, __LINE__, "sb", "Basedir :", con->physical.basedir); + log_error_write(srv, __FILE__, __LINE__, "sb", "Rel-Path :", con->physical.rel_path); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + } + + /* + * Noone catched away the file from normal path of execution yet (like mod_access) + * + * Go on and check of the file exists at all + */ + + if (con->mode == DIRECT) { + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- handling physical path"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + + r = http_response_physical_path_check(srv, con); + if (HANDLER_GO_ON != r) return r; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- handling subrequest"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + log_error_write(srv, __FILE__, __LINE__, "sb", "URI :", con->uri.path); + log_error_write(srv, __FILE__, __LINE__, "sb", "Pathinfo :", con->request.pathinfo); + } + + /* call the handlers */ + r = plugins_call_handle_subrequest_start(srv, con); + if (HANDLER_GO_ON != r) { + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- subrequest finished"); + } + return r; + } + + /* if we are still here, no one wanted the file, status 403 is ok I think */ + if (con->mode == DIRECT && con->http_status == 0) { + con->http_status = (con->request.http_method != HTTP_METHOD_OPTIONS) ? 403 : 200; + return HANDLER_FINISHED; + } + + } + + r = plugins_call_handle_subrequest(srv, con); + if (HANDLER_GO_ON == r) r = HANDLER_FINISHED; /* request was not handled, looks like we are done */ + return r; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/response.h b/data/lighttpd/lighttpd-1.4.53/src/response.h new file mode 100644 index 000000000..288f4f6f8 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/response.h @@ -0,0 +1,57 @@ +#ifndef _RESPONSE_H_ +#define _RESPONSE_H_ +#include "first.h" + +#include "base_decls.h" +#include "buffer.h" +#include "array.h" + +#include <time.h> + +int http_response_parse(server *srv, connection *con); +int http_response_write_header(server *srv, connection *con); + +typedef struct http_cgi_opts_t { + int authorizer; + int break_scriptfilename_for_php; + buffer *docroot; + buffer *strip_request_uri; +} http_cgi_opts; + +enum { + BACKEND_UNSET = 0, + BACKEND_PROXY, + BACKEND_CGI, + BACKEND_FASTCGI, + BACKEND_SCGI +}; + +typedef struct http_response_opts_t { + int fdfmt; + int backend; + int authorizer; + unsigned short local_redir; + unsigned short xsendfile_allow; + array *xsendfile_docroot; + void *pdata; + handler_t(*parse)(server *, connection *, struct http_response_opts_t *, buffer *, size_t); + handler_t(*headers)(server *, connection *, struct http_response_opts_t *); +} http_response_opts; + +typedef int (*http_cgi_header_append_cb)(void *vdata, const char *k, size_t klen, const char *v, size_t vlen); +int http_cgi_headers(server *srv, connection *con, http_cgi_opts *opts, http_cgi_header_append_cb cb, void *vdata); + +handler_t http_response_parse_headers(server *srv, connection *con, http_response_opts *opts, buffer *hdrs); +handler_t http_response_read(server *srv, connection *con, http_response_opts *opts, buffer *b, int fd, int *fde_ndx); +handler_t http_response_prepare(server *srv, connection *con); +int http_response_buffer_append_authority(server *srv, connection *con, buffer *b); +int http_response_redirect_to_directory(server *srv, connection *con); +int http_response_handle_cachable(server *srv, connection *con, buffer * mtime); +void http_response_body_clear(connection *con, int preserve_length); +void http_response_send_file (server *srv, connection *con, buffer *path); +void http_response_backend_done (server *srv, connection *con); +void http_response_backend_error (server *srv, connection *con); +void http_response_upgrade_read_body_unknown(server *srv, connection *con); + +buffer * strftime_cache_get(server *srv, time_t last_mod); +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/safe_memclear.c b/data/lighttpd/lighttpd-1.4.53/src/safe_memclear.c new file mode 100644 index 000000000..0e1c9dfde --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/safe_memclear.c @@ -0,0 +1,47 @@ +#include "first.h" + +#include "safe_memclear.h" + +#include <string.h> + +#if !defined(HAVE_MEMSET_S) && !defined(HAVE_EXPLICIT_BZERO) + +# if defined(HAVE_WEAK_SYMBOLS) +/* it seems weak functions are never inlined, even for static builds */ +__attribute__((weak)) void __li_safe_memset_hook(void *buf, size_t len); + +void __li_safe_memset_hook(void *buf, size_t len) +{ + UNUSED(buf); + UNUSED(len); +} +# endif /* HAVE_WEAK_SYMBOLS */ + +static void* safe_memset(void *s, int c, size_t n) +{ + if (n > 0) { + volatile unsigned volatile_zero = 0; + volatile unsigned char *vs = (volatile unsigned char*)s; + + do { + memset(s, c, n); + } while (vs[volatile_zero] != (unsigned char)c); +# if defined(HAVE_WEAK_SYMBOLS) + __li_safe_memset_hook(s, n); +# endif /* HAVE_WEAK_SYMBOLS */ + } + + return s; +} +#endif /* !defined(HAVE_MEMSET_S) && !defined(HAVE_EXPLICIT_BZERO) */ + + +void safe_memclear(void *s, size_t n) { +#if defined(HAVE_MEMSET_S) + memset_s(s, n, 0, n); +#elif defined(HAVE_EXPLICIT_BZERO) + explicit_bzero(s, n); +#else + safe_memset(s, 0, n); +#endif +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/safe_memclear.h b/data/lighttpd/lighttpd-1.4.53/src/safe_memclear.h new file mode 100644 index 000000000..9fd256364 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/safe_memclear.h @@ -0,0 +1,7 @@ +#ifndef _SAFE_MEMCLEAR_H_ +#define _SAFE_MEMCLEAR_H_ +#include "first.h" + +void safe_memclear(void *s, size_t n); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/server.c b/data/lighttpd/lighttpd-1.4.53/src/server.c new file mode 100644 index 000000000..1be67c305 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/server.c @@ -0,0 +1,2119 @@ +#include "first.h" + +#include "server.h" +#include "buffer.h" +#include "burl.h" +#include "network.h" +#include "log.h" +#include "rand.h" +#include "chunk.h" +#include "http_auth.h" +#include "http_vhostdb.h" +#include "fdevent.h" +#include "connections.h" +#include "sock_addr.h" +#include "stat_cache.h" +#include "configfile.h" +#include "plugin.h" +#include "joblist.h" +#include "network_write.h" + +#ifdef HAVE_VERSIONSTAMP_H +# include "versionstamp.h" +#else +# define REPO_VERSION "" +#endif + +#define PACKAGE_DESC PACKAGE_NAME "/" PACKAGE_VERSION REPO_VERSION + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <time.h> +#include <signal.h> +#include <locale.h> + +#include <stdio.h> + +#ifdef HAVE_GETOPT_H +# include <getopt.h> +#endif + +#ifdef HAVE_VALGRIND_VALGRIND_H +# include <valgrind/valgrind.h> +#endif + +#ifdef HAVE_SYS_WAIT_H +# include <sys/wait.h> +#endif + +#ifdef HAVE_PWD_H +# include <grp.h> +# include <pwd.h> +#endif + +#ifdef HAVE_SYSLOG_H +# include <syslog.h> +#endif + +#ifdef HAVE_SYS_RESOURCE_H +# include <sys/resource.h> +#endif + +#ifdef HAVE_SYS_PRCTL_H +# include <sys/prctl.h> +#endif + +#include "sys-crypto.h" +#ifdef USE_OPENSSL_CRYPTO +#define USE_SSL +#define TEXT_SSL " (ssl)" +#else +#define TEXT_SSL +#endif + +#ifndef __sgi +/* IRIX doesn't like the alarm based time() optimization */ +/* #define USE_ALARM */ +#endif + +static int oneshot_fd = 0; +static volatile int pid_fd = -2; +static server_socket_array graceful_sockets; +static server_socket_array inherited_sockets; +static volatile sig_atomic_t graceful_restart = 0; +static volatile sig_atomic_t graceful_shutdown = 0; +static volatile sig_atomic_t srv_shutdown = 0; +static volatile sig_atomic_t handle_sig_child = 0; +static volatile sig_atomic_t handle_sig_alarm = 1; +static volatile sig_atomic_t handle_sig_hup = 0; + +#if defined(HAVE_SIGACTION) && defined(SA_SIGINFO) +static volatile siginfo_t last_sigterm_info; +static volatile siginfo_t last_sighup_info; + +static void sigaction_handler(int sig, siginfo_t *si, void *context) { + static const siginfo_t empty_siginfo; + UNUSED(context); + + if (!si) *(const siginfo_t **)&si = &empty_siginfo; + + switch (sig) { + case SIGTERM: + srv_shutdown = 1; + last_sigterm_info = *si; + break; + case SIGUSR1: + if (!graceful_shutdown) { + graceful_restart = 1; + graceful_shutdown = 1; + last_sigterm_info = *si; + } + break; + case SIGINT: + if (graceful_shutdown) { + if (2 == graceful_restart) + graceful_restart = 1; + else + srv_shutdown = 1; + } else { + graceful_shutdown = 1; + } + last_sigterm_info = *si; + + break; + case SIGALRM: + handle_sig_alarm = 1; + break; + case SIGHUP: + handle_sig_hup = 1; + last_sighup_info = *si; + break; + case SIGCHLD: + handle_sig_child = 1; + break; + } +} +#elif defined(HAVE_SIGNAL) || defined(HAVE_SIGACTION) +static void signal_handler(int sig) { + switch (sig) { + case SIGTERM: srv_shutdown = 1; break; + case SIGUSR1: + if (!graceful_shutdown) { + graceful_restart = 1; + graceful_shutdown = 1; + } + break; + case SIGINT: + if (graceful_shutdown) { + if (2 == graceful_restart) + graceful_restart = 1; + else + srv_shutdown = 1; + } else { + graceful_shutdown = 1; + } + break; + case SIGALRM: handle_sig_alarm = 1; break; + case SIGHUP: handle_sig_hup = 1; break; + case SIGCHLD: handle_sig_child = 1; break; + } +} +#endif + +#ifdef HAVE_FORK +static int daemonize(void) { + int pipefd[2]; + pid_t pid; +#ifdef SIGTTOU + signal(SIGTTOU, SIG_IGN); +#endif +#ifdef SIGTTIN + signal(SIGTTIN, SIG_IGN); +#endif +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + + if (pipe(pipefd) < 0) exit(-1); + + if (0 > (pid = fork())) exit(-1); + + if (0 < pid) { + char buf; + ssize_t bytes; + + close(pipefd[1]); + /* parent waits for grandchild to be ready */ + do { + bytes = read(pipefd[0], &buf, sizeof(buf)); + } while (bytes < 0 && EINTR == errno); + close(pipefd[0]); + + if (bytes <= 0) { + /* closed fd (without writing) == failure in grandchild */ + fputs("daemonized server failed to start; check error log for details\n", stderr); + exit(-1); + } + + exit(0); + } + + close(pipefd[0]); + + if (-1 == setsid()) exit(0); + + signal(SIGHUP, SIG_IGN); + + if (0 != fork()) exit(0); + + if (0 != chdir("/")) exit(0); + + fdevent_setfd_cloexec(pipefd[1]); + return pipefd[1]; +} +#endif + +static server *server_init(void) { + int i; + server *srv = calloc(1, sizeof(*srv)); + force_assert(srv); +#define CLEAN(x) \ + srv->x = buffer_init(); + + CLEAN(response_header); + CLEAN(parse_full_path); + CLEAN(ts_debug_str); + CLEAN(ts_date_str); + CLEAN(errorlog_buf); + CLEAN(response_range); + CLEAN(tmp_buf); + srv->empty_string = buffer_init_string(""); + CLEAN(cond_check_buf); + + CLEAN(srvconf.errorlog_file); + CLEAN(srvconf.breakagelog_file); + CLEAN(srvconf.groupname); + CLEAN(srvconf.username); + CLEAN(srvconf.changeroot); + CLEAN(srvconf.bindhost); + CLEAN(srvconf.event_handler); + CLEAN(srvconf.pid_file); + CLEAN(srvconf.syslog_facility); + + CLEAN(tmp_chunk_len); +#undef CLEAN + +#define CLEAN(x) \ + srv->x = array_init(); + + CLEAN(config_context); + CLEAN(config_touched); + CLEAN(status); +#undef CLEAN + + for (i = 0; i < FILE_CACHE_MAX; i++) { + srv->mtime_cache[i].mtime = (time_t)-1; + srv->mtime_cache[i].str = buffer_init(); + } + + li_rand_reseed(); + + srv->cur_ts = time(NULL); + srv->startup_ts = srv->cur_ts; + + srv->conns = calloc(1, sizeof(*srv->conns)); + force_assert(srv->conns); + + srv->joblist = calloc(1, sizeof(*srv->joblist)); + force_assert(srv->joblist); + + srv->fdwaitqueue = calloc(1, sizeof(*srv->fdwaitqueue)); + force_assert(srv->fdwaitqueue); + + srv->srvconf.modules = array_init(); + srv->srvconf.modules_dir = buffer_init_string(LIBRARY_DIR); + srv->srvconf.network_backend = buffer_init(); + srv->srvconf.upload_tempdirs = array_init(); + srv->srvconf.reject_expect_100_with_417 = 1; + srv->srvconf.xattr_name = buffer_init_string("Content-Type"); + srv->srvconf.http_header_strict = 1; + srv->srvconf.http_host_strict = 1; /*(implies http_host_normalize)*/ + srv->srvconf.http_host_normalize = 0; + #if 0 + srv->srvconf.http_url_normalize = HTTP_PARSEOPT_URL_NORMALIZE + | HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED + | HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT + | HTTP_PARSEOPT_URL_NORMALIZE_PATH_BACKSLASH_TRANS + | HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE + | HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE; + #endif + srv->srvconf.http_url_normalize = 0; /* temporary; change in future */ + srv->srvconf.high_precision_timestamps = 0; + srv->srvconf.max_request_field_size = 8192; + srv->srvconf.loadavg[0] = 0.0; + srv->srvconf.loadavg[1] = 0.0; + srv->srvconf.loadavg[2] = 0.0; + srv->srvconf.compat_module_load = 1; + srv->srvconf.systemd_socket_activation = 0; + + /* use syslog */ + srv->errorlog_fd = STDERR_FILENO; + srv->errorlog_mode = ERRORLOG_FD; + + srv->split_vals = array_init(); + srv->request_env = plugins_call_handle_request_env; + + return srv; +} + +static void server_free(server *srv) { + size_t i; + + for (i = 0; i < FILE_CACHE_MAX; i++) { + buffer_free(srv->mtime_cache[i].str); + } + + if (oneshot_fd > 0) { + close(oneshot_fd); + } + +#define CLEAN(x) \ + buffer_free(srv->x); + + CLEAN(response_header); + CLEAN(parse_full_path); + CLEAN(ts_debug_str); + CLEAN(ts_date_str); + CLEAN(errorlog_buf); + CLEAN(response_range); + CLEAN(tmp_buf); + CLEAN(empty_string); + CLEAN(cond_check_buf); + + CLEAN(srvconf.errorlog_file); + CLEAN(srvconf.breakagelog_file); + CLEAN(srvconf.groupname); + CLEAN(srvconf.username); + CLEAN(srvconf.changeroot); + CLEAN(srvconf.bindhost); + CLEAN(srvconf.event_handler); + CLEAN(srvconf.pid_file); + CLEAN(srvconf.modules_dir); + CLEAN(srvconf.network_backend); + CLEAN(srvconf.xattr_name); + CLEAN(srvconf.syslog_facility); + + CLEAN(tmp_chunk_len); +#undef CLEAN + +#if 0 + fdevent_unregister(srv->ev, srv->fd); +#endif + fdevent_free(srv->ev); + + free(srv->conns); + + if (srv->config_storage) { + for (i = 0; i < srv->config_context->used; i++) { + specific_config *s = srv->config_storage[i]; + + if (!s) continue; + + buffer_free(s->document_root); + buffer_free(s->server_name); + buffer_free(s->server_tag); + buffer_free(s->error_handler); + buffer_free(s->error_handler_404); + buffer_free(s->errorfile_prefix); + buffer_free(s->socket_perms); + array_free(s->mimetypes); + free(s); + } + free(srv->config_storage); + srv->config_storage = NULL; + } + +#define CLEAN(x) \ + array_free(srv->x); + + CLEAN(config_context); + CLEAN(config_touched); + CLEAN(status); + CLEAN(srvconf.upload_tempdirs); +#undef CLEAN + + joblist_free(srv, srv->joblist); + fdwaitqueue_free(srv, srv->fdwaitqueue); + + if (srv->stat_cache) { + stat_cache_free(srv->stat_cache); + } + + array_free(srv->srvconf.modules); + array_free(srv->split_vals); + + li_rand_cleanup(); + chunkqueue_chunk_pool_free(); + + free(srv); +} + +static void remove_pid_file(server *srv) { + if (pid_fd <= -2) return; + if (!buffer_string_is_empty(srv->srvconf.pid_file) && 0 <= pid_fd) { + if (0 != ftruncate(pid_fd, 0)) { + log_error_write(srv, __FILE__, __LINE__, "sbds", + "ftruncate failed for:", + srv->srvconf.pid_file, + errno, + strerror(errno)); + } + } + if (0 <= pid_fd) { + close(pid_fd); + pid_fd = -1; + } + if (!buffer_string_is_empty(srv->srvconf.pid_file) && + buffer_string_is_empty(srv->srvconf.changeroot)) { + if (0 != unlink(srv->srvconf.pid_file->ptr)) { + if (errno != EACCES && errno != EPERM) { + log_error_write(srv, __FILE__, __LINE__, "sbds", + "unlink failed for:", + srv->srvconf.pid_file, + errno, + strerror(errno)); + } + } + } +} + + +static server_socket * server_oneshot_getsock(server *srv, sock_addr *cnt_addr) { + server_socket *srv_socket, *srv_socket_wild = NULL; + size_t i; + for (i = 0; i < srv->srv_sockets.used; ++i) { + srv_socket = srv->srv_sockets.ptr[i]; + if (!sock_addr_is_port_eq(&srv_socket->addr,cnt_addr)) continue; + if (sock_addr_is_addr_eq(&srv_socket->addr,cnt_addr)) return srv_socket; + + if (NULL != srv_socket_wild) continue; + if (sock_addr_is_addr_wildcard(&srv_socket->addr)) { + srv_socket_wild = srv_socket; + } + } + + if (NULL != srv_socket_wild) { + return srv_socket_wild; + } else if (srv->srv_sockets.used) { + return srv->srv_sockets.ptr[0]; + } else { + log_error_write(srv, __FILE__, __LINE__, "s", "no sockets configured"); + return NULL; + } +} + + +static int server_oneshot_init(server *srv, int fd) { + /* Note: does not work with netcat due to requirement that fd be socket. + * STDOUT_FILENO was not saved earlier in startup, and that is to where + * netcat expects output to be sent. Since lighttpd expects connections + * to be sockets, con->fd is where output is sent; separate fds are not + * stored for input and output, but netcat has different fds for stdin + * and * stdout. To support netcat, would additionally need to avoid + * S_ISSOCK(), getsockname(), and getpeername() below, reconstructing + * addresses from environment variables: + * NCAT_LOCAL_ADDR NCAT_LOCAL_PORT + * NCAT_REMOTE_ADDR NCAT_REMOTE_PORT + * NCAT_PROTO + */ + connection *con; + server_socket *srv_socket; + sock_addr cnt_addr; + socklen_t cnt_len; + struct stat st; + + if (0 != fstat(fd, &st)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "fstat:", strerror(errno)); + return 0; + } + + if (!S_ISSOCK(st.st_mode)) { + /* require that fd is a socket + * (modules might expect STDIN_FILENO and STDOUT_FILENO opened to /dev/null) */ + log_error_write(srv, __FILE__, __LINE__, "s", "lighttpd -1 stdin is not a socket"); + return 0; + } + + cnt_len = sizeof(cnt_addr); + if (0 != getsockname(fd, (struct sockaddr *)&cnt_addr, &cnt_len)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "getsockname:", strerror(errno)); + return 0; + } + + srv_socket = server_oneshot_getsock(srv, &cnt_addr); + if (NULL == srv_socket) return 0; + + cnt_len = sizeof(cnt_addr); + if (0 != getpeername(fd, (struct sockaddr *)&cnt_addr, &cnt_len)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "getpeername:", strerror(errno)); + return 0; + } + + /*(must set flags; fd did not pass through fdevent accept() logic)*/ + if (-1 == fdevent_fcntl_set_nb_cloexec(srv->ev, fd)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl:", strerror(errno)); + return 0; + } + + if (sock_addr_get_family(&cnt_addr) != AF_UNIX) { + network_accept_tcp_nagle_disable(fd); + } + + con = connection_accepted(srv, srv_socket, &cnt_addr, fd); + if (NULL == con) return 0; + + connection_state_machine(srv, con); + return 1; +} + + +static void show_version (void) { + char *b = PACKAGE_DESC TEXT_SSL \ +" - a light and fast webserver\n" +#ifdef NONREPRODUCIBLE_BUILD +"Build-Date: " __DATE__ " " __TIME__ "\n"; +#endif +; + write_all(STDOUT_FILENO, b, strlen(b)); +} + +static void show_features (void) { + static const char features[] = + "\nFeatures:\n\n" +#ifdef HAVE_IPV6 + "\t+ IPv6 support\n" +#else + "\t- IPv6 support\n" +#endif +#if defined HAVE_ZLIB_H && defined HAVE_LIBZ + "\t+ zlib support\n" +#else + "\t- zlib support\n" +#endif +#if defined HAVE_BZLIB_H && defined HAVE_LIBBZ2 + "\t+ bzip2 support\n" +#else + "\t- bzip2 support\n" +#endif +#if defined(HAVE_CRYPT) || defined(HAVE_CRYPT_R) || defined(HAVE_LIBCRYPT) + "\t+ crypt support\n" +#else + "\t- crypt support\n" +#endif +#ifdef USE_SSL + "\t+ SSL support\n" +#else + "\t- SSL support\n" +#endif +#ifdef HAVE_LIBPCRE + "\t+ PCRE support\n" +#else + "\t- PCRE support\n" +#endif +#ifdef HAVE_MYSQL + "\t+ MySQL support\n" +#else + "\t- MySQL support\n" +#endif +#ifdef HAVE_PGSQL + "\t+ PgSQL support\n" +#else + "\t- PgSQL support\n" +#endif +#ifdef HAVE_DBI + "\t+ DBI support\n" +#else + "\t- DBI support\n" +#endif +#ifdef HAVE_KRB5 + "\t+ Kerberos support\n" +#else + "\t- Kerberos support\n" +#endif +#if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER) + "\t+ LDAP support\n" +#else + "\t- LDAP support\n" +#endif +#ifdef HAVE_PAM + "\t+ PAM support\n" +#else + "\t- PAM support\n" +#endif +#ifdef USE_MEMCACHED + "\t+ memcached support\n" +#else + "\t- memcached support\n" +#endif +#ifdef HAVE_FAM_H + "\t+ FAM support\n" +#else + "\t- FAM support\n" +#endif +#ifdef HAVE_LUA_H + "\t+ LUA support\n" +#else + "\t- LUA support\n" +#endif +#ifdef HAVE_LIBXML_H + "\t+ xml support\n" +#else + "\t- xml support\n" +#endif +#ifdef HAVE_SQLITE3_H + "\t+ SQLite support\n" +#else + "\t- SQLite support\n" +#endif +#ifdef HAVE_GDBM_H + "\t+ GDBM support\n" +#else + "\t- GDBM support\n" +#endif + ; + show_version(); + printf("%s%s%s\n", fdevent_show_event_handlers(), network_write_show_handlers(), features); +} + +static void show_help (void) { + char *b = PACKAGE_DESC TEXT_SSL +#ifdef NONREPRODUCIBLE_BUILD +" ("__DATE__ " " __TIME__ ")" +#endif +" - a light and fast webserver\n" \ +"usage:\n" \ +" -f <name> filename of the config-file\n" \ +" -m <name> module directory (default: "LIBRARY_DIR")\n" \ +" -i <secs> graceful shutdown after <secs> of inactivity\n" \ +" -1 process single (one) request on stdin socket, then exit\n" \ +" -p print the parsed config-file in internal form, and exit\n" \ +" -t test config-file syntax, then exit\n" \ +" -tt test config-file syntax, load and init modules, then exit\n" \ +" -D don't go to background (default: go to background)\n" \ +" -v show version\n" \ +" -V show compile-time features\n" \ +" -h show this help\n" \ +"\n" +; + write_all(STDOUT_FILENO, b, strlen(b)); +} + +/** + * open the errorlog + * + * we have 4 possibilities: + * - stderr (default) + * - syslog + * - logfile + * - pipe + * + */ + +static int log_error_open(server *srv) { + int errfd; + #ifdef HAVE_SYSLOG_H + /* perhaps someone wants to use syslog() */ + int facility = -1; + if (!buffer_string_is_empty(srv->srvconf.syslog_facility)) { + static const struct facility_name_st { + const char *name; + int val; + } facility_names[] = { + { "auth", LOG_AUTH } + #ifdef LOG_AUTHPRIV + ,{ "authpriv", LOG_AUTHPRIV } + #endif + #ifdef LOG_CRON + ,{ "cron", LOG_CRON } + #endif + ,{ "daemon", LOG_DAEMON } + #ifdef LOG_FTP + ,{ "ftp", LOG_FTP } + #endif + #ifdef LOG_KERN + ,{ "kern", LOG_KERN } + #endif + #ifdef LOG_LPR + ,{ "lpr", LOG_LPR } + #endif + #ifdef LOG_MAIL + ,{ "mail", LOG_MAIL } + #endif + #ifdef LOG_NEWS + ,{ "news", LOG_NEWS } + #endif + ,{ "security", LOG_AUTH } /* DEPRECATED */ + #ifdef LOG_SYSLOG + ,{ "syslog", LOG_SYSLOG } + #endif + #ifdef LOG_USER + ,{ "user", LOG_USER } + #endif + #ifdef LOG_UUCP + ,{ "uucp", LOG_UUCP } + #endif + ,{ "local0", LOG_LOCAL0 } + ,{ "local1", LOG_LOCAL1 } + ,{ "local2", LOG_LOCAL2 } + ,{ "local3", LOG_LOCAL3 } + ,{ "local4", LOG_LOCAL4 } + ,{ "local5", LOG_LOCAL5 } + ,{ "local6", LOG_LOCAL6 } + ,{ "local7", LOG_LOCAL7 } + }; + unsigned int i; + for (i = 0; i < sizeof(facility_names)/sizeof(facility_names[0]); ++i) { + const struct facility_name_st *f = facility_names+i; + if (0 == strcmp(srv->srvconf.syslog_facility->ptr, f->name)) { + facility = f->val; + break; + } + } + if (-1 == facility) { + log_error_write(srv, __FILE__, __LINE__, "SBS", + "unrecognized server.syslog-facility: \"", + srv->srvconf.syslog_facility, + "\"; defaulting to \"daemon\" facility"); + } + } + openlog("lighttpd", LOG_CONS|LOG_PID, -1==facility ? LOG_DAEMON : facility); + #endif + + srv->errorlog_mode = ERRORLOG_FD; + srv->errorlog_fd = STDERR_FILENO; + + if (srv->srvconf.errorlog_use_syslog) { + srv->errorlog_mode = ERRORLOG_SYSLOG; + } + else if (!buffer_string_is_empty(srv->srvconf.errorlog_file)) { + const char *logfile = srv->srvconf.errorlog_file->ptr; + int fd = fdevent_open_logger(logfile); + if (-1 == fd) { + log_error_write(srv, __FILE__, __LINE__, "SSSS", + "opening errorlog '", logfile, + "' failed: ", strerror(errno)); + return -1; + } + srv->errorlog_fd = fd; + srv->errorlog_mode = logfile[0] == '|' ? ERRORLOG_PIPE : ERRORLOG_FILE; + } + + if (srv->errorlog_mode == ERRORLOG_FD && !srv->srvconf.dont_daemonize) { + /* We can only log to stderr in dont-daemonize mode; + * if we do daemonize and no errorlog file is specified, + * we log into /dev/null + */ + srv->errorlog_fd = -1; + } + + if (!buffer_string_is_empty(srv->srvconf.breakagelog_file)) { + const char *logfile = srv->srvconf.breakagelog_file->ptr; + + if (srv->errorlog_mode == ERRORLOG_FD) { + srv->errorlog_fd = dup(STDERR_FILENO); + fdevent_setfd_cloexec(srv->errorlog_fd); + } + + if (-1 == (errfd = fdevent_open_logger(logfile))) { + log_error_write(srv, __FILE__, __LINE__, "SSSS", + "opening errorlog '", logfile, + "' failed: ", strerror(errno)); + return -1; + } + + if (*logfile == '|') fdevent_breakagelog_logger_pipe(errfd); + } + else if (!srv->srvconf.dont_daemonize) { + /* move STDERR_FILENO to /dev/null */ + if (-1 == (errfd = fdevent_open_devnull())) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "opening /dev/null failed:", strerror(errno)); + return -1; + } + } + else { + /*(leave STDERR_FILENO as-is)*/ + errfd = -1; + } + + if (0 != fdevent_set_stdin_stdout_stderr(-1, -1, errfd)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "setting stderr failed:", strerror(errno)); + #ifdef FD_CLOEXEC + if (-1 != errfd) close(errfd); + #endif + return -1; + } + #ifdef FD_CLOEXEC + if (-1 != errfd) close(errfd); + #endif + + return 0; +} + +/** + * cycle the errorlog + * + */ + +static int log_error_cycle(server *srv) { + /* cycle only if the error log is a file */ + + if (srv->errorlog_mode == ERRORLOG_FILE) { + const char *logfile = srv->srvconf.errorlog_file->ptr; + if (-1 == fdevent_cycle_logger(logfile, &srv->errorlog_fd)) { + /* write to old log */ + log_error_write(srv, __FILE__, __LINE__, "SSSS", + "cycling errorlog '", logfile, + "' failed: ", strerror(errno)); + } + } + + return 0; +} + +static int log_error_close(server *srv) { + switch(srv->errorlog_mode) { + case ERRORLOG_PIPE: + case ERRORLOG_FILE: + case ERRORLOG_FD: + if (-1 != srv->errorlog_fd) { + /* don't close STDERR */ + /* fdevent_close_logger_pipes() closes ERRORLOG_PIPE */ + if (STDERR_FILENO != srv->errorlog_fd + && srv->errorlog_mode != ERRORLOG_PIPE) { + close(srv->errorlog_fd); + } + srv->errorlog_fd = -1; + } + break; + case ERRORLOG_SYSLOG: + #ifdef HAVE_SYSLOG_H + closelog(); + #endif + break; + } + + return 0; +} + +static void server_sockets_save (server *srv) { /* graceful_restart */ + memcpy(&graceful_sockets, &srv->srv_sockets, sizeof(server_socket_array)); + memset(&srv->srv_sockets, 0, sizeof(server_socket_array)); + memcpy(&inherited_sockets, &srv->srv_sockets_inherited, sizeof(server_socket_array)); + memset(&srv->srv_sockets_inherited, 0, sizeof(server_socket_array)); +} + +static void server_sockets_restore (server *srv) { /* graceful_restart */ + memcpy(&srv->srv_sockets, &graceful_sockets, sizeof(server_socket_array)); + memset(&graceful_sockets, 0, sizeof(server_socket_array)); + memcpy(&srv->srv_sockets_inherited, &inherited_sockets, sizeof(server_socket_array)); + memset(&inherited_sockets, 0, sizeof(server_socket_array)); +} + +static int server_sockets_set_nb_cloexec (server *srv) { + if (srv->sockets_disabled) return 0; /* lighttpd -1 (one-shot mode) */ + for (size_t i = 0; i < srv->srv_sockets.used; ++i) { + server_socket *srv_socket = srv->srv_sockets.ptr[i]; + if (-1 == fdevent_fcntl_set_nb_cloexec_sock(srv->ev, srv_socket->fd)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "fcntl failed:", strerror(errno)); + return -1; + } + } + return 0; +} + +static void server_sockets_set_event (server *srv, int event) { + for (size_t i = 0; i < srv->srv_sockets.used; ++i) { + server_socket *srv_socket = srv->srv_sockets.ptr[i]; + fdevent_event_set(srv->ev,&(srv_socket->fde_ndx),srv_socket->fd,event); + } +} + +static void server_sockets_unregister (server *srv) { + for (size_t i = 0; i < srv->srv_sockets.used; ++i) + network_unregister_sock(srv, srv->srv_sockets.ptr[i]); +} + +static void server_sockets_close (server *srv) { + /* closing socket right away will make it possible for the next lighttpd + * to take over (old-style graceful restart), but only if backends + * (e.g. fastcgi, scgi, etc) are independent from lighttpd, rather + * than started by lighttpd via "bin-path") + */ + for (size_t i = 0; i < srv->srv_sockets.used; ++i) { + server_socket *srv_socket = srv->srv_sockets.ptr[i]; + if (-1 == srv_socket->fd) continue; + network_unregister_sock(srv, srv_socket); + close(srv_socket->fd); + srv_socket->fd = -1; + /* network_close() will cleanup after us */ + } +} + +static void server_graceful_shutdown_maint (server *srv) { + connections *conns = srv->conns; + for (size_t ndx = 0; ndx < conns->used; ++ndx) { + connection * const con = conns->ptr[ndx]; + int changed = 0; + + if (con->state == CON_STATE_CLOSE) { + /* reduce remaining linger timeout to be + * (from zero) *up to* one more second, but no more */ + if (HTTP_LINGER_TIMEOUT > 1) + con->close_timeout_ts -= (HTTP_LINGER_TIMEOUT - 1); + if (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT) + changed = 1; + } + else if (con->state == CON_STATE_READ && con->request_count > 1 + && chunkqueue_is_empty(con->read_queue)) { + /* close connections in keep-alive waiting for next request */ + connection_set_state(srv, con, CON_STATE_ERROR); + changed = 1; + } + + con->keep_alive = 0; /* disable keep-alive */ + + con->conf.kbytes_per_second = 0; /* disable rate limit */ + con->conf.global_kbytes_per_second = 0; /* disable rate limit */ + if (con->traffic_limit_reached) { + con->traffic_limit_reached = 0; + changed = 1; + } + + if (changed) { + connection_state_machine(srv, con); + } + } +} + +static void server_graceful_state (server *srv) { + + if (!srv_shutdown) server_graceful_shutdown_maint(srv); + + if (!oneshot_fd) { + if (0==srv->srv_sockets.used || -1 == srv->srv_sockets.ptr[0]->fde_ndx) + return; + } + + log_error_write(srv, __FILE__, __LINE__, "s", + "[note] graceful shutdown started"); + + /* no graceful restart if chroot()ed, if oneshot mode, or if idle timeout */ + if (!buffer_string_is_empty(srv->srvconf.changeroot) + || oneshot_fd || 2 == graceful_shutdown) + graceful_restart = 0; + + if (graceful_restart) { + server_sockets_unregister(srv); + if (pid_fd > 0) pid_fd = -pid_fd; /*(flag to skip removing pid file)*/ + } + else { + server_sockets_close(srv); + remove_pid_file(srv); + buffer_clear(srv->srvconf.pid_file); /*(prevent more removal attempts)*/ + } +} + +static int server_main (server * const srv, int argc, char **argv) { + int print_config = 0; + int test_config = 0; + int i_am_root = 0; + int o; +#ifdef HAVE_FORK + int num_childs = 0; +#endif + size_t i; + time_t idle_limit = 0, last_active_ts = time(NULL); +#ifdef HAVE_SIGACTION + struct sigaction act; +#endif + +#ifdef HAVE_FORK + int parent_pipe_fd = -1; +#endif + int stdin_fd = -1; + +#ifdef HAVE_GETUID + i_am_root = (0 == getuid()); +#endif + + /* initialize globals (including file-scoped static globals) */ + oneshot_fd = 0; + srv_shutdown = 0; + graceful_shutdown = 0; + handle_sig_alarm = 1; + handle_sig_hup = 0; + chunkqueue_set_tempdirs_default_reset(); + http_auth_dumbdata_reset(); + http_vhostdb_dumbdata_reset(); + /*graceful_restart = 0;*//*(reset below to avoid further daemonizing)*/ + /*(intentionally preserved)*/ + /*memset(graceful_sockets, 0, sizeof(graceful_sockets));*/ + /*memset(inherited_sockets, 0, sizeof(inherited_sockets));*/ + /*pid_fd = -1;*/ + + srv->srvconf.port = 0; + srv->srvconf.dont_daemonize = 0; + srv->srvconf.preflight_check = 0; + + while(-1 != (o = getopt(argc, argv, "f:m:i:hvVD1pt"))) { + switch(o) { + case 'f': + if (srv->config_storage) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Can only read one config file. Use the include command to use multiple config files."); + return -1; + } + if (config_read(srv, optarg)) { + return -1; + } + break; + case 'm': + buffer_copy_string(srv->srvconf.modules_dir, optarg); + break; + case 'i': { + char *endptr; + long timeout = strtol(optarg, &endptr, 0); + if (!*optarg || *endptr || timeout < 0) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "Invalid idle timeout value:", optarg); + return -1; + } + idle_limit = (time_t)timeout; + break; + } + case 'p': print_config = 1; break; + case 't': ++test_config; break; + case '1': if (0 == oneshot_fd) oneshot_fd = dup(STDIN_FILENO); + break; + case 'D': srv->srvconf.dont_daemonize = 1; break; + case 'v': show_version(); return 0; + case 'V': show_features(); return 0; + case 'h': show_help(); return 0; + default: + show_help(); + return -1; + } + } + + #ifdef __CYGWIN__ + if (!srv->config_storage && NULL != getenv("NSSM_SERVICE_NAME")) { + char *dir = getenv("NSSM_SERVICE_DIR"); + if (NULL != dir && 0 != chdir(dir)) { + log_error_write(srv, __FILE__, __LINE__, "sss", "chdir failed:", dir, strerror(errno)); + return -1; + } + srv->srvconf.dont_daemonize = 1; + buffer_copy_string_len(srv->srvconf.modules_dir, CONST_STR_LEN("modules")); + if (config_read(srv, "conf/lighttpd.conf")) return -1; + } + #endif + + if (!srv->config_storage) { + log_error_write(srv, __FILE__, __LINE__, "s", + "No configuration available. Try using -f option."); + return -1; + } + + if (print_config) { + data_unset *dc = srv->config_context->data[0]; + if (dc) { + dc->fn->print(dc, 0); + fprintf(stdout, "\n"); + } else { + /* shouldn't happend */ + fprintf(stderr, "global config not found\n"); + } + } + + if (test_config) { + buffer_clear(srv->srvconf.pid_file); + if (1 == test_config) { + printf("Syntax OK\n"); + } else { /*(test_config > 1)*/ + test_config = 0; + srv->srvconf.preflight_check = 1; + srv->srvconf.dont_daemonize = 1; + } + } + + if (test_config || print_config) { + return 0; + } + + if (oneshot_fd) { + if (oneshot_fd <= STDERR_FILENO) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Invalid fds at startup with lighttpd -1"); + return -1; + } + graceful_shutdown = 1; + srv->sockets_disabled = 1; + srv->srvconf.dont_daemonize = 1; + buffer_clear(srv->srvconf.pid_file); + if (srv->srvconf.max_worker) { + srv->srvconf.max_worker = 0; + log_error_write(srv, __FILE__, __LINE__, "s", + "server one-shot command line option disables server.max-worker config file option."); + } + } + + if (buffer_is_equal_string(srv->srvconf.bindhost, CONST_STR_LEN("/dev/stdin"))) { + stdin_fd = dup(STDIN_FILENO); + if (stdin_fd <= STDERR_FILENO) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Invalid fds at startup"); + return -1; + } + } + + /* close stdin and stdout, as they are not needed */ + { + struct stat st; + int devnull; + int errfd; + do { + /* coverity[overwrite_var : FALSE] */ + devnull = fdevent_open_devnull(); + #ifdef __COVERITY__ + __coverity_escape__(devnull); + #endif + } while (-1 != devnull && devnull <= STDERR_FILENO); + if (-1 == devnull) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "opening /dev/null failed:", strerror(errno)); + return -1; + } + errfd = (0 == fstat(STDERR_FILENO, &st)) ? -1 : devnull; + if (0 != fdevent_set_stdin_stdout_stderr(devnull, devnull, errfd)) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "setting default fds failed:", strerror(errno)); + #ifdef FD_CLOEXEC + if (-1 != errfd) close(errfd); + if (devnull != errfd) close(devnull); + #endif + return -1; + } + #ifdef FD_CLOEXEC + if (-1 != errfd) close(errfd); + if (devnull != errfd) close(devnull); + #endif + } + + if (0 != config_set_defaults(srv)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "setting default values failed"); + return -1; + } + + /* check document-root */ + if (buffer_string_is_empty(srv->config_storage[0]->document_root)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "document-root is not set\n"); + return -1; + } + + if (plugins_load(srv)) { + log_error_write(srv, __FILE__, __LINE__, "s", + "loading plugins finally failed"); + return -1; + } + + if (HANDLER_GO_ON != plugins_call_init(srv)) { + log_error_write(srv, __FILE__, __LINE__, "s", "Initialization of plugins failed. Going down."); + return -1; + } + + /* mod_indexfile should be listed in server.modules prior to dynamic handlers */ + i = 0; + for (buffer *pname = NULL; i < srv->plugins.used; ++i) { + plugin *p = ((plugin **)srv->plugins.ptr)[i]; + if (buffer_is_equal_string(p->name, CONST_STR_LEN("indexfile"))) { + if (pname) { + log_error_write(srv, __FILE__, __LINE__, "SB", + "Warning: mod_indexfile should be listed in server.modules prior to mod_", pname); + } + break; + } + if (p->handle_subrequest_start && p->handle_subrequest) { + if (!pname) pname = p->name; + } + } + + /* open pid file BEFORE chroot */ + if (-2 == pid_fd) pid_fd = -1; /*(initial startup state)*/ + if (-1 == pid_fd && !buffer_string_is_empty(srv->srvconf.pid_file)) { + if (-1 == (pid_fd = fdevent_open_cloexec(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { + struct stat st; + if (errno != EEXIST) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); + return -1; + } + + if (0 != stat(srv->srvconf.pid_file->ptr, &st)) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "stating existing pid-file failed:", srv->srvconf.pid_file, strerror(errno)); + } + + if (!S_ISREG(st.st_mode)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "pid-file exists and isn't regular file:", srv->srvconf.pid_file); + return -1; + } + + if (-1 == (pid_fd = fdevent_open_cloexec(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); + return -1; + } + } + } + + { +#ifdef HAVE_GETRLIMIT + struct rlimit rlim; + int use_rlimit = 1; +#ifdef HAVE_VALGRIND_VALGRIND_H + if (RUNNING_ON_VALGRIND) use_rlimit = 0; +#endif + + if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { + log_error_write(srv, __FILE__, __LINE__, + "ss", "couldn't get 'max filedescriptors'", + strerror(errno)); + return -1; + } + + /** + * if we are not root can can't increase the fd-limit above rlim_max, but we can reduce it + */ + if (use_rlimit && srv->srvconf.max_fds + && (i_am_root || srv->srvconf.max_fds <= rlim.rlim_max)) { + /* set rlimits */ + + rlim.rlim_cur = srv->srvconf.max_fds; + if (i_am_root) rlim.rlim_max = srv->srvconf.max_fds; + + if (0 != setrlimit(RLIMIT_NOFILE, &rlim)) { + log_error_write(srv, __FILE__, __LINE__, + "ss", "couldn't set 'max filedescriptors'", + strerror(errno)); + return -1; + } + } + + srv->max_fds = rlim.rlim_cur; + /*(default upper limit of 4k if server.max-fds not specified)*/ + if (i_am_root && 0 == srv->srvconf.max_fds && rlim.rlim_cur > 4096) + srv->max_fds = 4096; + + /* set core file rlimit, if enable_cores is set */ + if (use_rlimit && srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { + rlim.rlim_cur = rlim.rlim_max; + setrlimit(RLIMIT_CORE, &rlim); + } +#endif + } + + /* we need root-perms for port < 1024 */ + if (0 != network_init(srv, stdin_fd)) { + return -1; + } + + if (i_am_root) { +#ifdef HAVE_PWD_H + /* set user and group */ + struct group *grp = NULL; + struct passwd *pwd = NULL; + + if (!buffer_string_is_empty(srv->srvconf.groupname)) { + if (NULL == (grp = getgrnam(srv->srvconf.groupname->ptr))) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "can't find groupname", srv->srvconf.groupname); + return -1; + } + } + + if (!buffer_string_is_empty(srv->srvconf.username)) { + if (NULL == (pwd = getpwnam(srv->srvconf.username->ptr))) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "can't find username", srv->srvconf.username); + return -1; + } + + if (pwd->pw_uid == 0) { + log_error_write(srv, __FILE__, __LINE__, "s", + "I will not set uid to 0\n"); + return -1; + } + + if (NULL == grp && NULL == (grp = getgrgid(pwd->pw_gid))) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "can't find group id", pwd->pw_gid); + return -1; + } + } + + if (NULL != grp) { + if (grp->gr_gid == 0) { + log_error_write(srv, __FILE__, __LINE__, "s", + "I will not set gid to 0\n"); + return -1; + } + } + + /* + * Change group before chroot, when we have access + * to /etc/group + * */ + if (NULL != grp) { + if (-1 == setgid(grp->gr_gid)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "setgid failed: ", strerror(errno)); + return -1; + } + if (-1 == setgroups(0, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "setgroups failed: ", strerror(errno)); + return -1; + } + if (!buffer_string_is_empty(srv->srvconf.username)) { + initgroups(srv->srvconf.username->ptr, grp->gr_gid); + } + } +#endif +#ifdef HAVE_CHROOT + if (!buffer_string_is_empty(srv->srvconf.changeroot)) { + tzset(); + + if (-1 == chroot(srv->srvconf.changeroot->ptr)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "chroot failed: ", strerror(errno)); + return -1; + } + if (-1 == chdir("/")) { + log_error_write(srv, __FILE__, __LINE__, "ss", "chdir failed: ", strerror(errno)); + return -1; + } + } +#endif +#ifdef HAVE_PWD_H + /* drop root privs */ + if (NULL != pwd) { + if (-1 == setuid(pwd->pw_uid)) { + log_error_write(srv, __FILE__, __LINE__, "ss", "setuid failed: ", strerror(errno)); + return -1; + } + } +#endif +#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) + /** + * on IRIX 6.5.30 they have prctl() but no DUMPABLE + */ + if (srv->srvconf.enable_cores) { + prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + } +#endif + } + + /* set max-conns */ + if (srv->srvconf.max_conns > srv->max_fds/2) { + /* we can't have more connections than max-fds/2 */ + log_error_write(srv, __FILE__, __LINE__, "sdd", "can't have more connections than fds/2: ", srv->srvconf.max_conns, srv->max_fds); + srv->max_conns = srv->max_fds/2; + } else if (srv->srvconf.max_conns) { + /* otherwise respect the wishes of the user */ + srv->max_conns = srv->srvconf.max_conns; + } else { + /* or use the default: we really don't want to hit max-fds */ + srv->max_conns = srv->max_fds/3; + } + +#ifdef HAVE_FORK + /* network is up, let's daemonize ourself */ + if (0 == srv->srvconf.dont_daemonize && 0 == graceful_restart) { + parent_pipe_fd = daemonize(); + } +#endif + graceful_restart = 0;/*(reset here after avoiding further daemonizing)*/ + if (0 == oneshot_fd) graceful_shutdown = 0; + + +#ifdef HAVE_SIGACTION + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); +# if defined(SA_SIGINFO) + last_sighup_info.si_uid = 0, + last_sighup_info.si_pid = 0; + last_sigterm_info.si_uid = 0, + last_sigterm_info.si_pid = 0; + act.sa_sigaction = sigaction_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; +# else + act.sa_handler = signal_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; +# endif + sigaction(SIGINT, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGALRM, &act, NULL); + sigaction(SIGUSR1, &act, NULL); + + /* it should be safe to restart syscalls after SIGCHLD */ + act.sa_flags |= SA_RESTART | SA_NOCLDSTOP; + sigaction(SIGCHLD, &act, NULL); + +#elif defined(HAVE_SIGNAL) + /* ignore the SIGPIPE from sendfile() */ + signal(SIGPIPE, SIG_IGN); + signal(SIGALRM, signal_handler); + signal(SIGTERM, signal_handler); + signal(SIGHUP, signal_handler); + signal(SIGCHLD, signal_handler); + signal(SIGINT, signal_handler); + signal(SIGUSR1, signal_handler); +#endif + + + srv->gid = getgid(); + srv->uid = getuid(); + srv->pid = getpid(); + + /* write pid file */ + if (pid_fd > 2) { + buffer_copy_int(srv->tmp_buf, srv->pid); + buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\n")); + if (-1 == write_all(pid_fd, CONST_BUF_LEN(srv->tmp_buf))) { + log_error_write(srv, __FILE__, __LINE__, "ss", "Couldn't write pid file:", strerror(errno)); + close(pid_fd); + pid_fd = -1; + return -1; + } + } else if (pid_fd < -2) { + pid_fd = -pid_fd; + } + + /* Close stderr ASAP in the child process to make sure that nothing + * is being written to that fd which may not be valid anymore. */ + if (!srv->srvconf.preflight_check) { + if (-1 == log_error_open(srv)) { + log_error_write(srv, __FILE__, __LINE__, "s", "Opening errorlog failed. Going down."); + return -1; + } + log_error_write(srv, __FILE__, __LINE__, "s", "server started (" PACKAGE_DESC ")"); + } + + if (buffer_is_empty(srv->config_storage[0]->server_tag)) { + buffer_copy_string_len(srv->config_storage[0]->server_tag, CONST_STR_LEN(PACKAGE_DESC)); + } + + if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { + log_error_write(srv, __FILE__, __LINE__, "s", "Configuration of plugins failed. Going down."); + return -1; + } + + /* settings might be enabled during module config set defaults */ + srv->config_storage[0]->high_precision_timestamps = srv->srvconf.high_precision_timestamps; + + /* dump unused config-keys */ + for (i = 0; i < srv->config_context->used; i++) { + array *config = ((data_config *)srv->config_context->data[i])->value; + size_t j; + + for (j = 0; config && j < config->used; j++) { + data_unset *du = config->data[j]; + + /* all var.* is known as user defined variable */ + if (strncmp(du->key->ptr, "var.", sizeof("var.") - 1) == 0) { + continue; + } + + if (NULL == array_get_element_klen(srv->config_touched, CONST_BUF_LEN(du->key))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "WARNING: unknown config-key:", + du->key, + "(ignored)"); + } + } + } + + if (srv->config_unsupported) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Configuration contains unsupported keys. Going down."); + } + + if (srv->config_deprecated) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Configuration contains deprecated keys. Going down."); + } + + if (srv->config_unsupported || srv->config_deprecated) { + return -1; + } + + if (srv->srvconf.preflight_check) { + /*printf("Preflight OK");*//*(stdout reopened to /dev/null)*/ + return 0; + } + + +#ifdef HAVE_FORK + /** + * notify daemonize-grandparent of successful startup + * do this before any further forking is done (workers) + */ + if (0 == srv->srvconf.dont_daemonize && -1 != parent_pipe_fd) { + if (0 > write(parent_pipe_fd, "", 1)) return -1; + close(parent_pipe_fd); + } + + if (idle_limit && srv->srvconf.max_worker) { + srv->srvconf.max_worker = 0; + log_error_write(srv, __FILE__, __LINE__, "s", + "server idle time limit command line option disables server.max-worker config file option."); + } + + /* start watcher and workers */ + num_childs = srv->srvconf.max_worker; + if (num_childs > 0) { + pid_t pids[num_childs]; + pid_t pid; + const int npids = num_childs; + int child = 0; + unsigned int timer = 0; + for (int n = 0; n < npids; ++n) pids[n] = -1; + while (!child && !srv_shutdown && !graceful_shutdown) { + if (num_childs > 0) { + switch ((pid = fork())) { + case -1: + return -1; + case 0: + child = 1; + alarm(0); + break; + default: + num_childs--; + for (int n = 0; n < npids; ++n) { + if (-1 == pids[n]) { + pids[n] = pid; + break; + } + } + break; + } + } else { + int status; + + if (-1 != (pid = wait(&status))) { + srv->cur_ts = time(NULL); + if (plugins_call_handle_waitpid(srv, pid, status) != HANDLER_GO_ON) { + if (!timer) alarm((timer = 5)); + continue; + } + switch (fdevent_reaped_logger_pipe(pid)) { + default: break; + case -1: if (!timer) alarm((timer = 5)); + /* fall through */ + case 1: continue; + } + /** + * check if one of our workers went away + */ + for (int n = 0; n < npids; ++n) { + if (pid == pids[n]) { + pids[n] = -1; + num_childs++; + break; + } + } + } else { + switch (errno) { + case EINTR: + srv->cur_ts = time(NULL); + /** + * if we receive a SIGHUP we have to close our logs ourself as we don't + * have the mainloop who can help us here + */ + if (handle_sig_hup) { + handle_sig_hup = 0; + + log_error_cycle(srv); + + /* forward SIGHUP to workers */ + for (int n = 0; n < npids; ++n) { + if (pids[n] > 0) kill(pids[n], SIGHUP); + } + } + if (handle_sig_alarm) { + handle_sig_alarm = 0; + timer = 0; + plugins_call_handle_trigger(srv); + fdevent_restart_logger_pipes(srv->cur_ts); + } + break; + default: + break; + } + } + } + } + + /** + * for the parent this is the exit-point + */ + if (!child) { + /** + * kill all children too + */ + if (graceful_shutdown || graceful_restart) { + /* flag to ignore one SIGINT if graceful_restart */ + if (graceful_restart) graceful_restart = 2; + kill(0, SIGINT); + server_graceful_state(srv); + } else if (srv_shutdown) { + kill(0, SIGTERM); + } + + return 0; + } + + /* ignore SIGUSR1 in workers; only parent directs graceful restart */ + #ifdef HAVE_SIGACTION + { + struct sigaction actignore; + memset(&actignore, 0, sizeof(actignore)); + actignore.sa_handler = SIG_IGN; + sigaction(SIGUSR1, &actignore, NULL); + } + #elif defined(HAVE_SIGNAL) + signal(SIGUSR1, SIG_IGN); + #endif + + /** + * make sure workers do not muck with pid-file + */ + if (0 <= pid_fd) { + close(pid_fd); + pid_fd = -1; + } + buffer_clear(srv->srvconf.pid_file); + + fdevent_clr_logger_pipe_pids(); + srv->pid = getpid(); + li_rand_reseed(); + } +#endif + + if (NULL == (srv->ev = fdevent_init(srv))) { + log_error_write(srv, __FILE__, __LINE__, + "s", "fdevent_init failed"); + return -1; + } + + /* libev backend overwrites our SIGCHLD handler and calls waitpid on SIGCHLD; we want our own SIGCHLD handling. */ +#ifdef HAVE_SIGACTION + sigaction(SIGCHLD, &act, NULL); +#elif defined(HAVE_SIGNAL) + signal(SIGCHLD, signal_handler); +#endif + + /* + * kqueue() is called here, select resets its internals, + * all server sockets get their handlers + * + * */ + if (0 != network_register_fdevents(srv)) { + return -1; + } + + /* might fail if user is using fam (not gamin) and famd isn't running */ + if (NULL == (srv->stat_cache = stat_cache_init(srv))) { + log_error_write(srv, __FILE__, __LINE__, "s", + "stat-cache could not be setup, dieing."); + return -1; + } + +#ifdef USE_ALARM + { + /* setup periodic timer (1 second) */ + struct itimerval interval; + interval.it_interval.tv_sec = 1; + interval.it_interval.tv_usec = 0; + interval.it_value.tv_sec = 1; + interval.it_value.tv_usec = 0; + if (setitimer(ITIMER_REAL, &interval, NULL)) { + log_error_write(srv, __FILE__, __LINE__, "s", "setting timer failed"); + return -1; + } + } +#endif + + + /* get the current number of FDs */ + { + int fd = fdevent_open_devnull(); + if (fd >= 0) { + srv->cur_fds = fd; + close(fd); + } + } + + if (0 != server_sockets_set_nb_cloexec(srv)) { + return -1; + } + + if (oneshot_fd && server_oneshot_init(srv, oneshot_fd)) { + oneshot_fd = -1; + } + + /* main-loop */ + while (!srv_shutdown) { + int n; + size_t ndx; + time_t min_ts; + + if (handle_sig_hup) { + handler_t r; + + /* reset notification */ + handle_sig_hup = 0; + + + /* cycle logfiles */ + + switch(r = plugins_call_handle_sighup(srv)) { + case HANDLER_GO_ON: + break; + default: + log_error_write(srv, __FILE__, __LINE__, "sd", "sighup-handler return with an error", r); + break; + } + + if (-1 == log_error_cycle(srv)) { + log_error_write(srv, __FILE__, __LINE__, "s", "cycling errorlog failed, dying"); + + return -1; + } else { +#ifdef HAVE_SIGACTION + log_error_write(srv, __FILE__, __LINE__, "sdsd", + "logfiles cycled UID =", + last_sighup_info.si_uid, + "PID =", + last_sighup_info.si_pid); +#else + log_error_write(srv, __FILE__, __LINE__, "s", + "logfiles cycled"); +#endif + } + } + + if (handle_sig_alarm) { + /* a new second */ + +#ifdef USE_ALARM + /* reset notification */ + handle_sig_alarm = 0; +#endif + + /* get current time */ + min_ts = time(NULL); + + if (min_ts != srv->cur_ts) { +#ifdef DEBUG_CONNECTION_STATES + int cs = 0; +#endif + connections *conns = srv->conns; + handler_t r; + + switch(r = plugins_call_handle_trigger(srv)) { + case HANDLER_GO_ON: + break; + case HANDLER_ERROR: + log_error_write(srv, __FILE__, __LINE__, "s", "one of the triggers failed"); + break; + default: + log_error_write(srv, __FILE__, __LINE__, "d", r); + break; + } + + srv->cur_ts = min_ts; + + /* check idle time limit, if enabled */ + if (idle_limit && idle_limit < min_ts - last_active_ts && !graceful_shutdown) { + log_error_write(srv, __FILE__, __LINE__, "sDs", "[note] idle timeout", (int)idle_limit, + "s exceeded, initiating graceful shutdown"); + graceful_shutdown = 2; /* value 2 indicates idle timeout */ + if (graceful_restart) { + graceful_restart = 0; + if (pid_fd < -2) pid_fd = -pid_fd; + server_sockets_close(srv); + } + } + + #ifdef HAVE_GETLOADAVG + /* refresh loadavg data every 30 seconds */ + if (srv->srvconf.loadts + 30 < min_ts) { + if (-1 != getloadavg(srv->srvconf.loadavg, 3)) { + srv->srvconf.loadts = min_ts; + } + } + #endif + + /* free excess chunkqueue buffers every 64 seconds */ + if (0 == (min_ts & 0x3f)) chunkqueue_chunk_pool_clear(); + /* cleanup stat-cache */ + stat_cache_trigger_cleanup(srv); + /* reset global/aggregate rate limit counters */ + for (i = 0; i < srv->config_context->used; ++i) { + srv->config_storage[i]->global_bytes_per_second_cnt = 0; + } + /* if graceful_shutdown, accelerate cleanup of recently completed request/responses */ + if (graceful_shutdown && !srv_shutdown) server_graceful_shutdown_maint(srv); + /** + * check all connections for timeouts + * + */ + for (ndx = 0; ndx < conns->used; ndx++) { + connection * const con = conns->ptr[ndx]; + const int waitevents = fdevent_event_get_interest(srv->ev, con->fd); + int changed = 0; + int t_diff; + + if (con->state == CON_STATE_CLOSE) { + if (srv->cur_ts - con->close_timeout_ts > HTTP_LINGER_TIMEOUT) { + changed = 1; + } + } else if (waitevents & FDEVENT_IN) { + if (con->request_count == 1 || con->state != CON_STATE_READ) { /* e.g. CON_STATE_READ_POST || CON_STATE_WRITE */ + if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) { + /* time - out */ + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed - read timeout:", con->fd); + } + + connection_set_state(srv, con, CON_STATE_ERROR); + changed = 1; + } + } else { + if (srv->cur_ts - con->read_idle_ts > con->keep_alive_idle) { + /* time - out */ + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "connection closed - keep-alive timeout:", con->fd); + } + + connection_set_state(srv, con, CON_STATE_ERROR); + changed = 1; + } + } + } + + /* max_write_idle timeout currently functions as backend timeout, + * too, after response has been started. + * future: have separate backend timeout, and then change this + * to check for write interest before checking for timeout */ + /*if (waitevents & FDEVENT_OUT)*/ + if ((con->state == CON_STATE_WRITE) && + (con->write_request_ts != 0)) { +#if 0 + if (srv->cur_ts - con->write_request_ts > 60) { + log_error_write(srv, __FILE__, __LINE__, "sdd", + "connection closed - pre-write-request-timeout:", con->fd, srv->cur_ts - con->write_request_ts); + } +#endif + + if (srv->cur_ts - con->write_request_ts > con->conf.max_write_idle) { + /* time - out */ + if (con->conf.log_timeouts) { + log_error_write(srv, __FILE__, __LINE__, "sbsbsosds", + "NOTE: a request from", + con->dst_addr_buf, + "for", + con->request.uri, + "timed out after writing", + con->bytes_written, + "bytes. We waited", + (int)con->conf.max_write_idle, + "seconds. If this a problem increase server.max-write-idle"); + } + connection_set_state(srv, con, CON_STATE_ERROR); + changed = 1; + } + } + + /* we don't like div by zero */ + if (0 == (t_diff = srv->cur_ts - con->connection_start)) t_diff = 1; + + if (con->traffic_limit_reached && + (con->conf.kbytes_per_second == 0 || + ((con->bytes_written / t_diff) < con->conf.kbytes_per_second * 1024))) { + /* enable connection again */ + con->traffic_limit_reached = 0; + + changed = 1; + } + + con->bytes_written_cur_second = 0; + + if (changed) { + connection_state_machine(srv, con); + } + +#if DEBUG_CONNECTION_STATES + if (cs == 0) { + fprintf(stderr, "connection-state: "); + cs = 1; + } + + fprintf(stderr, "c[%d,%d]: %s ", + con->fd, + con->fcgi.fd, + connection_get_state(con->state)); +#endif + } + +#ifdef DEBUG_CONNECTION_STATES + if (cs == 1) fprintf(stderr, "\n"); +#endif + } + } + + if (handle_sig_child) { + pid_t pid; + handle_sig_child = 0; + do { + int status; + pid = waitpid(-1, &status, WNOHANG); + if (pid > 0) { + if (plugins_call_handle_waitpid(srv, pid, status) != HANDLER_GO_ON) { + continue; + } + if (0 == srv->srvconf.max_worker) { + /* check piped-loggers and restart, even if shutting down */ + if (fdevent_waitpid_logger_pipe_pid(pid, srv->cur_ts)) { + continue; + } + } + } + } while (pid > 0 || (-1 == pid && errno == EINTR)); + } + + if (graceful_shutdown) { + server_graceful_state(srv); + srv->sockets_disabled = 1; + } else if (srv->sockets_disabled) { + /* our server sockets are disabled, why ? */ + + if ((srv->cur_fds + srv->want_fds < srv->max_fds * 8 / 10) && /* we have enough unused fds */ + (srv->conns->used <= srv->max_conns * 9 / 10)) { + server_sockets_set_event(srv, FDEVENT_IN); + log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets enabled again"); + + srv->sockets_disabled = 0; + } + } else { + if ((srv->cur_fds + srv->want_fds > srv->max_fds * 9 / 10) || /* out of fds */ + (srv->conns->used >= srv->max_conns)) { /* out of connections */ + /* disable server-fds */ + server_sockets_set_event(srv, 0); + + if (srv->conns->used >= srv->max_conns) { + log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, connection limit reached"); + } else { + log_error_write(srv, __FILE__, __LINE__, "s", "[note] sockets disabled, out-of-fds"); + } + + srv->sockets_disabled = 1; + } + } + + if (graceful_shutdown && srv->conns->used == 0) { + /* we are in graceful shutdown phase and all connections are closed + * we are ready to terminate without harming anyone */ + srv_shutdown = 1; + break; + } + + /* we still have some fds to share */ + if (srv->want_fds) { + /* check the fdwaitqueue for waiting fds */ + int free_fds = srv->max_fds - srv->cur_fds - 16; + connection *con; + + for (; free_fds > 0 && NULL != (con = fdwaitqueue_unshift(srv, srv->fdwaitqueue)); free_fds--) { + connection_state_machine(srv, con); + + srv->want_fds--; + } + } + + if ((n = fdevent_poll(srv->ev, 1000)) > 0) { + /* n is the number of events */ + int fd; + int revents; + int fd_ndx; + last_active_ts = srv->cur_ts; + fd_ndx = -1; + do { + fdevent_handler handler; + void *context; + + fd_ndx = fdevent_event_next_fdndx (srv->ev, fd_ndx); + if (-1 == fd_ndx) break; /* not all fdevent handlers know how many fds got an event */ + + revents = fdevent_event_get_revent (srv->ev, fd_ndx); + fd = fdevent_event_get_fd (srv->ev, fd_ndx); + handler = fdevent_get_handler(srv->ev, fd); + context = fdevent_get_context(srv->ev, fd); + if (NULL != handler) { + (*handler)(srv, context, revents); + } + } while (--n > 0); + } else if (n < 0 && errno != EINTR) { + log_error_write(srv, __FILE__, __LINE__, "ss", + "fdevent_poll failed:", + strerror(errno)); + } + + if (n >= 0) fdevent_sched_run(srv, srv->ev); + + for (ndx = 0; ndx < srv->joblist->used; ndx++) { + connection *con = srv->joblist->ptr[ndx]; + connection_state_machine(srv, con); + } + + srv->joblist->used = 0; + } + + if (graceful_shutdown || graceful_restart) { + server_graceful_state(srv); + } + + if (2 == graceful_shutdown) { /* value 2 indicates idle timeout */ + log_error_write(srv, __FILE__, __LINE__, "s", + "server stopped after idle timeout"); + } else { +#ifdef HAVE_SIGACTION + log_error_write(srv, __FILE__, __LINE__, "sdsd", + "server stopped by UID =", + last_sigterm_info.si_uid, + "PID =", + last_sigterm_info.si_pid); +#else + log_error_write(srv, __FILE__, __LINE__, "s", + "server stopped"); +#endif + } + + return 0; +} + +int main (int argc, char **argv) { + int rc; + + #ifdef HAVE_GETUID + #ifndef HAVE_ISSETUGID + #define issetugid() (geteuid() != getuid() || getegid() != getgid()) + #endif + if (0 != getuid() && issetugid()) { /*check as early as possible in main()*/ + fprintf(stderr, + "Are you nuts ? Don't apply a SUID bit to this binary\n"); + return -1; + } + #endif + + /* for nice %b handling in strftime() */ + setlocale(LC_TIME, "C"); + + do { + server * const srv = server_init(); + + if (graceful_restart) { + server_sockets_restore(srv); + optind = 1; + } + + rc = server_main(srv, argc, argv); + + /* clean-up */ + remove_pid_file(srv); + log_error_close(srv); + fdevent_close_logger_pipes(); + if (graceful_restart) + server_sockets_save(srv); + else + network_close(srv); + connections_free(srv); + plugins_free(srv); + server_free(srv); + + if (0 != rc || !graceful_restart) break; + + /* wait for all children to exit before graceful restart */ + while (waitpid(-1, NULL, 0) > 0) ; + } while (graceful_restart); + + return rc; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/server.h b/data/lighttpd/lighttpd-1.4.53/src/server.h new file mode 100644 index 000000000..d0c6f9bdd --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/server.h @@ -0,0 +1,7 @@ +#ifndef _SERVER_H_ +#define _SERVER_H_ +#include "first.h" + +#include "base_decls.h" + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/settings.h b/data/lighttpd/lighttpd-1.4.53/src/settings.h new file mode 100644 index 000000000..a28cf7754 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/settings.h @@ -0,0 +1,29 @@ +#ifndef _LIGHTTPD_SETTINGS_H_ +#define _LIGHTTPD_SETTINGS_H_ +#include "first.h" + +#define FILE_CACHE_MAX 16 + +/** + * max size of a buffer which will just be reset + * to ->used = 0 instead of really freeing the buffer + * + * 64kB (no real reason, just a guess) + */ +#define BUFFER_MAX_REUSE_SIZE (4 * 1024) + +/* both should be way smaller than SSIZE_MAX :) */ +#define MAX_READ_LIMIT (256*1024) +#define MAX_WRITE_LIMIT (256*1024) + +/** + * max size of the HTTP request header + * + * 32k should be enough for everything (just a guess) + * + */ +#define MAX_HTTP_REQUEST_HEADER (32 * 1024) + +#define HTTP_LINGER_TIMEOUT 5 + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/sock_addr.c b/data/lighttpd/lighttpd-1.4.53/src/sock_addr.c new file mode 100644 index 000000000..cc15086c6 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/sock_addr.c @@ -0,0 +1,689 @@ +#include "first.h" + +#include "sock_addr.h" + +#include "sys-socket.h" +#include <sys/types.h> +#include <errno.h> +#include <string.h> +#ifndef _WIN32 +#include <netdb.h> +#include <arpa/inet.h> +#endif + +#include "base.h" +#include "log.h" + + +unsigned short sock_addr_get_port (const sock_addr *saddr) +{ + switch (saddr->plain.sa_family) { + case AF_INET: + return ntohs(saddr->ipv4.sin_port); + #ifdef HAVE_IPV6 + case AF_INET6: + return ntohs(saddr->ipv6.sin6_port); + #endif + #ifdef HAVE_SYS_UN_H + /*case AF_UNIX:*/ + #endif + default: + return 0; + } +} + + +int sock_addr_is_addr_wildcard (const sock_addr *saddr) +{ + switch (saddr->plain.sa_family) { + case AF_INET: + return (saddr->ipv4.sin_addr.s_addr == INADDR_ANY); /*(htonl(0x0))*/ + #ifdef HAVE_IPV6 + case AF_INET6: + return !memcmp(&saddr->ipv6.sin6_addr,&in6addr_any,sizeof(in6addr_any)); + #endif + #ifdef HAVE_SYS_UN_H + /*case AF_UNIX:*/ + #endif + default: + return 0; + } +} + + +int sock_addr_is_family_eq (const sock_addr *saddr1, const sock_addr *saddr2) +{ + return saddr1->plain.sa_family == saddr2->plain.sa_family; +} + + +int sock_addr_is_port_eq (const sock_addr *saddr1, const sock_addr *saddr2) +{ + if (!sock_addr_is_family_eq(saddr1, saddr2)) return 0; + switch (saddr1->plain.sa_family) { + case AF_INET: + return saddr1->ipv4.sin_port == saddr2->ipv4.sin_port; + #ifdef HAVE_IPV6 + case AF_INET6: + return saddr1->ipv6.sin6_port == saddr2->ipv6.sin6_port; + #endif + #ifdef HAVE_SYS_UN_H + case AF_UNIX: + return 1; + #endif + default: + return 0; + } +} + + +int sock_addr_is_addr_eq (const sock_addr *saddr1, const sock_addr *saddr2) +{ + if (!sock_addr_is_family_eq(saddr1, saddr2)) return 0; + switch (saddr1->plain.sa_family) { + case AF_INET: + return saddr1->ipv4.sin_addr.s_addr == saddr2->ipv4.sin_addr.s_addr; + #ifdef HAVE_IPV6 + case AF_INET6: + return 0 == memcmp(&saddr1->ipv6.sin6_addr, &saddr2->ipv6.sin6_addr, + sizeof(struct in6_addr)); + #endif + #ifdef HAVE_SYS_UN_H + case AF_UNIX: + return 0 == strcmp(saddr1->un.sun_path, saddr2->un.sun_path); + #endif + default: + return 0; + } +} + + +#if 0 +int sock_addr_is_addr_port_eq (const sock_addr *saddr1, const sock_addr *saddr2) +{ + if (!sock_addr_is_family_eq(saddr1, saddr2)) return 0; + switch (saddr1->plain.sa_family) { + case AF_INET: + return saddr1->ipv4.sin_port == saddr2->ipv4.sin_port + && saddr1->ipv4.sin_addr.s_addr == saddr2->ipv4.sin_addr.s_addr; + #ifdef HAVE_IPV6 + case AF_INET6: + return saddr1->ipv6.sin6_port == saddr2->ipv6.sin6_port + && 0 == memcmp(&saddr1->ipv6.sin6_addr, &saddr2->ipv6.sin6_addr, + sizeof(struct in6_addr)); + #endif + #ifdef HAVE_SYS_UN_H + case AF_UNIX: + return 0 == strcmp(saddr1->un.sun_path, saddr2->un.sun_path); + #endif + default: + return 0; + } +} +#endif + + +int sock_addr_is_addr_eq_bits(const sock_addr *a, const sock_addr *b, int bits) { + switch (a->plain.sa_family) { + case AF_INET: + { + uint32_t nm; /* build netmask */ + if (bits > 32) bits = 32; + nm = htonl(~((1u << (32 - (0 != bits ? bits : 32))) - 1)); + if (b->plain.sa_family == AF_INET) { + return + (a->ipv4.sin_addr.s_addr & nm) == (b->ipv4.sin_addr.s_addr & nm); + } + #ifdef HAVE_IPV6 + else if (b->plain.sa_family == AF_INET6 + && IN6_IS_ADDR_V4MAPPED(&b->ipv6.sin6_addr)) { + #ifdef s6_addr32 + in_addr_t x = b->ipv6.sin6_addr.s6_addr32[3]; + #else + in_addr_t x; + memcpy(&x, b->ipv6.sin6_addr.s6_addr+12, sizeof(in_addr_t)); + #endif + return ((a->ipv4.sin_addr.s_addr & nm) == (x & nm)); + } + #endif + return 0; + } + #ifdef HAVE_IPV6 + case AF_INET6: + if (bits > 128) bits = 128; + if (b->plain.sa_family == AF_INET6) { + uint8_t *c = (uint8_t *)&a->ipv6.sin6_addr.s6_addr[0]; + uint8_t *d = (uint8_t *)&b->ipv6.sin6_addr.s6_addr[0]; + int match; + do { + match = (bits >= 8) + ? *c++ == *d++ + : (*c >> (8 - bits)) == (*d >> (8 - bits)); + } while (match && (bits -= 8) > 0); + return match; + } + else if (b->plain.sa_family == AF_INET + && IN6_IS_ADDR_V4MAPPED(&a->ipv6.sin6_addr)) { + uint32_t nm = bits < 128 + ? htonl(~(~0u >> (bits > 96 ? bits - 96 : 0))) + : ~0u; + #ifdef s6_addr32 + in_addr_t x = a->ipv6.sin6_addr.s6_addr32[3]; + #else + in_addr_t x; + memcpy(&x, a->ipv6.sin6_addr.s6_addr+12, sizeof(in_addr_t)); + #endif + return ((x & nm) == (b->ipv4.sin_addr.s_addr & nm)); + } + return 0; + #endif + #ifdef HAVE_SYS_UN_H + /*case AF_UNIX:*/ + #endif + default: + return 0; + } +} + + +int sock_addr_assign (sock_addr *saddr, int family, unsigned short nport, const void *naddr) +{ + switch (family) { + case AF_INET: + memset(&saddr->ipv4, 0, sizeof(struct sockaddr_in)); + saddr->ipv4.sin_family = AF_INET; + saddr->ipv4.sin_port = nport; + memcpy(&saddr->ipv4.sin_addr, naddr, 4); + return 0; + #ifdef HAVE_IPV6 + case AF_INET6: + memset(&saddr->ipv6, 0, sizeof(struct sockaddr_in6)); + saddr->ipv6.sin6_family = AF_INET6; + saddr->ipv6.sin6_port = nport; + memcpy(&saddr->ipv6.sin6_addr, naddr, 16); + return 0; + #endif + #ifdef HAVE_SYS_UN_H + case AF_UNIX: + { + size_t len = strlen((char *)naddr) + 1; + if (len > sizeof(saddr->un.sun_path)) { + errno = ENAMETOOLONG; + return -1; + } + memset(&saddr->un, 0, sizeof(struct sockaddr_un)); + saddr->un.sun_family = AF_UNIX; + memcpy(saddr->un.sun_path, naddr, len); + return 0; + } + #endif + default: + errno = EAFNOSUPPORT; + return -1; + } +} + + +int sock_addr_inet_pton(sock_addr *saddr, const char *str, + int family, unsigned short port) +{ + switch (family) { + case AF_INET: + memset(&saddr->ipv4, 0, sizeof(struct sockaddr_in)); + saddr->ipv4.sin_family = AF_INET; + saddr->ipv4.sin_port = htons(port); + #if defined(HAVE_INET_ATON) /*(Windows does not provide inet_aton())*/ + return (0 != inet_aton(str, &saddr->ipv4.sin_addr)); + #else + return ((saddr->ipv4.sin_addr.s_addr = inet_addr(str)) != INADDR_NONE); + #endif + #ifdef HAVE_IPV6 + case AF_INET6: + memset(&saddr->ipv6, 0, sizeof(struct sockaddr_in6)); + saddr->ipv6.sin6_family = AF_INET6; + saddr->ipv6.sin6_port = htons(port); + return inet_pton(AF_INET6, str, &saddr->ipv6.sin6_addr); + #endif + default: + errno = EAFNOSUPPORT; + return -1; + } +} + + +const char * sock_addr_inet_ntop(const sock_addr *saddr, char *buf, socklen_t sz) +{ + switch (saddr->plain.sa_family) { + case AF_INET: + #if defined(HAVE_INET_PTON) /*(expect inet_ntop if inet_pton)*/ + return inet_ntop(AF_INET,(const void *)&saddr->ipv4.sin_addr,buf,sz); + #else /*(inet_ntoa() not thread-safe)*/ + return inet_ntoa(saddr->ipv4.sin_addr); + #endif + #ifdef HAVE_IPV6 + case AF_INET6: + return inet_ntop(AF_INET6,(const void *)&saddr->ipv6.sin6_addr,buf,sz); + #endif + #ifdef HAVE_SYS_UN_H + case AF_UNIX: + return saddr->un.sun_path; + #endif + default: + errno = EAFNOSUPPORT; + return NULL; + } +} + + +int sock_addr_inet_ntop_copy_buffer(buffer *b, const sock_addr *saddr) +{ + /*(incur cost of extra copy to avoid potential extra memory allocation)*/ + char buf[UNIX_PATH_MAX]; + const char *s = sock_addr_inet_ntop(saddr, buf, sizeof(buf)); + if (NULL == s) return -1; /*(buffer not modified if any error occurs)*/ + buffer_copy_string(b, s); + return 0; +} + + +int sock_addr_inet_ntop_append_buffer(buffer *b, const sock_addr *saddr) +{ + /*(incur cost of extra copy to avoid potential extra memory allocation)*/ + char buf[UNIX_PATH_MAX]; + const char *s = sock_addr_inet_ntop(saddr, buf, sizeof(buf)); + if (NULL == s) return -1; /*(buffer not modified if any error occurs)*/ + buffer_append_string(b, s); + return 0; +} + +int sock_addr_stringify_append_buffer(buffer *b, const sock_addr *saddr) +{ + switch (saddr->plain.sa_family) { + case AF_INET: + if (0 != sock_addr_inet_ntop_append_buffer(b, saddr)) return -1; + buffer_append_string_len(b, CONST_STR_LEN(":")); + buffer_append_int(b, ntohs(saddr->ipv4.sin_port)); + return 0; + #ifdef HAVE_IPV6 + case AF_INET6: + buffer_append_string_len(b, CONST_STR_LEN("[")); + if (0 != sock_addr_inet_ntop_append_buffer(b, saddr)) { + #ifdef __COVERITY__ + force_assert(buffer_string_length(b) > 0); /*(appended "[")*/ + #endif + /* coverity[overflow_sink : FALSE] */ + buffer_string_set_length(b, buffer_string_length(b)-1); + return -1; + } + buffer_append_string_len(b, CONST_STR_LEN("]")); + buffer_append_string_len(b, CONST_STR_LEN(":")); + buffer_append_int(b, ntohs(saddr->ipv6.sin6_port)); + return 0; + #endif + #ifdef HAVE_SYS_UN_H + case AF_UNIX: + buffer_append_string(b, saddr->un.sun_path); + return 0; + #endif + default: + return 0; + } +} + + +int sock_addr_nameinfo_append_buffer(server *srv, buffer *b, const sock_addr *saddr) +{ + /*(this routine originates from + * http-header-glue.c:http_response_redirect_to_directory())*/ + /*(note: name resolution here is *blocking*)*/ + switch (saddr->plain.sa_family) { + case AF_INET: + { + struct hostent *he = gethostbyaddr((char *)&saddr->ipv4.sin_addr, + sizeof(struct in_addr), AF_INET); + if (NULL == he) { + log_error_write(srv, __FILE__, __LINE__, + "SdS", "NOTICE: gethostbyaddr failed: ", + h_errno, ", using ip-address instead"); + + sock_addr_inet_ntop_append_buffer(b, saddr); + } else { + buffer_append_string(b, he->h_name); + } + return 0; + } + #ifdef HAVE_IPV6 + case AF_INET6: + { + char hbuf[256]; + if (0 != getnameinfo((const struct sockaddr *)(&saddr->ipv6), + sizeof(saddr->ipv6), + hbuf, sizeof(hbuf), NULL, 0, 0)) { + log_error_write(srv, __FILE__, __LINE__, + "SSS", "NOTICE: getnameinfo failed: ", + strerror(errno), ", using ip-address instead"); + + buffer_append_string_len(b, CONST_STR_LEN("[")); + sock_addr_inet_ntop_append_buffer(b, saddr); + buffer_append_string_len(b, CONST_STR_LEN("]")); + } else { + buffer_append_string(b, hbuf); + } + return 0; + } + #endif + default: + log_error_write(srv, __FILE__, __LINE__, + "S", "ERROR: unsupported address-type"); + return -1; + } +} + + +int sock_addr_from_str_hints(server *srv, sock_addr *saddr, socklen_t *len, const char *str, int family, unsigned short port) +{ + /*(note: name resolution here is *blocking*)*/ + switch(family) { + case AF_UNSPEC: + if (0 == strcmp(str, "localhost")) { + /*(special-case "localhost" to IPv4 127.0.0.1)*/ + memset(saddr, 0, sizeof(struct sockaddr_in)); + saddr->ipv4.sin_family = AF_INET; + saddr->ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + saddr->ipv4.sin_port = htons(port); + *len = sizeof(struct sockaddr_in); + return 1; + } + #ifdef HAVE_IPV6 + else { + struct addrinfo hints, *res; + int r; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (0 != (r = getaddrinfo(str, NULL, &hints, &res))) { + log_error_write(srv, __FILE__, __LINE__, + "sssss", "getaddrinfo failed: ", + gai_strerror(r), "'", str, "'"); + return 0; + } + + memcpy(saddr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + if (AF_INET6 == saddr->plain.sa_family) { + saddr->ipv6.sin6_port = htons(port); + *len = sizeof(struct sockaddr_in6); + } + else { /* AF_INET */ + saddr->ipv4.sin_port = htons(port); + *len = sizeof(struct sockaddr_in); + } + return 1; + } + #else + /* fall through */ + #endif + #ifdef HAVE_IPV6 + case AF_INET6: + memset(saddr, 0, sizeof(struct sockaddr_in6)); + saddr->ipv6.sin6_family = AF_INET6; + if (0 == strcmp(str, "::")) { + saddr->ipv6.sin6_addr = in6addr_any; + } + else if (0 == strcmp(str, "::1")) { + saddr->ipv6.sin6_addr = in6addr_loopback; + } + else { + struct addrinfo hints, *res; + int r; + + memset(&hints, 0, sizeof(hints)); + + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (0 != (r = getaddrinfo(str, NULL, &hints, &res))) { + hints.ai_family = AF_INET; + if ( + #ifdef EAI_ADDRFAMILY + EAI_ADDRFAMILY == r && + #endif + 0 == getaddrinfo(str, NULL, &hints, &res)) { + memcpy(saddr, res->ai_addr, res->ai_addrlen); + saddr->ipv4.sin_family = AF_INET; + saddr->ipv4.sin_port = htons(port); + *len = sizeof(struct sockaddr_in); + /*assert(*len == res->ai_addrlen);*/ + freeaddrinfo(res); + return 1; + } + + log_error_write(srv, __FILE__, __LINE__, + "sssss", "getaddrinfo failed: ", + gai_strerror(r), "'", str, "'"); + + return 0; + } + + memcpy(saddr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + } + saddr->ipv6.sin6_port = htons(port); + *len = sizeof(struct sockaddr_in6); + return 1; + #endif + case AF_INET: + memset(saddr, 0, sizeof(struct sockaddr_in)); + saddr->ipv4.sin_family = AF_INET; + if (0 == strcmp(str, "0.0.0.0")) { + saddr->ipv4.sin_addr.s_addr = htonl(INADDR_ANY); + } + else if (0 == strcmp(str, "127.0.0.1")) { + saddr->ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } + else { + #ifdef HAVE_INET_PTON + /*(reuse HAVE_INET_PTON for presence of getaddrinfo())*/ + struct addrinfo hints, *res; + int r; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (0 != (r = getaddrinfo(str, NULL, &hints, &res))) { + log_error_write(srv, __FILE__, __LINE__, + "sssss", "getaddrinfo failed: ", + gai_strerror(r), "'", str, "'"); + return 0; + } + + memcpy(saddr, res->ai_addr, res->ai_addrlen); + freeaddrinfo(res); + #else + struct hostent *he = gethostbyname(str); + if (NULL == he) { + log_error_write(srv, __FILE__, __LINE__, "sds", + "gethostbyname failed:", h_errno, str); + return 0; + } + + if (he->h_addrtype != AF_INET) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "addr-type != AF_INET:", he->h_addrtype); + return 0; + } + + if (he->h_length != sizeof(struct in_addr)) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "addr-length != sizeof(in_addr):",he->h_length); + return 0; + } + + memcpy(&saddr->ipv4.sin_addr.s_addr, + he->h_addr_list[0], he->h_length); + #endif + } + saddr->ipv4.sin_port = htons(port); + *len = sizeof(struct sockaddr_in); + return 1; + #ifdef HAVE_SYS_UN_H + case AF_UNIX: + memset(saddr, 0, sizeof(struct sockaddr_un)); + saddr->un.sun_family = AF_UNIX; + { + size_t hostlen = strlen(str) + 1; + if (hostlen > sizeof(saddr->un.sun_path)) { + log_error_write(srv, __FILE__, __LINE__, "sS", + "unix socket filename too long:", str); + /*errno = ENAMETOOLONG;*/ + return 0; + } + memcpy(saddr->un.sun_path, str, hostlen); + #if defined(SUN_LEN) + *len = SUN_LEN(&saddr->un); + #else + /* stevens says: */ + *len = hostlen + sizeof(saddr->un.sun_family); + #endif + } + return 1; + #else + case AF_UNIX: + log_error_write(srv, __FILE__, __LINE__, "s", + "unix domain sockets are not supported."); + return 0; + #endif + default: + log_error_write(srv, __FILE__, __LINE__, "sd", + "address family unsupported:", family); + /*errno = EAFNOSUPPORT;*/ + return 0; + } +} + + +int sock_addr_from_str_numeric(server *srv, sock_addr *saddr, const char *str) +{ + /*(note: does not handle port if getaddrinfo() is not available)*/ + /*(note: getaddrinfo() is stricter than inet_aton() in what is accepted)*/ + /*(this routine originates from mod_extforward.c:ipstr_to_sockaddr()*/ + #ifdef HAVE_IPV6 + struct addrinfo hints, *addrlist = NULL; + int result; + + /** + * quoting $ man getaddrinfo + * + * NOTES + * AI_ADDRCONFIG, AI_ALL, and AI_V4MAPPED are available since glibc 2.3.3. + * AI_NUMERICSERV is available since glibc 2.3.4. + */ + #ifndef AI_NUMERICSERV + #define AI_NUMERICSERV 0 + #endif + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; + + errno = 0; + result = getaddrinfo(str, NULL, &hints, &addrlist); + + if (result != 0) { + log_error_write(srv, __FILE__, __LINE__, "SSSs(S)", + "could not parse ip address ", str, " because ", + gai_strerror(result), strerror(errno)); + } else if (addrlist == NULL) { + log_error_write(srv, __FILE__, __LINE__, "SSS", + "Problem in parsing ip address ", str, + ": succeeded, but no information returned"); + result = -1; + } else switch (addrlist->ai_family) { + case AF_INET: + memcpy(&saddr->ipv4, addrlist->ai_addr, sizeof(saddr->ipv4)); + force_assert(AF_INET == saddr->plain.sa_family); + break; + case AF_INET6: + memcpy(&saddr->ipv6, addrlist->ai_addr, sizeof(saddr->ipv6)); + force_assert(AF_INET6 == saddr->plain.sa_family); + break; + default: + log_error_write(srv, __FILE__, __LINE__, "SSS", + "Problem in parsing ip address ", str, + ": succeeded, but unknown family"); + result = -1; + break; + } + + freeaddrinfo(addrlist); + return (0 == result); + #else + UNUSED(srv); + saddr->ipv4.sin_addr.s_addr = inet_addr(str); + saddr->plain.sa_family = AF_INET; + return (saddr->ipv4.sin_addr.s_addr != 0xFFFFFFFF); + #endif +} + + +int sock_addr_from_buffer_hints_numeric(server *srv, sock_addr *saddr, socklen_t *len, const buffer *b, int family, unsigned short port) +{ + /*(this routine originates from mod_fastcgi.c and mod_scgi.c)*/ + if (buffer_string_is_empty(b)) { + /*(preserve existing behavior (for now))*/ + /*(would be better if initialized default when reading config)*/ + memset(&saddr->ipv4, 0, sizeof(struct sockaddr_in)); + saddr->ipv4.sin_family = AF_INET; + saddr->ipv4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + saddr->ipv4.sin_port = htons(port); + *len = sizeof(struct sockaddr_in); + return 1; + } + else if (1 == sock_addr_inet_pton(saddr, b->ptr, family, port)) { + *len = (family == AF_INET) + ? sizeof(struct sockaddr_in) /* family == AF_INET */ + : sizeof(struct sockaddr_in6); /* family == AF_INET6 */ + return 1; + } + #if defined(HAVE_IPV6) && defined(HAVE_INET_PTON) + else if (family == AF_INET6) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "invalid IPv6 address literal:", b); + return 0; + } + #endif + #ifndef HAVE_INET_PTON /*(preserve existing behavior (for now))*/ + else { + struct hostent *he = gethostbyname(b->ptr); + if (NULL == he) { + log_error_write(srv, __FILE__, __LINE__, "sdb", + "gethostbyname failed:", h_errno, b); + return 0; + } + + if (he->h_addrtype != AF_INET) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "addr-type != AF_INET:", he->h_addrtype); + return 0; + } + + if (he->h_length != sizeof(struct in_addr)) { + log_error_write(srv, __FILE__, __LINE__, "sd", + "addr-length != sizeof(in_addr):",he->h_length); + return 0; + } + + memset(&saddr->ipv4, 0, sizeof(struct sockaddr_in)); + memcpy(&saddr->ipv4.sin_addr.s_addr, he->h_addr_list[0], he->h_length); + saddr->ipv4.sin_family = AF_INET; + saddr->ipv4.sin_port = htons(port); + *len = sizeof(struct sockaddr_in); + } + #else + UNUSED(srv); + #endif + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/sock_addr.h b/data/lighttpd/lighttpd-1.4.53/src/sock_addr.h new file mode 100644 index 000000000..e949e7ef9 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/sock_addr.h @@ -0,0 +1,51 @@ +#ifndef INCLUDED_SOCK_ADDR_H +#define INCLUDED_SOCK_ADDR_H +#include "first.h" + +#include <sys/types.h> +#include "sys-socket.h" + +#include "base_decls.h" +#include "buffer.h" + + +union sock_addr { +#ifdef HAVE_IPV6 + struct sockaddr_in6 ipv6; +#endif + struct sockaddr_in ipv4; +#ifdef HAVE_SYS_UN_H + struct sockaddr_un un; +#endif + struct sockaddr plain; +}; + + +static inline int sock_addr_get_family (const sock_addr *saddr); +static inline int sock_addr_get_family (const sock_addr *saddr) { + return saddr->plain.sa_family; +} + +unsigned short sock_addr_get_port (const sock_addr *saddr); +int sock_addr_is_addr_wildcard (const sock_addr *saddr); +int sock_addr_is_family_eq (const sock_addr *saddr1, const sock_addr *saddr2); +int sock_addr_is_port_eq (const sock_addr *saddr1, const sock_addr *saddr2); +int sock_addr_is_addr_eq (const sock_addr *saddr1, const sock_addr *saddr2); +/*int sock_addr_is_addr_port_eq (const sock_addr *saddr1, const sock_addr *saddr2);*/ +int sock_addr_is_addr_eq_bits(const sock_addr *a, const sock_addr *b, int bits); +int sock_addr_assign (sock_addr *saddr, int family, unsigned short nport, const void *naddr); + +int sock_addr_inet_pton(sock_addr *saddr, const char *str, int family, unsigned short port); + +const char * sock_addr_inet_ntop(const sock_addr *saddr, char *buf, socklen_t sz); +int sock_addr_inet_ntop_copy_buffer(buffer *b, const sock_addr *saddr); +int sock_addr_inet_ntop_append_buffer(buffer *b, const sock_addr *saddr); +int sock_addr_stringify_append_buffer(buffer *b, const sock_addr *saddr); +int sock_addr_nameinfo_append_buffer(server *srv, buffer *b, const sock_addr *saddr); + +int sock_addr_from_buffer_hints_numeric(server *srv, sock_addr *saddr, socklen_t *len, const buffer *b, int family, unsigned short port); +int sock_addr_from_str_hints(server *srv, sock_addr *saddr, socklen_t *len, const char *str, int family, unsigned short port); +int sock_addr_from_str_numeric(server *srv, sock_addr *saddr, const char *str); + + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/splaytree.c b/data/lighttpd/lighttpd-1.4.53/src/splaytree.c new file mode 100644 index 000000000..51aa0ca7b --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/splaytree.c @@ -0,0 +1,209 @@ +/* + An implementation of top-down splaying with sizes + D. Sleator <sleator@cs.cmu.edu>, January 1994. + + This extends top-down-splay.c to maintain a size field in each node. + This is the number of nodes in the subtree rooted there. This makes + it possible to efficiently compute the rank of a key. (The rank is + the number of nodes to the left of the given key.) It it also + possible to quickly find the node of a given rank. Both of these + operations are illustrated in the code below. The remainder of this + introduction is taken from top-down-splay.c. + + "Splay trees", or "self-adjusting search trees" are a simple and + efficient data structure for storing an ordered set. The data + structure consists of a binary tree, with no additional fields. It + allows searching, insertion, deletion, deletemin, deletemax, + splitting, joining, and many other operations, all with amortized + logarithmic performance. Since the trees adapt to the sequence of + requests, their performance on real access patterns is typically even + better. Splay trees are described in a number of texts and papers + [1,2,3,4]. + + The code here is adapted from simple top-down splay, at the bottom of + page 669 of [2]. It can be obtained via anonymous ftp from + spade.pc.cs.cmu.edu in directory /usr/sleator/public. + + The chief modification here is that the splay operation works even if the + item being splayed is not in the tree, and even if the tree root of the + tree is NULL. So the line: + + t = splay(i, t); + + causes it to search for item with key i in the tree rooted at t. If it's + there, it is splayed to the root. If it isn't there, then the node put + at the root is the last one before NULL that would have been reached in a + normal binary search for i. (It's a neighbor of i in the tree.) This + allows many other operations to be easily implemented, as shown below. + + [1] "Data Structures and Their Algorithms", Lewis and Denenberg, + Harper Collins, 1991, pp 243-251. + [2] "Self-adjusting Binary Search Trees" Sleator and Tarjan, + JACM Volume 32, No 3, July 1985, pp 652-686. + [3] "Data Structure and Algorithm Analysis", Mark Weiss, + Benjamin Cummins, 1992, pp 119-130. + [4] "Data Structures, Algorithms, and Performance", Derick Wood, + Addison-Wesley, 1993, pp 367-375 +*/ + +#include "splaytree.h" +#include <stdlib.h> +#include <assert.h> + +#define compare(i,j) ((i)-(j)) +/* This is the comparison. */ +/* Returns <0 if i<j, =0 if i=j, and >0 if i>j */ + +#define node_size splaytree_size + +/* Splay using the key i (which may or may not be in the tree.) + * The starting root is t, and the tree used is defined by rat + * size fields are maintained */ +splay_tree * splaytree_splay (splay_tree *t, int i) { + splay_tree N, *l, *r, *y; + int comp, l_size, r_size; + + if (t == NULL) return t; + N.left = N.right = NULL; + l = r = &N; + l_size = r_size = 0; + + for (;;) { + comp = compare(i, t->key); + if (comp < 0) { + if (t->left == NULL) break; + if (compare(i, t->left->key) < 0) { + y = t->left; /* rotate right */ + t->left = y->right; + y->right = t; + t->size = node_size(t->left) + node_size(t->right) + 1; + t = y; + if (t->left == NULL) break; + } + r->left = t; /* link right */ + r = t; + t = t->left; + r_size += 1+node_size(r->right); + } else if (comp > 0) { + if (t->right == NULL) break; + if (compare(i, t->right->key) > 0) { + y = t->right; /* rotate left */ + t->right = y->left; + y->left = t; + t->size = node_size(t->left) + node_size(t->right) + 1; + t = y; + if (t->right == NULL) break; + } + l->right = t; /* link left */ + l = t; + t = t->right; + l_size += 1+node_size(l->left); + } else { + break; + } + } + l_size += node_size(t->left); /* Now l_size and r_size are the sizes of */ + r_size += node_size(t->right); /* the left and right trees we just built.*/ + t->size = l_size + r_size + 1; + + l->right = r->left = NULL; + + /* The following two loops correct the size fields of the right path */ + /* from the left child of the root and the right path from the left */ + /* child of the root. */ + for (y = N.right; y != NULL; y = y->right) { + y->size = l_size; + l_size -= 1+node_size(y->left); + } + for (y = N.left; y != NULL; y = y->left) { + y->size = r_size; + r_size -= 1+node_size(y->right); + } + + l->right = t->left; /* assemble */ + r->left = t->right; + t->left = N.right; + t->right = N.left; + + return t; +} + +splay_tree * splaytree_insert(splay_tree * t, int i, void *data) { +/* Insert key i into the tree t, if it is not already there. */ +/* Return a pointer to the resulting tree. */ + splay_tree * new; + + if (t != NULL) { + t = splaytree_splay(t, i); + if (compare(i, t->key)==0) { + return t; /* it's already there */ + } + } + new = (splay_tree *) malloc (sizeof (splay_tree)); + assert(new); + if (t == NULL) { + new->left = new->right = NULL; + } else if (compare(i, t->key) < 0) { + new->left = t->left; + new->right = t; + t->left = NULL; + t->size = 1+node_size(t->right); + } else { + new->right = t->right; + new->left = t; + t->right = NULL; + t->size = 1+node_size(t->left); + } + new->key = i; + new->data = data; + new->size = 1 + node_size(new->left) + node_size(new->right); + return new; +} + +splay_tree * splaytree_delete(splay_tree *t, int i) { +/* Deletes i from the tree if it's there. */ +/* Return a pointer to the resulting tree. */ + splay_tree * x; + int tsize; + + if (t==NULL) return NULL; + tsize = t->size; + t = splaytree_splay(t, i); + if (compare(i, t->key) == 0) { /* found it */ + if (t->left == NULL) { + x = t->right; + } else { + x = splaytree_splay(t->left, i); + x->right = t->right; + } + free(t); + if (x != NULL) { + x->size = tsize-1; + } + return x; + } else { + return t; /* It wasn't there */ + } +} + +#if 0 +static splay_tree *find_rank(int r, splay_tree *t) { +/* Returns a pointer to the node in the tree with the given rank. */ +/* Returns NULL if there is no such node. */ +/* Does not change the tree. To guarantee logarithmic behavior, */ +/* the node found here should be splayed to the root. */ + int lsize; + if ((r < 0) || (r >= node_size(t))) return NULL; + for (;;) { + lsize = node_size(t->left); + if (r < lsize) { + t = t->left; + } else if (r > lsize) { + r = r - lsize -1; + t = t->right; + } else { + return t; + } + } +} +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/splaytree.h b/data/lighttpd/lighttpd-1.4.53/src/splaytree.h new file mode 100644 index 000000000..cc5fe9b1c --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/splaytree.h @@ -0,0 +1,25 @@ +#ifndef _SPLAY_TREE_H_ +#define _SPLAY_TREE_H_ +#include "first.h" + +typedef struct tree_node { + struct tree_node * left, * right; + int key; + int size; /* maintained to be the number of nodes rooted here */ + + void *data; +} splay_tree; + + +splay_tree * splaytree_splay (splay_tree *t, int key); +splay_tree * splaytree_insert(splay_tree *t, int key, void *data); +splay_tree * splaytree_delete(splay_tree *t, int key); +splay_tree * splaytree_size(splay_tree *t); + +#define splaytree_size(x) (((x)==NULL) ? 0 : ((x)->size)) +/* This macro returns the size of a node. Unlike "x->size", */ +/* it works even if x=NULL. The test could be avoided by using */ +/* a special version of NULL which was a real node with size 0. */ + + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/stat_cache.c b/data/lighttpd/lighttpd-1.4.53/src/stat_cache.c new file mode 100644 index 000000000..d470b2817 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/stat_cache.c @@ -0,0 +1,859 @@ +#include "first.h" + +#include "stat_cache.h" +#include "base.h" +#include "log.h" +#include "fdevent.h" +#include "etag.h" +#include "splaytree.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#ifdef HAVE_ATTR_ATTRIBUTES_H +# include <attr/attributes.h> +#endif + +#ifdef HAVE_SYS_EXTATTR_H +# include <sys/extattr.h> +#endif + +#ifndef HAVE_LSTAT +# define lstat stat +#endif + +/* + * stat-cache + * + * we cache the stat() calls in our own storage + * the directories are cached in FAM + * + * if we get a change-event from FAM, we increment the version in the FAM->dir mapping + * + * if the stat()-cache is queried we check if the version id for the directory is the + * same and return immediatly. + * + * + * What we need: + * + * - for each stat-cache entry we need a fast indirect lookup on the directory name + * - for each FAMRequest we have to find the version in the directory cache (index as userdata) + * + * stat <<-> directory <-> FAMRequest + * + * if file is deleted, directory is dirty, file is rechecked ... + * if directory is deleted, directory mapping is removed + * + * */ + +/* the directory name is too long to always compare on it + * - we need a hash + * - the hash-key is used as sorting criteria for a tree + * - a splay-tree is used as we can use the caching effect of it + */ + +/* we want to cleanup the stat-cache every few seconds, let's say 10 + * + * - remove entries which are outdated since 30s + * - remove entries which are fresh but havn't been used since 60s + * - if we don't have a stat-cache entry for a directory, release it from the monitor + */ + + +enum { + STAT_CACHE_ENGINE_UNSET, + STAT_CACHE_ENGINE_NONE, + STAT_CACHE_ENGINE_SIMPLE, + STAT_CACHE_ENGINE_FAM +}; + +#ifdef HAVE_FAM_H +struct stat_cache_fam; +#endif + +typedef struct stat_cache { + splay_tree *files; /* the nodes of the tree are stat_cache_entry's */ + #ifdef HAVE_FAM_H + struct stat_cache_fam *scf; + #endif +} stat_cache; + + +/* the famous DJB hash function for strings */ +static uint32_t hashme(const char *str, int z) { + uint32_t hash = 5381; + for (const unsigned char *s = (const unsigned char *)str; *s; ++s) { + hash = ((hash << 5) + hash) ^ *s; + } + + /* customizations */ + + /* (differentiate hash values with and w/o con->conf.follow_symlink) */ + hash = ((hash << 5) + hash) ^ (z ? '1' : '0'); + + hash &= ~(((uint32_t)1) << 31); /* strip the highest bit */ + + return hash; +} + + +#ifdef HAVE_FAM_H + +#include <fam.h> + +typedef struct { + FAMRequest *req; + buffer *name; + int version; +} fam_dir_entry; + +typedef struct stat_cache_fam { + splay_tree *dirs; /* the nodes of the tree are fam_dir_entry */ + + FAMConnection fam; + int fam_fcce_ndx; + + int dir_ndx; + fam_dir_entry *fam_dir; + buffer *dir_name; /* for building the dirname from the filename */ +} stat_cache_fam; + +static fam_dir_entry * fam_dir_entry_init(void) { + fam_dir_entry *fam_dir = NULL; + + fam_dir = calloc(1, sizeof(*fam_dir)); + force_assert(NULL != fam_dir); + + fam_dir->name = buffer_init(); + + return fam_dir; +} + +static void fam_dir_entry_free(FAMConnection *fc, void *data) { + fam_dir_entry *fam_dir = data; + + if (!fam_dir) return; + + FAMCancelMonitor(fc, fam_dir->req); + + buffer_free(fam_dir->name); + free(fam_dir->req); + + free(fam_dir); +} + +static handler_t stat_cache_handle_fdevent(server *srv, void *_fce, int revent) { + size_t i; + stat_cache_fam *scf = srv->stat_cache->scf; + size_t events; + + UNUSED(_fce); + /* */ + + if (revent & FDEVENT_IN) { + events = FAMPending(&scf->fam); + + for (i = 0; i < events; i++) { + FAMEvent fe; + fam_dir_entry *fam_dir; + splay_tree *node; + int ndx, j; + + FAMNextEvent(&scf->fam, &fe); + + /* handle event */ + + switch(fe.code) { + case FAMChanged: + case FAMDeleted: + case FAMMoved: + /* if the filename is a directory remove the entry */ + + fam_dir = fe.userdata; + fam_dir->version++; + + /* file/dir is still here */ + if (fe.code == FAMChanged) break; + + /* we have 2 versions, follow and no-follow-symlink */ + + for (j = 0; j < 2; j++) { + + ndx = hashme(fe.filename, j); + + scf->dirs = splaytree_splay(scf->dirs, ndx); + node = scf->dirs; + + if (node && (node->key == ndx)) { + int osize = splaytree_size(scf->dirs); + + fam_dir_entry_free(&scf->fam, node->data); + scf->dirs = splaytree_delete(scf->dirs, ndx); + + force_assert(osize - 1 == splaytree_size(scf->dirs)); + } + } + break; + default: + break; + } + } + } + + if (revent & (FDEVENT_HUP|FDEVENT_RDHUP)) { + /* fam closed the connection */ + fdevent_event_del(srv->ev, &(scf->fam_fcce_ndx), FAMCONNECTION_GETFD(&scf->fam)); + fdevent_unregister(srv->ev, FAMCONNECTION_GETFD(&scf->fam)); + + FAMClose(&scf->fam); + } + + return HANDLER_GO_ON; +} + +static stat_cache_fam * stat_cache_init_fam(server *srv) { + stat_cache_fam *scf = calloc(1, sizeof(*scf)); + scf->fam_fcce_ndx = -1; + scf->dir_name = buffer_init(); + + /* setup FAM */ + if (0 != FAMOpen2(&scf->fam, "lighttpd")) { + log_error_write(srv, __FILE__, __LINE__, "s", + "could not open a fam connection, dieing."); + return NULL; + } + #ifdef HAVE_FAMNOEXISTS + FAMNoExists(&scf->fam); + #endif + + fdevent_setfd_cloexec(FAMCONNECTION_GETFD(&scf->fam)); + fdevent_register(srv->ev, FAMCONNECTION_GETFD(&scf->fam), stat_cache_handle_fdevent, NULL); + fdevent_event_set(srv->ev, &(scf->fam_fcce_ndx), FAMCONNECTION_GETFD(&scf->fam), FDEVENT_IN | FDEVENT_RDHUP); + + return scf; +} + +static void stat_cache_free_fam(stat_cache_fam *scf) { + if (NULL == scf) return; + buffer_free(scf->dir_name); + + while (scf->dirs) { + int osize; + splay_tree *node = scf->dirs; + + osize = scf->dirs->size; + + fam_dir_entry_free(&scf->fam, node->data); + scf->dirs = splaytree_delete(scf->dirs, node->key); + + if (osize == 1) { + force_assert(NULL == scf->dirs); + } else { + force_assert(osize == (scf->dirs->size + 1)); + } + } + + if (-1 != scf->fam_fcce_ndx) { + /* fd events already gone */ + scf->fam_fcce_ndx = -1; + + FAMClose(&scf->fam); + } + + free(scf); +} + +static int buffer_copy_dirname(buffer *dst, const buffer *file) { + size_t i; + + if (buffer_string_is_empty(file)) return -1; + + for (i = buffer_string_length(file); i > 0; i--) { + if (file->ptr[i] == '/') { + buffer_copy_string_len(dst, file->ptr, i); + return 0; + } + } + + return -1; +} + +static handler_t stat_cache_fam_dir_check(server *srv, stat_cache_fam *scf, stat_cache_entry *sce, const buffer *name, unsigned int follow_symlink) { + if (0 != buffer_copy_dirname(scf->dir_name, name)) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "no '/' found in filename:", name); + return HANDLER_ERROR; + } + + scf->dir_ndx = hashme(scf->dir_name->ptr, follow_symlink); + + scf->dirs = splaytree_splay(scf->dirs, scf->dir_ndx); + + if ((NULL != scf->dirs) && (scf->dirs->key == scf->dir_ndx)) { + scf->fam_dir = scf->dirs->data; + + /* check whether we got a collision */ + if (buffer_is_equal(scf->dir_name, scf->fam_dir->name)) { + /* test whether a found file cache entry is still ok */ + if ((NULL != sce) && (scf->fam_dir->version == sce->dir_version)) { + /* the stat()-cache entry is still ok */ + return HANDLER_FINISHED; + } + } else { + /* hash collision, forget about the entry */ + scf->fam_dir = NULL; + } + } else { + scf->fam_dir = NULL; + } + + return HANDLER_GO_ON; +} + +static void stat_cache_fam_dir_monitor(server *srv, stat_cache_fam *scf, stat_cache_entry *sce, const buffer *name) { + /* is this directory already registered ? */ + fam_dir_entry *fam_dir = scf->fam_dir; + if (NULL == fam_dir) { + fam_dir = fam_dir_entry_init(); + + buffer_copy_buffer(fam_dir->name, scf->dir_name); + + fam_dir->version = 1; + + fam_dir->req = calloc(1, sizeof(FAMRequest)); + force_assert(NULL != fam_dir); + + if (0 != FAMMonitorDirectory(&scf->fam, fam_dir->name->ptr, + fam_dir->req, fam_dir)) { + + log_error_write(srv, __FILE__, __LINE__, "sbsbs", + "monitoring dir failed:", + fam_dir->name, + "file:", name, + FamErrlist[FAMErrno]); + + fam_dir_entry_free(&scf->fam, fam_dir); + fam_dir = NULL; + } else { + int osize = splaytree_size(scf->dirs); + + /* already splayed scf->dir_ndx */ + if ((NULL != scf->dirs) && (scf->dirs->key == scf->dir_ndx)) { + /* hash collision: replace old entry */ + fam_dir_entry_free(&scf->fam, scf->dirs->data); + scf->dirs->data = fam_dir; + } else { + scf->dirs = splaytree_insert(scf->dirs, scf->dir_ndx, fam_dir); + force_assert(osize == (splaytree_size(scf->dirs) - 1)); + } + + force_assert(scf->dirs); + force_assert(scf->dirs->data == fam_dir); + scf->fam_dir = fam_dir; + } + } + + /* bind the fam_fc to the stat() cache entry */ + + if (fam_dir) { + sce->dir_version = fam_dir->version; + } +} + +#endif + + +stat_cache *stat_cache_init(server *srv) { + stat_cache *sc = NULL; + UNUSED(srv); + + sc = calloc(1, sizeof(*sc)); + force_assert(NULL != sc); + +#ifdef HAVE_FAM_H + if (STAT_CACHE_ENGINE_FAM == srv->srvconf.stat_cache_engine) { + sc->scf = stat_cache_init_fam(srv); + if (NULL == sc->scf) { + free(sc); + return NULL; + } + } +#endif + + return sc; +} + +static stat_cache_entry * stat_cache_entry_init(void) { + stat_cache_entry *sce = NULL; + + sce = calloc(1, sizeof(*sce)); + force_assert(NULL != sce); + + sce->name = buffer_init(); + sce->etag = buffer_init(); + sce->content_type = buffer_init(); + + return sce; +} + +static void stat_cache_entry_free(void *data) { + stat_cache_entry *sce = data; + if (!sce) return; + + buffer_free(sce->etag); + buffer_free(sce->name); + buffer_free(sce->content_type); + + free(sce); +} + +void stat_cache_free(stat_cache *sc) { + while (sc->files) { + int osize; + splay_tree *node = sc->files; + + osize = sc->files->size; + + stat_cache_entry_free(node->data); + sc->files = splaytree_delete(sc->files, node->key); + + force_assert(osize - 1 == splaytree_size(sc->files)); + } + +#ifdef HAVE_FAM_H + stat_cache_free_fam(sc->scf); +#endif + free(sc); +} + +int stat_cache_choose_engine (server *srv, const buffer *stat_cache_string) { + if (buffer_string_is_empty(stat_cache_string)) { + srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; + } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("simple"))) { + srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_SIMPLE; +#ifdef HAVE_FAM_H + } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("fam"))) { + srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_FAM; +#endif + } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("disable"))) { + srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_NONE; + } else { + log_error_write(srv, __FILE__, __LINE__, "sb", + "server.stat-cache-engine can be one of \"disable\", \"simple\"," +#ifdef HAVE_FAM_H + " \"fam\"," +#endif + " but not:", stat_cache_string); + return -1; + } + return 0; +} + +#if defined(HAVE_XATTR) +static int stat_cache_attr_get(buffer *buf, char *name, char *xattrname) { + int attrlen; + int ret; + + buffer_string_prepare_copy(buf, 1023); + attrlen = buf->size - 1; + if(0 == (ret = attr_get(name, xattrname, buf->ptr, &attrlen, 0))) { + buffer_commit(buf, attrlen); + } + return ret; +} +#elif defined(HAVE_EXTATTR) +static int stat_cache_attr_get(buffer *buf, char *name, char *xattrname) { + ssize_t attrlen; + + buffer_string_prepare_copy(buf, 1023); + + if (-1 != (attrlen = extattr_get_file(name, EXTATTR_NAMESPACE_USER, xattrname, buf->ptr, buf->size - 1))) { + buf->used = attrlen + 1; + buf->ptr[attrlen] = '\0'; + return 0; + } + return -1; +} +#endif + +const buffer * stat_cache_mimetype_by_ext(const connection *con, const char *name, size_t nlen) +{ + const char *end = name + nlen; /*(end of string)*/ + const size_t used = con->conf.mimetypes->used; + if (used < 16) { + for (size_t i = 0; i < used; ++i) { + /* suffix match */ + const data_string *ds = (data_string *)con->conf.mimetypes->data[i]; + const size_t klen = buffer_string_length(ds->key); + if (klen <= nlen && 0 == strncasecmp(end-klen, ds->key->ptr, klen)) + return ds->value; + } + } + else { + const char *s; + const data_string *ds; + if (nlen) { + for (s = end-1; s != name && *s != '/'; --s) ; /*(like memrchr())*/ + if (*s == '/') ++s; + } + else { + s = name; + } + /* search for basename, then longest .ext2.ext1, then .ext1, then "" */ + ds = (data_string *)array_get_element_klen(con->conf.mimetypes, s, end - s); + if (NULL != ds) return ds->value; + while (++s < end) { + while (*s != '.' && ++s != end) ; + if (s == end) break; + /* search ".ext" then "ext" */ + ds = (data_string *)array_get_element_klen(con->conf.mimetypes, s, end - s); + if (NULL != ds) return ds->value; + /* repeat search without leading '.' to handle situation where + * admin configured mimetype.assign keys without leading '.' */ + if (++s < end) { + if (*s == '.') { --s; continue; } + ds = (data_string *)array_get_element_klen(con->conf.mimetypes, s, end - s); + if (NULL != ds) return ds->value; + } + } + /* search for ""; catchall */ + ds = (data_string *)array_get_element(con->conf.mimetypes, ""); + if (NULL != ds) return ds->value; + } + + return NULL; +} + +const buffer * stat_cache_content_type_get(server *srv, connection *con, const buffer *name, stat_cache_entry *sce) +{ + /*(invalid caching if user config has multiple, different + * con->conf.mimetypes for same extension (not expected))*/ + if (!buffer_string_is_empty(sce->content_type)) return sce->content_type; + + if (S_ISREG(sce->st.st_mode)) { + /* determine mimetype */ + buffer_clear(sce->content_type); + #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) + if (con->conf.use_xattr) { + stat_cache_attr_get(sce->content_type, name->ptr, srv->srvconf.xattr_name->ptr); + } + #else + UNUSED(srv); + #endif + /* xattr did not set a content-type. ask the config */ + if (buffer_string_is_empty(sce->content_type)) { + const buffer *type = stat_cache_mimetype_by_ext(con, CONST_BUF_LEN(name)); + if (NULL != type) { + buffer_copy_buffer(sce->content_type, type); + } + } + return sce->content_type; + } + + return NULL; +} + +const buffer * stat_cache_etag_get(stat_cache_entry *sce, etag_flags_t flags) { + /*(invalid caching if user config has multiple, different con->etag_flags + * for same path (not expected, since etag flags should be by filesystem))*/ + if (!buffer_string_is_empty(sce->etag)) return sce->etag; + + if (S_ISREG(sce->st.st_mode) || S_ISDIR(sce->st.st_mode)) { + etag_create(sce->etag, &sce->st, flags); + return sce->etag; + } + + return NULL; +} + +#ifdef HAVE_LSTAT +static int stat_cache_lstat(server *srv, buffer *dname, struct stat *lst) { + if (lstat(dname->ptr, lst) == 0) { + return S_ISLNK(lst->st_mode) ? 0 : 1; + } + else { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "lstat failed for:", + dname, strerror(errno)); + }; + return -1; +} +#endif + +/*** + * + * + * + * returns: + * - HANDLER_FINISHED on cache-miss (don't forget to reopen the file) + * - HANDLER_ERROR on stat() failed -> see errno for problem + */ + +handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) { + stat_cache_entry *sce = NULL; + stat_cache *sc; + struct stat st; + int fd; + const int follow_symlink = con->conf.follow_symlink; + struct stat lst; + int file_ndx; + + *ret_sce = NULL; + + /* + * check if the directory for this file has changed + */ + + sc = srv->stat_cache; + + file_ndx = hashme(name->ptr, follow_symlink); + sc->files = splaytree_splay(sc->files, file_ndx); + + if (sc->files && (sc->files->key == file_ndx)) { + /* we have seen this file already and + * don't stat() it again in the same second */ + + sce = sc->files->data; + + /* check if the name is the same, we might have a collision */ + + if (buffer_is_equal(name, sce->name)) { + if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_SIMPLE) { + if (sce->stat_ts == srv->cur_ts && follow_symlink) { + *ret_sce = sce; + return HANDLER_GO_ON; + } + } + } else { + /* collision, forget about the entry */ + sce = NULL; + } + } + +#ifdef HAVE_FAM_H + /* dir-check */ + if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { + switch (stat_cache_fam_dir_check(srv, sc->scf, sce, name, follow_symlink)) { + case HANDLER_GO_ON: + break; + case HANDLER_FINISHED: + *ret_sce = sce; + return HANDLER_GO_ON; + case HANDLER_ERROR: + default: + return HANDLER_ERROR; + } + } +#endif + + /* + * *lol* + * - open() + fstat() on a named-pipe results in a (intended) hang. + * - stat() if regular file + open() to see if we can read from it is better + * + * */ + if (-1 == stat(name->ptr, &st)) { + return HANDLER_ERROR; + } + + + if (S_ISREG(st.st_mode)) { + /* fix broken stat/open for symlinks to reg files with appended slash on freebsd,osx */ + if (name->ptr[buffer_string_length(name) - 1] == '/') { + errno = ENOTDIR; + return HANDLER_ERROR; + } + + /* try to open the file to check if we can read it */ + if (-1 == (fd = open(name->ptr, O_RDONLY))) { + return HANDLER_ERROR; + } + close(fd); + } + + if (NULL == sce) { + + sce = stat_cache_entry_init(); + buffer_copy_buffer(sce->name, name); + + /* already splayed file_ndx */ + if ((NULL != sc->files) && (sc->files->key == file_ndx)) { + /* hash collision: replace old entry */ + stat_cache_entry_free(sc->files->data); + sc->files->data = sce; + } else { + int osize = splaytree_size(sc->files); + + sc->files = splaytree_insert(sc->files, file_ndx, sce); + force_assert(osize + 1 == splaytree_size(sc->files)); + } + force_assert(sc->files); + force_assert(sc->files->data == sce); + + } else { + + buffer_clear(sce->etag); + #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) + buffer_clear(sce->content_type); + #endif + + } + + sce->st = st; + sce->stat_ts = srv->cur_ts; + + /* catch the obvious symlinks + * + * this is not a secure check as we still have a race-condition between + * the stat() and the open. We can only solve this by + * 1. open() the file + * 2. fstat() the fd + * + * and keeping the file open for the rest of the time. But this can + * only be done at network level. + * + * per default it is not a symlink + * */ +#ifdef HAVE_LSTAT + sce->is_symlink = 0; + + /* we want to only check for symlinks if we should block symlinks. + */ + if (!follow_symlink) { + if (stat_cache_lstat(srv, name, &lst) == 0) { + sce->is_symlink = 1; + } + + /* + * we assume "/" can not be symlink, so + * skip the symlink stuff if our path is / + **/ + else if (buffer_string_length(name) > 1) { + buffer *dname; + char *s_cur; + + dname = buffer_init(); + buffer_copy_buffer(dname, name); + + while ((s_cur = strrchr(dname->ptr, '/'))) { + buffer_string_set_length(dname, s_cur - dname->ptr); + if (dname->ptr == s_cur) { + break; + } + if (stat_cache_lstat(srv, dname, &lst) == 0) { + sce->is_symlink = 1; + break; + }; + }; + buffer_free(dname); + }; + } +#endif + +#ifdef HAVE_FAM_H + if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_FAM) { + stat_cache_fam_dir_monitor(srv, sc->scf, sce, name); + } +#endif + + *ret_sce = sce; + + return HANDLER_GO_ON; +} + +int stat_cache_open_rdonly_fstat (server *srv, connection *con, buffer *name, struct stat *st) { + /*(Note: O_NOFOLLOW affects only the final path segment, the target file, + * not any intermediate symlinks along the path)*/ + #ifndef O_BINARY + #define O_BINARY 0 + #endif + #ifndef O_LARGEFILE + #define O_LARGEFILE 0 + #endif + #ifndef O_NOCTTY + #define O_NOCTTY 0 + #endif + #ifndef O_NONBLOCK + #define O_NONBLOCK 0 + #endif + #ifndef O_NOFOLLOW + #define O_NOFOLLOW 0 + #endif + const int oflags = O_BINARY | O_LARGEFILE | O_NOCTTY | O_NONBLOCK + | (con->conf.follow_symlink ? 0 : O_NOFOLLOW); + const int fd = fdevent_open_cloexec(name->ptr, O_RDONLY | oflags, 0); + if (fd >= 0) { + if (0 == fstat(fd, st)) { + return fd; + } else { + close(fd); + } + } + UNUSED(srv); /*(might log_error_write(srv, ...) in the future)*/ + return -1; +} + +/** + * remove stat() from cache which havn't been stat()ed for + * more than 10 seconds + * + * + * walk though the stat-cache, collect the ids which are too old + * and remove them in a second loop + */ + +static int stat_cache_tag_old_entries(server *srv, splay_tree *t, int *keys, size_t *ndx) { + stat_cache_entry *sce; + + if (!t) return 0; + + stat_cache_tag_old_entries(srv, t->left, keys, ndx); + stat_cache_tag_old_entries(srv, t->right, keys, ndx); + + sce = t->data; + + if (srv->cur_ts - sce->stat_ts > 2) { + keys[(*ndx)++] = t->key; + } + + return 0; +} + +int stat_cache_trigger_cleanup(server *srv) { + stat_cache *sc; + size_t max_ndx = 0, i; + int *keys; + + sc = srv->stat_cache; + + if (!sc->files) return 0; + + keys = calloc(1, sizeof(int) * sc->files->size); + force_assert(NULL != keys); + + stat_cache_tag_old_entries(srv, sc->files, keys, &max_ndx); + + for (i = 0; i < max_ndx; i++) { + int ndx = keys[i]; + splay_tree *node; + + sc->files = splaytree_splay(sc->files, ndx); + + node = sc->files; + + if (node && (node->key == ndx)) { + stat_cache_entry_free(node->data); + sc->files = splaytree_delete(sc->files, ndx); + } + } + + free(keys); + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/stat_cache.h b/data/lighttpd/lighttpd-1.4.53/src/stat_cache.h new file mode 100644 index 000000000..e5fc2d748 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/stat_cache.h @@ -0,0 +1,45 @@ +#ifndef _FILE_CACHE_H_ +#define _FILE_CACHE_H_ +#include "first.h" + +#include "base_decls.h" +#include "buffer.h" +#include "etag.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> + +struct stat_cache; /* declaration */ + +typedef struct { + buffer *name; + buffer *etag; + + struct stat st; + + time_t stat_ts; + +#ifdef HAVE_LSTAT + char is_symlink; +#endif + +#ifdef HAVE_FAM_H + int dir_version; +#endif + + buffer *content_type; +} stat_cache_entry; + +int stat_cache_choose_engine (server *srv, const buffer *stat_cache_string); +struct stat_cache *stat_cache_init(server *srv); +void stat_cache_free(struct stat_cache *fc); + +const buffer * stat_cache_mimetype_by_ext(const connection *con, const char *name, size_t nlen); +const buffer * stat_cache_content_type_get(server *srv, connection *con, const buffer *name, stat_cache_entry *sce); +const buffer * stat_cache_etag_get(stat_cache_entry *sce, etag_flags_t flags); +handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **sce); +int stat_cache_open_rdonly_fstat (server *srv, connection *con, buffer *name, struct stat *st); + +int stat_cache_trigger_cleanup(server *srv); +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/status_counter.h b/data/lighttpd/lighttpd-1.4.53/src/status_counter.h new file mode 100644 index 000000000..cdbd5c137 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/status_counter.h @@ -0,0 +1,42 @@ +#ifndef _STATUS_COUNTER_H_ +#define _STATUS_COUNTER_H_ +#include "first.h" + +#include "base_decls.h" + +static inline +int *status_counter_get_counter(server *srv, const char *s, size_t len); +static inline +void status_counter_inc(server *srv, const char *s, size_t len); +static inline +void status_counter_dec(server *srv, const char *s, size_t len); +static inline +void status_counter_set(server *srv, const char *s, size_t len, int val); + +/* inline status counter routines */ + +#include "base.h" /* (srv->status) */ +#include "array.h" + +static inline +int *status_counter_get_counter(server *srv, const char *s, size_t len) { + return array_get_int_ptr(srv->status, s, len); +} + +static inline +void status_counter_inc(server *srv, const char *s, size_t len) { + ++(*array_get_int_ptr(srv->status, s, len)); +} + +static inline +void status_counter_dec(server *srv, const char *s, size_t len) { + --(*array_get_int_ptr(srv->status, s, len)); +} + +static inline +void status_counter_set(server *srv, const char *s, size_t len, int val) { + *array_get_int_ptr(srv->status, s, len) = val; +} + + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/stream.c b/data/lighttpd/lighttpd-1.4.53/src/stream.c new file mode 100644 index 000000000..9099bf67d --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/stream.c @@ -0,0 +1,157 @@ +#include "first.h" + +#include "stream.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> + +#include "sys-mmap.h" + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +/* don't want to block when open()ing a fifo */ +#if defined(O_NONBLOCK) +# define FIFO_NONBLOCK O_NONBLOCK +#else +# define FIFO_NONBLOCK 0 +#endif + +int stream_open(stream *f, const buffer *fn) { + +#if !defined(__WIN32) + + struct stat st; + int fd; + + f->start = NULL; + f->size = 0; + f->mapped = 0; + + if (-1 == (fd = open(fn->ptr, O_RDONLY | O_BINARY | FIFO_NONBLOCK))) { + return -1; + } + + if (-1 == fstat(fd, &st)) { + close(fd); + return -1; + } + + if (0 == st.st_size) { + /* empty file doesn't need a mapping */ + close(fd); + return 0; + } + + f->start = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + + if (MAP_FAILED == f->start) { + f->start = malloc((size_t)st.st_size); + if (NULL == f->start + || st.st_size != read(fd, f->start, (size_t)st.st_size)) { + free(f->start); + f->start = NULL; + close(fd); + return -1; + } + } else { + f->mapped = 1; + } + + close(fd); + + f->size = st.st_size; + return 0; + +#elif defined __WIN32 + + HANDLE *fh, *mh; + void *p; + LARGE_INTEGER fsize; + + f->start = NULL; + f->size = 0; + + fh = CreateFile(fn->ptr, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_READONLY, + NULL); + + if (!fh) return -1; + + if (0 != GetFileSizeEx(fh, &fsize)) { + CloseHandle(fh); + return -1; + } + + if (0 == fsize) { + CloseHandle(fh); + return 0; + } + + mh = CreateFileMapping( fh, + NULL, + PAGE_READONLY, + (sizeof(off_t) > 4) ? fsize >> 32 : 0, + fsize & 0xffffffff, + NULL); + + if (!mh) { +/* + LPVOID lpMsgBuf; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, NULL ); +*/ + CloseHandle(fh); + return -1; + } + + p = MapViewOfFile(mh, + FILE_MAP_READ, + 0, + 0, + 0); + CloseHandle(mh); + CloseHandle(fh); + + f->start = p; + f->size = (off_t)fsize; + return 0; + +#endif + +} + +int stream_close(stream *f) { +#ifdef HAVE_MMAP + if (f->start) { + if (f->mapped) { + f->mapped = 0; + munmap(f->start, f->size); + } else { + free(f->start); + } + } +#elif defined(__WIN32) + if (f->start) UnmapViewOfFile(f->start); +#endif + + f->start = NULL; + f->size = 0; + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/stream.h b/data/lighttpd/lighttpd-1.4.53/src/stream.h new file mode 100644 index 000000000..75b912093 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/stream.h @@ -0,0 +1,16 @@ +#ifndef _STREAM_H_ +#define _STREAM_H_ +#include "first.h" + +#include "buffer.h" + +typedef struct { + char *start; + off_t size; + int mapped; +} stream; + +int stream_open(stream *f, const buffer *fn); +int stream_close(stream *f); + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/sys-crypto.h b/data/lighttpd/lighttpd-1.4.53/src/sys-crypto.h new file mode 100644 index 000000000..8158abac5 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/sys-crypto.h @@ -0,0 +1,19 @@ +#ifndef LI_SYS_CRYPTO_H +#define LI_SYS_CRYPTO_H +#include "first.h" + +#if defined HAVE_LIBSSL && defined HAVE_OPENSSL_SSL_H +#define USE_OPENSSL_CRYPTO +#endif + +#ifdef HAVE_WOLFSSL_SSL_H +#define USE_OPENSSL_CRYPTO +/* wolfSSL needs to be built with ./configure --enable-lighty for lighttpd. + * Doing so defines OPENSSL_EXTRA and HAVE_LIGHTY in <wolfssl/options.h>, and + * these defines are necessary for wolfSSL headers to expose sufficient openssl + * compatibility layer for wolfSSL to be able to provide an openssl substitute + * for use by lighttpd */ +#include <wolfssl/options.h> +#endif + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/sys-endian.h b/data/lighttpd/lighttpd-1.4.53/src/sys-endian.h new file mode 100644 index 000000000..7fa28eead --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/sys-endian.h @@ -0,0 +1,71 @@ +#ifndef LI_SYS_ENDIAN_H +#define LI_SYS_ENDIAN_H +#include "first.h" + + +#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) + + +/* copied from of plasma_endian.h + * https://github.com/gstrauss/plasma/blob/master/plasma_endian.h + * (used with permission from the author (gstrauss)) */ +#if defined(__BYTE_ORDER__) \ + && ( defined(__ORDER_LITTLE_ENDIAN__) \ + || defined(__ORDER_BIG_ENDIAN__) \ + || defined(__ORDER_PDP_ENDIAN__) ) + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + #define __LITTLE_ENDIAN__ 1 + #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define __BIG_ENDIAN__ 1 + #endif +#elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) + #define __LITTLE_ENDIAN__ 1 +#elif !defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN) + #define __BIG_ENDIAN__ 1 +#elif defined(_WIN32) /* little endian on all current MS-supported platforms */ + #define __LITTLE_ENDIAN__ 1 +#elif defined(__GLIBC__) || defined(__linux__) + #include <endian.h> + #if __BYTE_ORDER == __LITTLE_ENDIAN + #define __LITTLE_ENDIAN__ 1 + #elif __BYTE_ORDER == __BIG_ENDIAN + #define __BIG_ENDIAN__ 1 + #endif +#elif defined(__sun__) && defined(__SVR4) + #include <sys/isa_defs.h> + #if defined(_LITTLE_ENDIAN) + #define __LITTLE_ENDIAN__ 1 + #elif defined(_BIG_ENDIAN) + #define __BIG_ENDIAN__ 1 + #endif +#elif defined(_AIX) + #include <sys/machine.h> + #if BYTE_ORDER == LITTLE_ENDIAN + #define __LITTLE_ENDIAN__ 1 + #elif BYTE_ORDER == BIG_ENDIAN + #define __BIG_ENDIAN__ 1 + #endif +#elif defined(__APPLE__) && defined(__MACH__) + #include <machine/endian.h> + #if BYTE_ORDER == LITTLE_ENDIAN + #define __LITTLE_ENDIAN__ 1 + #elif BYTE_ORDER == BIG_ENDIAN + #define __BIG_ENDIAN__ 1 + #endif +#elif defined(__FreeBSD__) || defined(__NetBSD__) \ + || defined(__OpenBSD__) || defined(__DragonFly__) + #include <machine/endian.h> + #if _BYTE_ORDER == _LITTLE_ENDIAN + #define __LITTLE_ENDIAN__ 1 + #elif _BYTE_ORDER == _BIG_ENDIAN + #define __BIG_ENDIAN__ 1 + #endif +#else /*(else assume little endian)*/ + #define __LITTLE_ENDIAN__ 1 +#endif + + +#endif /* !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__) */ + + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/sys-mmap.h b/data/lighttpd/lighttpd-1.4.53/src/sys-mmap.h new file mode 100644 index 000000000..51232ab55 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/sys-mmap.h @@ -0,0 +1,23 @@ +#ifndef LI_SYS_MMAP_H +#define LI_SYS_MMAP_H +#include "first.h" + +#if defined(HAVE_SYS_MMAN_H) +# include <sys/mman.h> +#else /* HAVE_SYS_MMAN_H */ + +# define PROT_SHARED 0 +# define MAP_SHARED 0 +# define PROT_READ 0 + +# define mmap(a, b, c, d, e, f) (-1) +# define munmap(a, b) (-1) + +#endif /* HAVE_SYS_MMAN_H */ + +/* NetBSD 1.3.x needs it; also make it available if mmap() is not present */ +#if !defined(MAP_FAILED) +# define MAP_FAILED ((char*)-1) +#endif + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/sys-socket.h b/data/lighttpd/lighttpd-1.4.53/src/sys-socket.h new file mode 100644 index 000000000..7851656be --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/sys-socket.h @@ -0,0 +1,45 @@ +#ifndef WIN32_SOCKET_H +#define WIN32_SOCKET_H +#include "first.h" + +#ifdef __WIN32 + +#include <winsock2.h> + +#define ECONNRESET WSAECONNRESET +#define EINPROGRESS WSAEINPROGRESS +#define EALREADY WSAEALREADY +#define ECONNABORTED WSAECONNABORTED + +#else + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif + +#endif + + +#ifndef INET_ADDRSTRLEN +#define INET_ADDRSTRLEN 16 +#endif +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +/* for solaris 2.5 and NetBSD 1.3.x */ +#ifndef HAVE_SOCKLEN_T +typedef int socklen_t; +#endif + +#ifndef SHUT_WR +#define SHUT_WR 1 +#endif + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/sys-strings.h b/data/lighttpd/lighttpd-1.4.53/src/sys-strings.h new file mode 100644 index 000000000..d4ff0f31b --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/sys-strings.h @@ -0,0 +1,20 @@ +#ifndef LI_SYS_STRINGS_H +#define LI_SYS_STRINGS_H +#include "first.h" + +#if defined(HAVE_STRINGS_H) + +#include <strings.h> + +#else /* HAVE_STRINGS_H */ + +#ifdef _MSC_VER +#define strcasecmp(s1,s2) _stricmp(s1,s2) +#define strncasecmp(s1,s2,n) _strnicmp(s1,s2,n) +#else +/* ??? */ +#endif + +#endif /* HAVE_STRINGS_H */ + +#endif diff --git a/data/lighttpd/lighttpd-1.4.53/src/t/test_array.c b/data/lighttpd/lighttpd-1.4.53/src/t/test_array.c new file mode 100644 index 000000000..79d36ecda --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/t/test_array.c @@ -0,0 +1,104 @@ +#include "first.h" + +#undef NDEBUG +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include "array.h" +#include "buffer.h" + +static void test_array_get_int_ptr (void) { + data_integer *di; + int *i; + array *a = array_init(); + + i = array_get_int_ptr(a, CONST_STR_LEN("abc")); + assert(NULL != i); + *i = 4; + i = array_get_int_ptr(a, CONST_STR_LEN("abc")); + assert(NULL != i); + assert(*i == 4); + di = (data_integer *)array_get_element_klen(a, CONST_STR_LEN("does-not-exist")); + assert(NULL == di); + di = (data_integer *)array_get_element_klen(a, CONST_STR_LEN("abc")); + assert(NULL != di); + assert(di->value == 4); + + array_free(a); +} + +static void test_array_insert_value (void) { + data_string *ds; + array *a = array_init(); + + array_insert_value(a, CONST_STR_LEN("def")); + ds = (data_string *)a->data[0]; + assert(NULL != ds); + assert(buffer_is_equal_string(ds->value, CONST_STR_LEN("def"))); + + array_free(a); +} + +static void test_array_insert_key_value (void) { + data_string *ds; + array *a = array_init(); + + array_insert_key_value(a, CONST_STR_LEN("abc"), CONST_STR_LEN("alfrag")); + ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("does-not-exist")); + assert(NULL == ds); + ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("abc")); + assert(NULL != ds); + assert(buffer_is_equal_string(ds->key, CONST_STR_LEN("abc"))); + assert(buffer_is_equal_string(ds->value, CONST_STR_LEN("alfrag"))); + + array_insert_key_value(a, CONST_STR_LEN("abc"), CONST_STR_LEN("hameplman")); + ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("does-not-exist")); + assert(NULL == ds); + ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("abc")); + assert(NULL != ds); + assert(buffer_is_equal_string(ds->key, CONST_STR_LEN("abc"))); + assert(buffer_is_equal_string(ds->value, CONST_STR_LEN("alfrag, hameplman"))); + + array_insert_key_value(a, CONST_STR_LEN("123"), CONST_STR_LEN("alfrag")); + ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("does-not-exist")); + assert(NULL == ds); + ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("123")); + assert(NULL != ds); + assert(buffer_is_equal_string(ds->key, CONST_STR_LEN("123"))); + assert(buffer_is_equal_string(ds->value, CONST_STR_LEN("alfrag"))); + + array_free(a); +} + +static void test_array_set_key_value (void) { + data_string *ds; + array *a = array_init(); + + array_set_key_value(a, CONST_STR_LEN("abc"), CONST_STR_LEN("def")); + ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("does-not-exist")); + assert(NULL == ds); + ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("abc")); + assert(NULL != ds); + assert(buffer_is_equal_string(ds->key, CONST_STR_LEN("abc"))); + assert(buffer_is_equal_string(ds->value, CONST_STR_LEN("def"))); + + array_set_key_value(a, CONST_STR_LEN("abc"), CONST_STR_LEN("ghi")); + ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("does-not-exist")); + assert(NULL == ds); + ds = (data_string *)array_get_element_klen(a, CONST_STR_LEN("abc")); + assert(NULL != ds); + assert(buffer_is_equal_string(ds->key, CONST_STR_LEN("abc"))); + assert(buffer_is_equal_string(ds->value, CONST_STR_LEN("ghi"))); + + array_free(a); +} + +int main() { + test_array_get_int_ptr(); + test_array_insert_value(); + test_array_insert_key_value(); + test_array_set_key_value(); + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/t/test_base64.c b/data/lighttpd/lighttpd-1.4.53/src/t/test_base64.c new file mode 100644 index 000000000..71c9c7cac --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/t/test_base64.c @@ -0,0 +1,60 @@ +#include "first.h" + +#include "base64.h" + +static const base64_charset encs[] = { BASE64_STANDARD, BASE64_URL }; +static buffer *check; + +inline +static void check_base64 (char *out, const size_t out_sz, const char *in, const size_t in_len, const base64_charset enc) { + force_assert(out_sz == li_to_base64_no_padding(out, out_sz, (const unsigned char *)in, in_len, enc)); + + buffer_reset(check); + force_assert(NULL != buffer_append_base64_decode(check, out, out_sz, enc)); + force_assert(buffer_is_equal_string(check, in, in_len)); +} + +static void check_all_len_0 (const base64_charset enc) { + check_base64(NULL, 0, "", 0, enc); +} + +static void check_all_len_1 (const base64_charset enc) { + unsigned int c1; + for (c1 = 0; c1 < 256; ++c1) { + unsigned char in[] = { c1 }; + char out[2] = { 0, 0 }; + check_base64(out, sizeof(out), (char *)in, sizeof(in), enc); + } +} + +static void check_all_len_2 (const base64_charset enc) { + unsigned int c1, c2; + for (c1 = 0; c1 < 256; ++c1) for (c2 = 0; c2 < 256; ++c2) { + unsigned char in[] = { c1, c2 }; + char out[3] = { 0, 0, 0 }; + check_base64(out, sizeof(out), (char *)in, sizeof(in), enc); + } +} + +static void check_all_len_3 (const base64_charset enc) { + unsigned int c1, c2, c3; + for (c1 = 0; c1 < 256; c1+=255) for (c2 = 0; c2 < 256; ++c2) for (c3 = 0; c3 < 256; ++c3) { + unsigned char in[] = { c1, c2, c3 }; + char out[4] = { 0, 0, 0, 0 }; + check_base64(out, sizeof(out), (char *)in, sizeof(in), enc); + } +} + +int main() { + check = buffer_init(); + + for (unsigned int enc = 0; enc < sizeof(encs)/sizeof(*encs); ++enc) { + check_all_len_0(encs[enc]); + check_all_len_1(encs[enc]); + check_all_len_2(encs[enc]); + check_all_len_3(encs[enc]); + } + + buffer_free(check); + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/t/test_buffer.c b/data/lighttpd/lighttpd-1.4.53/src/t/test_buffer.c new file mode 100644 index 000000000..1dae0b41f --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/t/test_buffer.c @@ -0,0 +1,157 @@ +#include "first.h" + +#undef NDEBUG +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include "buffer.h" + +static void run_buffer_path_simplify(buffer *psrc, buffer *pdest, const char *in, size_t in_len, const char *out, size_t out_len) { + buffer_copy_string_len(psrc, in, in_len); + + buffer_path_simplify(pdest, psrc); + + if (!buffer_is_equal_string(pdest, out, out_len)) { + fprintf(stderr, + "%s.%d: buffer_path_simplify('%s') failed: expected '%s', got '%s'\n", + __FILE__, + __LINE__, + in, + out, + pdest->ptr ? pdest->ptr : ""); + fflush(stderr); + abort(); + } else { + if (psrc != pdest) buffer_copy_buffer(psrc, pdest); + buffer_path_simplify(pdest, psrc); + + if (!buffer_is_equal_string(pdest, out, out_len)) { + fprintf(stderr, + "%s.%d: buffer_path_simplify('%s') failed - not idempotent: expected '%s', got '%s'\n", + __FILE__, + __LINE__, + in, + out, + pdest->ptr ? pdest->ptr : ""); + fflush(stderr); + abort(); + } + } +} + +static void test_buffer_path_simplify_with(buffer *psrc, buffer *pdest) { + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN(""), CONST_STR_LEN("")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/"), CONST_STR_LEN("/")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("//"), CONST_STR_LEN("/")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc"), CONST_STR_LEN("abc")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc//"), CONST_STR_LEN("abc/")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/./xyz"), CONST_STR_LEN("abc/xyz")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/.//xyz"), CONST_STR_LEN("abc/xyz")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/../xyz"), CONST_STR_LEN("/xyz")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/abc/./xyz"), CONST_STR_LEN("/abc/xyz")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/abc//./xyz"), CONST_STR_LEN("/abc/xyz")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/abc/../xyz"), CONST_STR_LEN("/xyz")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/../xyz/."), CONST_STR_LEN("/xyz/")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/abc/../xyz/."), CONST_STR_LEN("/xyz/")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/./xyz/.."), CONST_STR_LEN("abc/")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/abc/./xyz/.."), CONST_STR_LEN("/abc/")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("."), CONST_STR_LEN("")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN(".."), CONST_STR_LEN("")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("..."), CONST_STR_LEN("...")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("...."), CONST_STR_LEN("....")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN(".../"), CONST_STR_LEN(".../")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("./xyz/.."), CONST_STR_LEN("/")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN(".//xyz/.."), CONST_STR_LEN("/")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/./xyz/.."), CONST_STR_LEN("/")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN(".././xyz/.."), CONST_STR_LEN("/")); + run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/.././xyz/.."), CONST_STR_LEN("/")); +} + +static void test_buffer_path_simplify(void) { + buffer *psrc = buffer_init(); + buffer *pdest = buffer_init(); + + /* test with using the same buffer and with using different buffers */ + test_buffer_path_simplify_with(psrc, psrc); + test_buffer_path_simplify_with(pdest, psrc); + + buffer_free(psrc); + buffer_free(pdest); +} + +static void test_buffer_to_lower_upper(void) { + buffer *psrc = buffer_init(); + + buffer_copy_string_len(psrc, CONST_STR_LEN("0123456789abcdefghijklmnopqrstuvwxyz")); + buffer_to_lower(psrc); + assert(buffer_is_equal_string(psrc, CONST_STR_LEN("0123456789abcdefghijklmnopqrstuvwxyz"))); + buffer_to_upper(psrc); + assert(buffer_is_equal_string(psrc, CONST_STR_LEN("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"))); + buffer_to_upper(psrc); + assert(buffer_is_equal_string(psrc, CONST_STR_LEN("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"))); + buffer_to_lower(psrc); + assert(buffer_is_equal_string(psrc, CONST_STR_LEN("0123456789abcdefghijklmnopqrstuvwxyz"))); + + buffer_free(psrc); +} + +static void test_buffer_string_space(void) { + buffer *b = buffer_init(); + size_t space; + + space = buffer_string_space(b); + assert(0 == space); + buffer_copy_string_len(b, CONST_STR_LEN("")); + space = buffer_string_space(b); + assert(space > 0); + assert(space + buffer_string_length(b) == b->size - 1); + buffer_commit(b, b->size - 1); + assert(b->used == b->size); + space = buffer_string_space(b); + assert(0 == space); + + buffer_free(b); +} + +static void test_buffer_append_path_len(void) { + buffer *b = buffer_init(); + + buffer_append_path_len(b, CONST_STR_LEN("a")); + assert(buffer_is_equal_string(b, CONST_STR_LEN("/a"))); + buffer_clear(b); + buffer_append_path_len(b, CONST_STR_LEN("a")); + assert(buffer_is_equal_string(b, CONST_STR_LEN("/a"))); + buffer_clear(b); + buffer_append_path_len(b, CONST_STR_LEN("/a")); + assert(buffer_is_equal_string(b, CONST_STR_LEN("/a"))); + buffer_copy_string_len(b, CONST_STR_LEN("/")); + buffer_append_path_len(b, CONST_STR_LEN("a")); + assert(buffer_is_equal_string(b, CONST_STR_LEN("/a"))); + buffer_copy_string_len(b, CONST_STR_LEN("/")); + buffer_append_path_len(b, CONST_STR_LEN("/a")); + assert(buffer_is_equal_string(b, CONST_STR_LEN("/a"))); + buffer_copy_string_len(b, CONST_STR_LEN("a")); + buffer_append_path_len(b, CONST_STR_LEN("a")); + assert(buffer_is_equal_string(b, CONST_STR_LEN("a/a"))); + buffer_copy_string_len(b, CONST_STR_LEN("a/")); + buffer_append_path_len(b, CONST_STR_LEN("a")); + assert(buffer_is_equal_string(b, CONST_STR_LEN("a/a"))); + buffer_copy_string_len(b, CONST_STR_LEN("a/")); + buffer_append_path_len(b, CONST_STR_LEN("/a")); + assert(buffer_is_equal_string(b, CONST_STR_LEN("a/a"))); + buffer_copy_string_len(b, CONST_STR_LEN("/a/")); + buffer_append_path_len(b, CONST_STR_LEN("/a")); + assert(buffer_is_equal_string(b, CONST_STR_LEN("/a/a"))); + + buffer_free(b); +} + +int main() { + test_buffer_path_simplify(); + test_buffer_to_lower_upper(); + test_buffer_string_space(); + test_buffer_append_path_len(); + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/t/test_burl.c b/data/lighttpd/lighttpd-1.4.53/src/t/test_burl.c new file mode 100644 index 000000000..7be9be50d --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/t/test_burl.c @@ -0,0 +1,143 @@ +#include "first.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "burl.h" + +static void run_burl_normalize (buffer *psrc, buffer *ptmp, int flags, int line, const char *in, size_t in_len, const char *out, size_t out_len) { + int qs; + buffer_copy_string_len(psrc, in, in_len); + qs = burl_normalize(psrc, ptmp, flags); + if (out_len == (size_t)-2) { + if (-2 == qs) return; + fprintf(stderr, + "%s.%d: %s('%s') failed: expected error, got '%s'\n", + __FILE__, line, __func__+4, in, psrc->ptr); + } + else { + if (buffer_is_equal_string(psrc, out, out_len)) return; + fprintf(stderr, + "%s.%d: %s('%s') failed: expected '%s', got '%s'\n", + __FILE__, line, __func__+4, in, out, psrc->ptr); + } + fflush(stderr); + abort(); +} + +static void test_burl_normalize (void) { + buffer *psrc = buffer_init(); + buffer *ptmp = buffer_init(); + int flags; + + flags = HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED; + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("no-slash"), CONST_STR_LEN("no-slash")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/"), CONST_STR_LEN("/")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc"), CONST_STR_LEN("/abc")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc/"), CONST_STR_LEN("/abc/")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc/def"), CONST_STR_LEN("/abc/def")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?"), CONST_STR_LEN("/abc?")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d"), CONST_STR_LEN("/abc?d")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d="), CONST_STR_LEN("/abc?d=")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d=e"), CONST_STR_LEN("/abc?d=e")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d=e&"), CONST_STR_LEN("/abc?d=e&")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d=e&f"), CONST_STR_LEN("/abc?d=e&f")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d=e&f=g"), CONST_STR_LEN("/abc?d=e&f=g")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d=e&f=g#"), CONST_STR_LEN("/abc?d=e&f=g")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d=e&f=g#any"), CONST_STR_LEN("/abc?d=e&f=g")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%2F"), CONST_STR_LEN("/%2F")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%2f"), CONST_STR_LEN("/%2F")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%20"), CONST_STR_LEN("/%20")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%2b"), CONST_STR_LEN("/%2B")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%2B"), CONST_STR_LEN("/%2B")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%3a"), CONST_STR_LEN("/%3A")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%3A"), CONST_STR_LEN("/%3A")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/~test%20รค_"), CONST_STR_LEN("/~test%20%C3%A4_")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/\375"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/\376"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/\377"), "", (size_t)-2); + + flags = HTTP_PARSEOPT_URL_NORMALIZE_REQUIRED; + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/"), CONST_STR_LEN("/")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc"), CONST_STR_LEN("/abc")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc/"), CONST_STR_LEN("/abc/")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc/def"), CONST_STR_LEN("/abc/def")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?"), CONST_STR_LEN("/abc?")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d"), CONST_STR_LEN("/abc?d")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d="), CONST_STR_LEN("/abc?d=")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d=e"), CONST_STR_LEN("/abc?d=e")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d=e&"), CONST_STR_LEN("/abc?d=e&")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d=e&f"), CONST_STR_LEN("/abc?d=e&f")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d=e&f=g"), CONST_STR_LEN("/abc?d=e&f=g")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d=e&f=g#"), CONST_STR_LEN("/abc?d=e&f=g")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc?d=e&f=g#any"), CONST_STR_LEN("/abc?d=e&f=g")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%2F"), CONST_STR_LEN("/%2F")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%2f"), CONST_STR_LEN("/%2F")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%20"), CONST_STR_LEN("/%20")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%2b"), CONST_STR_LEN("/+")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%2B"), CONST_STR_LEN("/+")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%3a"), CONST_STR_LEN("/:")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/%3A"), CONST_STR_LEN("/:")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/~test%20รค_"), CONST_STR_LEN("/~test%20%C3%A4_")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/\375"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/\376"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/\377"), "", (size_t)-2); + + flags |= HTTP_PARSEOPT_URL_NORMALIZE_CTRLS_REJECT; + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/\a"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/\t"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/\r"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/\177"), "", (size_t)-2); + + #if defined(__WIN32) || defined(__CYGWIN__) + flags |= HTTP_PARSEOPT_URL_NORMALIZE_PATH_BACKSLASH_TRANS; + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a\\b"), CONST_STR_LEN("/a/b")); + #endif + + flags |= HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE; + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/b?c=/"), CONST_STR_LEN("/a/b?c=/")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/b?c=%2f"), CONST_STR_LEN("/a/b?c=/")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a%2fb"), CONST_STR_LEN("/a/b")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a%2Fb"), CONST_STR_LEN("/a/b")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a%2fb?c=/"), CONST_STR_LEN("/a/b?c=/")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a%2fb?c=%2f"), CONST_STR_LEN("/a/b?c=/")); + flags &= ~HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_DECODE; + + flags |= HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT; + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a%2fb"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a%2Fb"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/b?c=%2f"), CONST_STR_LEN("/a/b?c=/")); + flags &= ~HTTP_PARSEOPT_URL_NORMALIZE_PATH_2F_REJECT; + + flags |= HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE; + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("./a/b"), CONST_STR_LEN("/a/b")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("../a/b"), CONST_STR_LEN("/a/b")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/./b"), CONST_STR_LEN("/a/b")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/../b"), CONST_STR_LEN("/b")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/b/."), CONST_STR_LEN("/a/b/")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/b/.."), CONST_STR_LEN("/a/")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/../b/.."), CONST_STR_LEN("/")); + flags &= ~HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REMOVE; + + flags |= HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT; + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("./a/b"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("../a/b"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/./b"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/../b"), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/b/."), "", (size_t)-2); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/b/.."), "", (size_t)-2); + flags &= ~HTTP_PARSEOPT_URL_NORMALIZE_PATH_DOTSEG_REJECT; + + flags |= HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS; + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/b?c=d+e"), CONST_STR_LEN("/a/b?c=d+e")); + run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/a/b?c=d%20e"), CONST_STR_LEN("/a/b?c=d+e")); + flags &= ~HTTP_PARSEOPT_URL_NORMALIZE_QUERY_20_PLUS; + + buffer_free(psrc); + buffer_free(ptmp); +} + +int main (void) { + test_burl_normalize(); + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/t/test_configfile.c b/data/lighttpd/lighttpd-1.4.53/src/t/test_configfile.c new file mode 100644 index 000000000..735a79673 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/t/test_configfile.c @@ -0,0 +1,73 @@ +#include "first.h" + +#undef NDEBUG +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +#include "configfile-glue.c" + +const struct { + const char *string; + const char *rmtstr; + int rmtfamily; + int expect; +} rmtmask[] = { + { "1.0.0.1/1", "1.0.0.1", AF_INET, 1 } + ,{ "254.254.254.254/1", "254.0.0.1", AF_INET, 1 } + ,{ "254.254.254.252/31", "254.254.254.253", AF_INET, 1 } + ,{ "254.254.254.253/31", "254.254.254.254", AF_INET, 0 } + ,{ "254.254.254.253/32", "254.254.254.254", AF_INET, 0 } + ,{ "254.254.254.254/32", "254.254.254.254", AF_INET, 1 } + #ifdef HAVE_IPV6 + ,{ "2001::/3", "2001::1", AF_INET6, 1 } + ,{ "2f01::/5", "2701::1", AF_INET6, 0 } + ,{ "2f01::/32", "2f01::1", AF_INET6, 1 } + ,{ "2f01::/32", "2f02::1", AF_INET6, 0 } + ,{ "2001::1/127", "2001::1", AF_INET6, 1 } + ,{ "2001::1/127", "2001::2", AF_INET6, 0 } + ,{ "2001::2/128", "2001::2", AF_INET6, 1 } + ,{ "2001::2/128", "2001::3", AF_INET6, 0 } + ,{ "1.0.0.1/1", "::ffff:1.0.0.1", AF_INET6, 1 } + ,{ "254.254.254.254/1", "::ffff:254.0.0.1", AF_INET6, 1 } + ,{ "254.254.254.252/31", "::ffff:254.254.254.253", AF_INET6, 1 } + ,{ "254.254.254.253/31", "::ffff:254.254.254.254", AF_INET6, 0 } + ,{ "254.254.254.253/32", "::ffff:254.254.254.254", AF_INET6, 0 } + ,{ "254.254.254.254/32", "::ffff:254.254.254.254", AF_INET6, 1 } + ,{ "::ffff:1.0.0.1/97", "1.0.0.1", AF_INET, 1 } + ,{ "::ffff:254.254.254.254/97", "254.0.0.1", AF_INET, 1 } + ,{ "::ffff:254.254.254.252/127", "254.254.254.253", AF_INET, 1 } + ,{ "::ffff:254.254.254.253/127", "254.254.254.254", AF_INET, 0 } + ,{ "::ffff:254.254.254.253/128", "254.254.254.254", AF_INET, 0 } + ,{ "::ffff:254.254.254.254/128", "254.254.254.254", AF_INET, 1 } + #endif +}; + +static void test_configfile_addrbuf_eq_remote_ip_mask (void) { + int i, m; + buffer * const s = buffer_init(); + char *slash; + sock_addr rmt; + + for (i = 0; i < (int)(sizeof(rmtmask)/sizeof(rmtmask[0])); ++i) { + if (1 != sock_addr_inet_pton(&rmt, rmtmask[i].rmtstr, rmtmask[i].rmtfamily, 0)) exit(-1); /*(bad test)*/ + buffer_copy_string(s, rmtmask[i].string); + slash = strchr(s->ptr,'/'); assert(slash); + m = config_addrbuf_eq_remote_ip_mask(NULL, s, slash, &rmt); + if (m != rmtmask[i].expect) { + fprintf(stderr, "failed assertion: %s %s %s\n", + rmtmask[i].string, + rmtmask[i].expect ? "==" : "!=", + rmtmask[i].rmtstr); + exit(-1); + } + } + + buffer_free(s); +} + +int main (void) { + test_configfile_addrbuf_eq_remote_ip_mask(); + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/t/test_keyvalue.c b/data/lighttpd/lighttpd-1.4.53/src/t/test_keyvalue.c new file mode 100644 index 000000000..83dac9236 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/t/test_keyvalue.c @@ -0,0 +1,124 @@ +#include "first.h" + +#undef NDEBUG +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> /* STDERR_FILENO */ + +#include "keyvalue.c" + +#ifdef HAVE_PCRE_H +static pcre_keyvalue_buffer * test_keyvalue_test_kvb_init (void) { + pcre_keyvalue_buffer *kvb = pcre_keyvalue_buffer_init(); + buffer *k = buffer_init(); + buffer *v = buffer_init(); + server srv; + + memset(&srv, 0, sizeof(srv)); + srv.errorlog_fd = STDERR_FILENO; + srv.errorlog_mode = ERRORLOG_FD; + srv.errorlog_buf = buffer_init(); + + buffer_copy_string_len(k, CONST_STR_LEN("^/foo($|\\?.+)")); + buffer_copy_string_len(v, CONST_STR_LEN("/foo/$1")); + assert(0 == pcre_keyvalue_buffer_append(&srv, kvb, k, v)); + buffer_copy_string_len(k, CONST_STR_LEN("^/bar(?:$|\\?(.+))")); + buffer_copy_string_len(v, CONST_STR_LEN("/?bar&$1")); + assert(0 == pcre_keyvalue_buffer_append(&srv, kvb, k, v)); + buffer_copy_string_len(k, CONST_STR_LEN("^/redirect(?:\\?(.*))?$")); + buffer_copy_string_len(v, CONST_STR_LEN("/?seg=%1&$1")); + assert(0 == pcre_keyvalue_buffer_append(&srv, kvb, k, v)); + buffer_copy_string_len(k, CONST_STR_LEN("^(/[^?]*)(?:\\?(.*))?$")); + buffer_copy_string_len(v, CONST_STR_LEN("/?file=$1&$2")); + assert(0 == pcre_keyvalue_buffer_append(&srv, kvb, k, v)); + + buffer_free(k); + buffer_free(v); + buffer_free(srv.errorlog_buf); + + return kvb; +} + +static void test_keyvalue_pcre_keyvalue_buffer_process (void) { + pcre_keyvalue_buffer *kvb = test_keyvalue_test_kvb_init(); + buffer *url = buffer_init(); + buffer *result = buffer_init(); + struct burl_parts_t burl; + cond_cache_t cache; + pcre_keyvalue_ctx ctx; + handler_t rc; + + ctx.burl = &burl; + burl.scheme = buffer_init(); + burl.authority = buffer_init(); + burl.port = 80; + burl.path = buffer_init(); + burl.query = buffer_init(); + buffer_copy_string_len(burl.scheme, CONST_STR_LEN("http")); + buffer_copy_string_len(burl.authority, CONST_STR_LEN("www.example.com")); + /* model outer conditional match of $HTTP["host"] =~ "^(www).example.com$" */ + ctx.cache = &cache; + memset(&cache, 0, sizeof(cache)); + cache.patterncount = 2; + cache.comp_value = burl.authority; + cache.matches[0] = 0; + cache.matches[1] = 15; + cache.matches[2] = 0; + cache.matches[3] = 3; + + /* converted from prior sparse tests/mod-redirect.t and tests/mod-rewrite.t + * (real-world use should prefer ${url.path} and ${qsa} in substitutions) + */ + + buffer_copy_string_len(url, CONST_STR_LEN("/foo")); + buffer_copy_string_len(burl.path, CONST_STR_LEN("/foo")); + buffer_clear(burl.query); + rc = pcre_keyvalue_buffer_process(kvb, &ctx, url, result); + assert(HANDLER_FINISHED == rc); + assert(buffer_is_equal_string(result, CONST_STR_LEN("/foo/"))); + + buffer_copy_string_len(url, CONST_STR_LEN("/foo?a=b")); + buffer_copy_string_len(burl.path, CONST_STR_LEN("/foo")); + buffer_copy_string_len(burl.query, CONST_STR_LEN("a=b")); + rc = pcre_keyvalue_buffer_process(kvb, &ctx, url, result); + assert(HANDLER_FINISHED == rc); + assert(buffer_is_equal_string(result, CONST_STR_LEN("/foo/?a=b"))); + + buffer_copy_string_len(url, CONST_STR_LEN("/bar?a=b")); + buffer_copy_string_len(burl.path, CONST_STR_LEN("/bar")); + buffer_copy_string_len(burl.query, CONST_STR_LEN("a=b")); + rc = pcre_keyvalue_buffer_process(kvb, &ctx, url, result); + assert(HANDLER_FINISHED == rc); + assert(buffer_is_equal_string(result, CONST_STR_LEN("/?bar&a=b"))); + + buffer_copy_string_len(url, CONST_STR_LEN("/nofile?a=b")); + buffer_copy_string_len(burl.path, CONST_STR_LEN("/nofile")); + buffer_copy_string_len(burl.query, CONST_STR_LEN("a=b")); + rc = pcre_keyvalue_buffer_process(kvb, &ctx, url, result); + assert(HANDLER_FINISHED == rc); + assert(buffer_is_equal_string(result, CONST_STR_LEN("/?file=/nofile&a=b"))); + + buffer_copy_string_len(url, CONST_STR_LEN("/redirect?a=b")); + buffer_copy_string_len(burl.path, CONST_STR_LEN("/redirect")); + buffer_copy_string_len(burl.query, CONST_STR_LEN("a=b")); + rc = pcre_keyvalue_buffer_process(kvb, &ctx, url, result); + assert(HANDLER_FINISHED == rc); + assert(buffer_is_equal_string(result, CONST_STR_LEN("/?seg=www&a=b"))); + + buffer_free(url); + buffer_free(result); + buffer_free(burl.scheme); + buffer_free(burl.authority); + buffer_free(burl.path); + buffer_free(burl.query); + pcre_keyvalue_buffer_free(kvb); +} +#endif + +int main (void) { + #ifdef HAVE_PCRE_H + test_keyvalue_pcre_keyvalue_buffer_process(); + #endif + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/t/test_mod_access.c b/data/lighttpd/lighttpd-1.4.53/src/t/test_mod_access.c new file mode 100644 index 000000000..820cee4c7 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/t/test_mod_access.c @@ -0,0 +1,55 @@ +#include "first.h" + +#undef NDEBUG +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +#include "mod_access.c" + +static void test_mod_access_check(void) { + array *allow = array_init(); + array *deny = array_init(); + buffer *urlpath = buffer_init(); + int lc = 0; + + /* empty allow and deny lists */ + buffer_copy_string_len(urlpath, CONST_STR_LEN("/")); + assert(1 == mod_access_check(allow, deny, urlpath, lc)); + + array_insert_value(deny, CONST_STR_LEN("~")); + array_insert_value(deny, CONST_STR_LEN(".inc")); + + /* deny */ + buffer_copy_string_len(urlpath, CONST_STR_LEN("/index.html~")); + assert(0 == mod_access_check(allow, deny, urlpath, lc)); + lc = 1; + buffer_copy_string_len(urlpath, CONST_STR_LEN("/index.INC")); + assert(0 == mod_access_check(allow, deny, urlpath, lc)); + lc = 0; + + array_insert_value(allow, CONST_STR_LEN(".txt")); + array_insert_value(deny, CONST_STR_LEN(".txt"));/* allow takes precedence */ + + /* explicitly allowed */ + buffer_copy_string_len(urlpath, CONST_STR_LEN("/ssi-include.txt")); + assert(1 == mod_access_check(allow, deny, urlpath, lc)); + lc = 1; + buffer_copy_string_len(urlpath, CONST_STR_LEN("/ssi-include.TXT")); + assert(1 == mod_access_check(allow, deny, urlpath, lc)); + lc = 0; + + /* allow not empty and urlpath not explicitly allowed */ + buffer_copy_string_len(urlpath, CONST_STR_LEN("/cgi.pl")); + assert(0 == mod_access_check(allow, deny, urlpath, lc)); + + array_free(allow); + array_free(deny); + buffer_free(urlpath); +} + +int main (void) { + test_mod_access_check(); + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/t/test_mod_evhost.c b/data/lighttpd/lighttpd-1.4.53/src/t/test_mod_evhost.c new file mode 100644 index 000000000..0600479e8 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/t/test_mod_evhost.c @@ -0,0 +1,80 @@ +#include "first.h" + +#undef NDEBUG +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +#include "mod_evhost.c" + +static plugin_config * test_mod_evhost_plugin_config_init(void) { + plugin_config *s = calloc(1, sizeof(plugin_config)); + s->path_pieces_raw = buffer_init(); + s->path_pieces = NULL; + s->len = 0; + return s; +} + +static void test_mod_evhost_plugin_config_free(plugin_config *s) { + buffer_free(s->path_pieces_raw); + for (size_t i = 0; i < s->len; ++i) buffer_free(s->path_pieces[i]); + free(s->path_pieces); + free(s); +} + +static void test_mod_evhost_build_doc_root_path(void) { + buffer *authority = buffer_init_string("host.example.org"); + buffer *b = buffer_init(); + array *a = array_init(); + struct ttt { + const char *pattern; + size_t plen; + const char *expect; + size_t elen; + } tt[] = { + /* correct pattern not using dot notation */ + { CONST_STR_LEN("/web/%3/"), + CONST_STR_LEN("/web/host/") } + /* correct pattern using dot notation */ + ,{ CONST_STR_LEN("/web/%{3.1}/%{3.2}/%3/"), + CONST_STR_LEN("/web/h/o/host/") } + /* other pattern 1 */ + ,{ CONST_STR_LEN("/web/%{3.0}/"), + CONST_STR_LEN("/web/host/") } + /* other pattern 2 */ + ,{ CONST_STR_LEN("/web/%3.\1/"), + CONST_STR_LEN("/web/host.\1/") } + }; + + for (size_t i = 0; i < sizeof(tt)/sizeof(tt[0]); ++i) { + struct ttt *t = tt+i; + plugin_config *s = test_mod_evhost_plugin_config_init(); + buffer_copy_string_len(s->path_pieces_raw, t->pattern, t->plen); + assert(0 == mod_evhost_parse_pattern(s)); + mod_evhost_build_doc_root_path(b, a, authority, s->path_pieces, s->len); + assert(buffer_is_equal_string(b, t->expect, t->elen)); + test_mod_evhost_plugin_config_free(s); + } + + buffer_free(authority); + buffer_free(b); + array_free(a); +} + +int main (void) { + test_mod_evhost_build_doc_root_path(); + + return 0; +} + +/* + * stub functions + */ + +handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **sce) { + UNUSED(srv); + UNUSED(con); + UNUSED(name); + UNUSED(sce); + return HANDLER_GO_ON; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/t/test_mod_simple_vhost.c b/data/lighttpd/lighttpd-1.4.53/src/t/test_mod_simple_vhost.c new file mode 100644 index 000000000..55af4c287 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/t/test_mod_simple_vhost.c @@ -0,0 +1,53 @@ +#include "first.h" + +#undef NDEBUG +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> + +#include "mod_simple_vhost.c" + +static void test_mod_simple_vhost_build_doc_root_path(void) { + buffer *sroot = buffer_init(); + buffer *host = buffer_init(); + buffer *droot = buffer_init(); + buffer *result= buffer_init(); + + buffer_copy_string_len(sroot, CONST_STR_LEN("/sroot/a/")); + buffer_copy_string_len(host, CONST_STR_LEN("www.example.org")); + buffer_copy_string_len(droot, CONST_STR_LEN("/droot/b/")); + build_doc_root_path(result, sroot, host, droot); + assert(buffer_is_equal_string(result, CONST_STR_LEN("/sroot/a/www.example.org/droot/b/"))); + + buffer_copy_string_len(host, CONST_STR_LEN("www.example.org:8080")); + build_doc_root_path(result, sroot, host, droot); + assert(buffer_is_equal_string(result, CONST_STR_LEN("/sroot/a/www.example.org/droot/b/"))); + + buffer_copy_string_len(droot, CONST_STR_LEN("")); + build_doc_root_path(result, sroot, host, droot); + assert(buffer_is_equal_string(result, CONST_STR_LEN("/sroot/a/www.example.org/"))); + + buffer_free(sroot); + buffer_free(host); + buffer_free(droot); + buffer_free(result); +} + +int main (void) { + test_mod_simple_vhost_build_doc_root_path(); + + return 0; +} + +/* + * stub functions + */ + +handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **sce) { + UNUSED(srv); + UNUSED(con); + UNUSED(name); + UNUSED(sce); + return HANDLER_GO_ON; +} + diff --git a/data/lighttpd/lighttpd-1.4.53/src/t/test_request.c b/data/lighttpd/lighttpd-1.4.53/src/t/test_request.c new file mode 100644 index 000000000..e14647b4d --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/t/test_request.c @@ -0,0 +1,490 @@ +#include "first.h" + +#undef NDEBUG +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "request.h" +#include "base.h" +#include "burl.h" + +static void test_request_connection_reset(connection *con) +{ + con->request.http_method = HTTP_METHOD_UNSET; + con->request.http_version = HTTP_VERSION_UNSET; + con->request.http_host = NULL; + con->request.htags = 0; + con->request.content_length = 0; + con->header_len = 0; + con->http_status = 0; + buffer_reset(con->proto); + buffer_reset(con->parse_request); + buffer_reset(con->request.request); + buffer_reset(con->request.request_line); + buffer_reset(con->request.orig_uri); + buffer_reset(con->request.uri); + array_reset(con->request.headers); +} + +static void run_http_request_parse(server *srv, connection *con, int line, int status, const char *desc, const char *req, size_t reqlen) +{ + test_request_connection_reset(con); + buffer_copy_string_len(con->request.request, req, reqlen); + http_request_parse(srv, con); + if (con->http_status != status) { + fprintf(stderr, + "%s.%d: %s() failed: expected '%d', got '%d' for test %s\n", + __FILE__, line, "http_request_parse", status, con->http_status, + desc); + fflush(stderr); + abort(); + } +} + +static void test_request_http_request_parse(server *srv, connection *con) +{ + data_string *ds; + + run_http_request_parse(srv, con, __LINE__, 0, + "hostname", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: www.example.org\r\n" + "\r\n")); + assert(buffer_is_equal_string(con->request.http_host, + CONST_STR_LEN("www.example.org"))); + + run_http_request_parse(srv, con, __LINE__, 0, + "IPv4 address", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: 127.0.0.1\r\n" + "\r\n")); + assert(buffer_is_equal_string(con->request.http_host, + CONST_STR_LEN("127.0.0.1"))); + + run_http_request_parse(srv, con, __LINE__, 0, + "IPv6 address", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: [::1]\r\n" + "\r\n")); + assert(buffer_is_equal_string(con->request.http_host, + CONST_STR_LEN("[::1]"))); + + run_http_request_parse(srv, con, __LINE__, 0, + "hostname + port", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: www.example.org:80\r\n" + "\r\n")); + assert(buffer_is_equal_string(con->request.http_host, + CONST_STR_LEN("www.example.org"))); + + run_http_request_parse(srv, con, __LINE__, 0, + "IPv4 address + port", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: 127.0.0.1:80\r\n" + "\r\n")); + assert(buffer_is_equal_string(con->request.http_host, + CONST_STR_LEN("127.0.0.1"))); + + run_http_request_parse(srv, con, __LINE__, 0, + "IPv6 address + port", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: [::1]:80\r\n" + "\r\n")); + assert(buffer_is_equal_string(con->request.http_host, + CONST_STR_LEN("[::1]"))); + + run_http_request_parse(srv, con, __LINE__, 400, + "directory traversal", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: ../123.org\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "leading and trailing dot", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: .jsdh.sfdg.sdfg.\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 0, + "trailing dot is ok", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: jsdh.sfdg.sdfg.\r\n" + "\r\n")); + assert(buffer_is_equal_string(con->request.http_host, + CONST_STR_LEN("jsdh.sfdg.sdfg"))); + + run_http_request_parse(srv, con, __LINE__, 400, + "leading dot", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: .jsdh.sfdg.sdfg\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "two dots", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: jsdh..sfdg.sdfg\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "broken port-number", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: jsdh.sfdg.sdfg:asd\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "negative port-number", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: jsdh.sfdg.sdfg:-1\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "port given but host missing", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: :80\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "port and host are broken", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: .jsdh.sfdg.:sdfg.\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 0, + "allowed characters in host-name", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: a.b-c.d123\r\n" + "\r\n")); + assert(buffer_is_equal_string(con->request.http_host, + CONST_STR_LEN("a.b-c.d123"))); + + run_http_request_parse(srv, con, __LINE__, 400, + "leading dash", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: -a.c\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "dot only", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: .\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "broken IPv4 address - non-digit", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: a192.168.2.10:1234\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "broken IPv4 address - too short", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: 192.168.2:1234\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "IPv6 address + SQL injection", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: [::1]' UNION SELECT '/\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "IPv6 address + path traversal", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: [::1]/../../../\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "negative Content-Length", + CONST_STR_LEN("POST /12345.txt HTTP/1.0\r\n" + "Content-Length: -2\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 411, + "Content-Length is empty", + CONST_STR_LEN("POST /12345.txt HTTP/1.0\r\n" + "Host: 123.example.org\r\n" + "Content-Length:\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "Host missing", + CONST_STR_LEN("GET / HTTP/1.1\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "empty request-URI", + CONST_STR_LEN("GET HTTP/1.0\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 0, + "#1232 - duplicate headers with line-wrapping", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Location: foo\r\n" + "Location: foobar\r\n" + " baz\r\n" + "\r\n")); + ds = (data_string *) + array_get_element_klen(con->request.headers, CONST_STR_LEN("Location")); + assert(ds + && buffer_is_equal_string(ds->value, + CONST_STR_LEN("foo, foobar baz"))); + + run_http_request_parse(srv, con, __LINE__, 0, + "#1232 - duplicate headers with line-wrapping - test 2", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Location: \r\n" + "Location: foobar\r\n" + " baz\r\n" + "\r\n")); + ds = (data_string *) + array_get_element_klen(con->request.headers, CONST_STR_LEN("Location")); + assert(ds + && buffer_is_equal_string(ds->value, CONST_STR_LEN("foobar baz"))); + + run_http_request_parse(srv, con, __LINE__, 0, + "#1232 - duplicate headers with line-wrapping - test 3", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "A: \r\n" + "Location: foobar\r\n" + " baz\r\n" + "\r\n")); + ds = (data_string *) + array_get_element_klen(con->request.headers, CONST_STR_LEN("Location")); + assert(ds + && buffer_is_equal_string(ds->value, CONST_STR_LEN("foobar baz"))); + + run_http_request_parse(srv, con, __LINE__, 400, + "missing protocol", + CONST_STR_LEN("GET /\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 0, + "zeros in protocol version", + CONST_STR_LEN("GET / HTTP/01.01\r\n" + "Host: foo\r\n" + "\r\n")); + assert(con->request.http_version == HTTP_VERSION_1_1); + + run_http_request_parse(srv, con, __LINE__, 400, + "missing major version", + CONST_STR_LEN("GET / HTTP/.01\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "missing minor version", + CONST_STR_LEN("GET / HTTP/01.\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "strings as version", + CONST_STR_LEN("GET / HTTP/a.b\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "missing protocol + unknown method", + CONST_STR_LEN("BC /\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "missing protocol + unknown method + missing URI", + CONST_STR_LEN("ABC\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 501, + "unknown method", + CONST_STR_LEN("ABC / HTTP/1.0\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 505, + "unknown protocol", + CONST_STR_LEN("GET / HTTP/1.3\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 0, + "absolute URI", + CONST_STR_LEN("GET http://www.example.org/ HTTP/1.0\r\n" + "\r\n")); + assert(buffer_is_equal_string(con->request.http_host, + CONST_STR_LEN("www.example.org"))); + assert(buffer_is_equal_string(con->request.uri, + CONST_STR_LEN("/"))); + + run_http_request_parse(srv, con, __LINE__, 0, + "whitespace after key", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "ABC : foo\r\n" + "\r\n")); + ds = (data_string *) + array_get_element_klen(con->request.headers, CONST_STR_LEN("ABC")); + assert(ds && buffer_is_equal_string(ds->value, CONST_STR_LEN("foo"))); + + run_http_request_parse(srv, con, __LINE__, 400, + "whitespace within key", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "ABC a: foo\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 0, + "no whitespace", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "ABC:foo\r\n" + "\r\n")); + ds = (data_string *) + array_get_element_klen(con->request.headers, CONST_STR_LEN("ABC")); + assert(ds && buffer_is_equal_string(ds->value, CONST_STR_LEN("foo"))); + + run_http_request_parse(srv, con, __LINE__, 0, + "line-folding", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "ABC:foo\r\n" + " bc\r\n" + "\r\n")); + ds = (data_string *) + array_get_element_klen(con->request.headers, CONST_STR_LEN("ABC")); + assert(ds && buffer_is_equal_string(ds->value, CONST_STR_LEN("foo bc"))); + + run_http_request_parse(srv, con, __LINE__, 411, + "POST request, no Content-Length", + CONST_STR_LEN("POST / HTTP/1.0\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "Duplicate Host headers, Bug #25", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: www.example.org\r\n" + "Host: 123.example.org\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "Duplicate Content-Length headers", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Content-Length: 5\r\n" + "Content-Length: 4\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "Duplicate Content-Type headers", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Content-Type: 5\r\n" + "Content-Type: 4\r\n" + "\r\n")); + + /* (not actually testing Range here anymore; parsing deferred until use) */ + + run_http_request_parse(srv, con, __LINE__, 0, + "Duplicate Range headers (get appended)", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Range: bytes=5-6\r\n" + "Range: bytes=5-9\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 0, + "Duplicate Range headers with invalid range (a)", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Range: bytes=0\r\n" + "Range: bytes=5-9\r\n" + "\r\n")); + run_http_request_parse(srv, con, __LINE__, 0, + "Duplicate Range headers with invalid range (b)", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Range: bytes=5-9\r\n" + "Range: bytes=0\r\n" + "\r\n")); + run_http_request_parse(srv, con, __LINE__, 0, + "Duplicate Range headers with invalid range (c)", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Range: 0\r\n" + "Range: bytes=5-9\r\n" + "\r\n")); + run_http_request_parse(srv, con, __LINE__, 0, + "Duplicate Range headers with invalid range (d)", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Range: bytes=5-9\r\n" + "Range: 0\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 0, + "Duplicate If-None-Match headers", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "If-None-Match: 5\r\n" + "If-None-Match: 4\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "Duplicate If-Modified-Since headers", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "If-Modified-Since: 5\r\n" + "If-Modified-Since: 4\r\n" + "\r\n")); + + run_http_request_parse(srv, con, __LINE__, 400, + "GET with Content-Length", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Content-Length: 4\r\n" + "\r\n" + "1234")); + + run_http_request_parse(srv, con, __LINE__, 400, + "HEAD with Content-Length", + CONST_STR_LEN("HEAD / HTTP/1.0\r\n" + "Content-Length: 4\r\n" + "\r\n" + "1234")); + + run_http_request_parse(srv, con, __LINE__, 400, + "invalid chars in Header values (bug #1286)", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "If-Modified-Since: \0\r\n" + "\r\n")); + + /* (quick check that none of above tests were left in a state + * which resulted in subsequent tests returning 400 for other + * reasons) */ + run_http_request_parse(srv, con, __LINE__, 0, + "valid", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Host: www.example.org\r\n" + "\r\n")); +} + +int main (void) +{ + server srv; + connection con; + + memset(&srv, 0, sizeof(server)); + srv.errorlog_fd = -1; /* use 2 for STDERR_FILENO from unistd.h */ + srv.errorlog_mode = ERRORLOG_FD; + srv.errorlog_buf = buffer_init(); + srv.split_vals = array_init(); + + memset(&con, 0, sizeof(connection)); + con.proto = buffer_init(); + con.parse_request = buffer_init(); + con.request.request = buffer_init(); + con.request.request_line = buffer_init(); + con.request.orig_uri = buffer_init(); + con.request.uri = buffer_init(); + con.request.headers = array_init(); + con.conf.allow_http11 = 1; + con.conf.http_parseopts = HTTP_PARSEOPT_HEADER_STRICT + | HTTP_PARSEOPT_HOST_STRICT + | HTTP_PARSEOPT_HOST_NORMALIZE; + + test_request_http_request_parse(&srv, &con); + + buffer_free(con.proto); + buffer_free(con.parse_request); + buffer_free(con.request.request); + buffer_free(con.request.request_line); + buffer_free(con.request.orig_uri); + buffer_free(con.request.uri); + array_free(con.request.headers); + + array_free(srv.split_vals); + buffer_free(srv.errorlog_buf); + + return 0; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/vector.c b/data/lighttpd/lighttpd-1.4.53/src/vector.c new file mode 100644 index 000000000..29b707890 --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/vector.c @@ -0,0 +1,23 @@ +#include "first.h" + +#include "vector.h" + +#include <stdlib.h> +#include <string.h> + +void vector_free(void *data) { free(data); } + +void *vector_malloc(size_t sz) { return malloc(sz); } + +void *vector_realloc(void *data, size_t elem_size, size_t size, size_t used) { + const size_t total_size = elem_size * size; + const size_t used_size = elem_size * used; + force_assert(size <= SIZE_MAX / elem_size); + data = realloc(data, total_size); + force_assert(NULL != data); + + /* clear new memory */ + memset(((char*)data) + used_size, 0, total_size - used_size); + + return data; +} diff --git a/data/lighttpd/lighttpd-1.4.53/src/vector.h b/data/lighttpd/lighttpd-1.4.53/src/vector.h new file mode 100644 index 000000000..d3587936c --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/vector.h @@ -0,0 +1,68 @@ +#ifndef LI_VECTOR_H +#define LI_VECTOR_H +#include "first.h" + +#include "buffer.h" /* force_assert() */ + +static inline size_t vector_align_size(size_t s) { + size_t a = (s + 16) & ((size_t)~15); + return (a < s) ? s : a; +} + +void vector_free(void *data); +void *vector_malloc(size_t sz); +void *vector_realloc(void *data, size_t elem_size, size_t size, size_t used); + +#define DEFINE_TYPED_VECTOR(name, entry, release) \ + typedef struct vector_ ## name { \ + entry* data; \ + size_t used; \ + size_t size; \ + } vector_ ## name; \ + static inline void vector_ ## name ## _init(vector_ ## name *v) { \ + v->data = NULL; \ + v->used = v->size = 0; \ + } \ + static inline vector_ ## name *vector_ ## name ## _alloc() { \ + vector_ ## name *v = vector_malloc(sizeof(*v)); \ + force_assert(NULL != v); \ + vector_ ## name ## _init(v); \ + return v; \ + } \ + static inline void vector_ ## name ## _clear(vector_ ## name *v) { \ + size_t ndx; \ + vector_ ## name vcopy = *v; \ + vector_ ## name ## _init(v); \ + if (release) for (ndx = 0; ndx < vcopy.used; ++ndx) release(vcopy.data[ndx]); \ + vector_free(vcopy.data); \ + } \ + static inline void vector_ ## name ## _free(vector_ ## name *v) { \ + if (NULL != v) { \ + vector_ ## name ## _clear(v); \ + vector_free(v); \ + } \ + } \ + static inline void vector_ ## name ## _reserve(vector_ ## name *v, size_t p) { \ + force_assert(v->used < SIZE_MAX - p); \ + if (v->size < v->used + p) { \ + v->size = vector_align_size(v->used + p); \ + v->data = vector_realloc(v->data, sizeof(entry), v->size, v->used); \ + } \ + } \ + static inline void vector_ ## name ## _push(vector_ ## name *v, entry e) { \ + vector_ ## name ## _reserve(v, 1); \ + v->data[v->used++] = e; \ + } \ + static inline entry vector_ ## name ## _pop(vector_ ## name *v) { \ + force_assert(v->used > 0); \ + return v->data[--v->used]; \ + } \ + struct vector_ ## name /* expect trailing semicolon */ \ + /* end of DEFINE_TYPED_VECTOR */ + +#define DEFINE_TYPED_VECTOR_NO_RELEASE(name, entry) \ + DEFINE_TYPED_VECTOR(name, entry, ((void(*)(entry)) NULL)) \ + /* end of DEFINE_TYPED_VECTOR_NO_RELEASE */ + + +#endif |