diff options
Diffstat (limited to 'src')
181 files changed, 0 insertions, 60354 deletions
diff --git a/src/.cvsignore b/src/.cvsignore deleted file mode 100644 index 55061b0a..00000000 --- a/src/.cvsignore +++ /dev/null @@ -1,17 +0,0 @@ -Makefile.in -Makefile -spawn-fcgi -chunk -lemon -lighttpd -*.loT -*.exe -.deps -.libs -array -proc_open -regex -mod_ssi_exprparser.c -mod_ssi_exprparser.h -configparser.c -configparser.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 1323272d..00000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,678 +0,0 @@ -INCLUDE(CheckCSourceCompiles) -INCLUDE(CheckIncludeFiles) -INCLUDE(CheckFunctionExists) -INCLUDE(CheckVariableExists) -INCLUDE(CheckTypeSize) -INCLUDE(CheckLibraryExists) -INCLUDE(CMakeDetermineCCompiler) -INCLUDE(FindThreads) -INCLUDE(CPack) -INCLUDE(UsePkgConfig) - -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 the mod_sql_vhost [default: off]") -OPTION(WITH_POSTGRESQL "with postgress-support for the mod_sql_vhost [default: off]") -OPTION(WITH_OPENSSL "with openssl-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_BZIP "with bzip2-support for mod_compress [default: off]") -OPTION(WITH_ZLIB "with deflate-support for mod_compress [default: on]" ON) -OPTION(WITH_LDAP "with LDAP-support for the mod_auth [default: off]") -OPTION(WITH_LIBAIO "with libaio for the linux [default: off]") -OPTION(WITH_LIBFCGI "with libfcgi for fcgi-stat-accel [default: off]") -OPTION(WITH_LUA "with lua 5.1 for mod_magnet [default: off]") -OPTION(WITH_GLIB "with glib support for internal caches [default: on]" ON) - -IF(CMAKE_COMPILER_IS_GNUCC) - OPTION(BUILD_EXTRA_WARNINGS "extra warnings") - - IF(BUILD_EXTRA_WARNINGS) - SET(WARN_FLAGS "-g -O2 -g2 -Wall -Wmissing-declarations -Wdeclaration-after-statement -Wcast-align -Winline -Wsign-compare -Wnested-externs -Wpointer-arith -Wformat-security") - # -Wl,--as-needed - # -Werror -Wbad-function-cast -Wmissing-prototypes - ELSE(BUILD_EXTRA_WARNINGS) - SET(WARN_FLAGS "") - ENDIF(BUILD_EXTRA_WARNINGS) -ENDIF(CMAKE_COMPILER_IS_GNUCC) - -OPTION(BUILD_STATIC "build a static lighttpd with all modules added") -IF(BUILD_STATIC) - SET(LIGHTTPD_STATIC 1) -ELSE(BUILD_STATIC) - SET(CMAKE_SHARED_LIBRARY_PREFIX "") -ENDIF(BUILD_STATIC) - -IF(WITH_WEBDAV_PROPS) - SET(WITH_XML 1) - SET(WITH_SQLITE3 1) - SET(WITH_UUID 1) -ENDIF(WITH_WEBDAV_PROPS) - -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/syslimits.h HAVE_SYS_SYSLIMITS_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(time.h HAVE_TIME_H) -CHECK_INCLUDE_FILES(unistd.h HAVE_UNISTD_H) -CHECK_INCLUDE_FILES(pthread.h HAVE_PTHREAD_H) - - -## 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(${_package}CONFIG_EXECUTABLE) - MESSAGE(STATUS "found ${_package}: no") - ENDIF(${_package}CONFIG_EXECUTABLE) -ENDMACRO(XCONFIG _package _include_DIR _link_DIR _link_FLAGS _cflags) - -##INCLUDE_DIRECTORIES(${GTHREAD_INCDIR}) -ADD_DEFINITIONS(${GTHREAD_CFLAGS}) - -IF(WITH_XATTR) - CHECK_INCLUDE_FILES(attr/attributes.h HAVE_ATTR_ATTRIBUTES_H) -ENDIF(WITH_XATTR) - -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_LIBMYSQL) - ENDIF(HAVE_MYSQL_H) -ENDIF(WITH_MYSQL) - -IF(WITH_POSTGRESQL) - SET(CMAKE_REQUIRED_INCLUDES /usr/include/pgsql) - CHECK_INCLUDE_FILES(libpq-fe.h HAVE_LIBPQ_FE_H) - SET(CMAKE_REQUIRED_INCLUDES) - IF(HAVE_LIBPG_FE_H) - CHECK_LIBRARY_EXISTS(pq PQconnectdb "" HAVE_LIBPQ) - ENDIF(HAVE_LIBPG_FE_H) -ENDIF(WITH_POSTGRESQL) - -IF(WITH_OPENSSL) - CHECK_INCLUDE_FILES(openssl/ssl.h HAVE_OPENSSL_SSL_H) - IF(HAVE_OPENSSL_SSL_H) - CHECK_LIBRARY_EXISTS(crypto BIO_f_base64 "" HAVE_LIBCRYPTO) - IF(HAVE_LIBCRYPTO) - SET(OPENSSL_NO_KRB5 1) - CHECK_LIBRARY_EXISTS(ssl SSL_new "" HAVE_LIBSSL) - ENDIF(HAVE_LIBCRYPTO) - ENDIF(HAVE_OPENSSL_SSL_H) -ENDIF(WITH_OPENSSL) - -CHECK_INCLUDE_FILES(aio.h HAVE_AIO_H) -IF(WITH_BZIP) - CHECK_INCLUDE_FILES(bzlib.h HAVE_BZLIB_H) - CHECK_LIBRARY_EXISTS(bz2 BZ2_bzCompressInit "" HAVE_LIBBZ2) -ENDIF(WITH_BZIP) - -CHECK_INCLUDE_FILES(getopt.h HAVE_GETOPT_H) -CHECK_INCLUDE_FILES(inttypes.h HAVE_INTTYPES_H) -IF(WITH_LDAP) - CHECK_INCLUDE_FILES(ldap.h HAVE_LDAP_H) - CHECK_LIBRARY_EXISTS(ldap ldap_open "" HAVE_LIBLDAP) -ENDIF(WITH_LDAP) - -IF(WITH_LIBAIO) - CHECK_INCLUDE_FILES(libaio.h HAVE_LIBAIO_H) - CHECK_LIBRARY_EXISTS(aio io_getevents "" HAVE_LIBAIO) -ENDIF(WITH_LIBAIO) - -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(XML2_LDFLAGS OR XML2_CFLAGS) - CHECK_INCLUDE_FILES(libxml.h HAVE_LIBXML_H) - CHECK_LIBRARY_EXISTS(xml2 xmlParseChunk "" HAVE_LIBXML) - ENDIF(XML2_LDFLAGS OR XML2_CFLAGS) - - IF(NOT HAVE_LIBXML_H) - MESSAGE(FATAL_ERROR "libxml/tree.h couldn't be found") - ENDIF(NOT HAVE_LIBXML_H) - IF(NOT HAVE_LIBXML) - MESSAGE(FATAL_ERROR "libxml2 couldn't be found") - ENDIF(NOT HAVE_LIBXML) - -ENDIF(WITH_XML) - -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(NOT PCRE_CFLAGS STREQUAL "\n") - - CHECK_INCLUDE_FILES(pcre.h HAVE_PCRE_H) - - SET(CMAKE_REQUIRED_FLAGS ${PCRE_LDFLAGS}) - CHECK_LIBRARY_EXISTS(pcre pcre_exec "" HAVE_LIBPCRE) - SET(CMAKE_REQUIRED_FLAGS) - - ELSE(PCRE_LDFLAGS OR PCRE_CFLAGS) - IF(NOT WIN32) - CHECK_INCLUDE_FILES(pcre.h HAVE_PCRE_H) - CHECK_LIBRARY_EXISTS(pcre pcre_exec "" HAVE_LIBPCRE) - SET(PCRE_LIBRARY pcre) - ELSE(NOT WIN32) - 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(PCRE_INCLUDE_DIR AND PCRE_LIBRARY) - ENDIF(NOT WIN32) - ENDIF(PCRE_LDFLAGS OR PCRE_CFLAGS) - - IF(NOT HAVE_PCRE_H) - MESSAGE(FATAL_ERROR "pcre.h couldn't be found") - ENDIF(NOT HAVE_PCRE_H) - IF(NOT HAVE_LIBPCRE) - MESSAGE(FATAL_ERROR "libpcre couldn't be found") - ENDIF(NOT HAVE_LIBPCRE) - -ENDIF(WITH_PCRE) - -CHECK_INCLUDE_FILES(poll.h HAVE_POLL_H) -CHECK_INCLUDE_FILES(pwd.h HAVE_PWD_H) - -OPTION(WITH_SQLITE3 "with property-support [sqlite3] for mod_webdav [default: off]") -IF(WITH_SQLITE3) - CHECK_INCLUDE_FILES(sqlite3.h HAVE_SQLITE3_H) - CHECK_LIBRARY_EXISTS(sqlite3 sqlite3_reset "" HAVE_SQLITE3) -ENDIF(WITH_SQLITE3) - -IF(WITH_GLIB) - PKGCONFIG(gthread-2.0 GTHREAD_INCDIR GTHREAD_LIBDIR GTHREAD_LDFLAGS GTHREAD_CFLAGS) - MESSAGE(STATUS "found gthread-2.0 at: INCDIR: ${GTHREAD_INCDIR} LIBDIR: ${GTHREAD_LIBDIR} LDFLAGS: ${GTHREAD_LDFLAGS} CFLAGS: ${GTHREAD_CFLAGS}") - - SET(GLIB_INC_DIRS ${GTHREAD_INCDIR}/glib-2.0/ ${GTHREAD_LIBDIR}/glib-2.0/include/) - INCLUDE_DIRECTORIES(${GLIB_INC_DIRS}) - - SET(CMAKE_REQUIRED_INCLUDES ${GLIB_INC_DIRS}) - CHECK_INCLUDE_FILES(glib.h HAVE_GLIB_H) - SET(CMAKE_REQUIRED_INCLUDES) -ENDIF(WITH_GLIB) - -IF(WITH_LIBFCGI) - CHECK_INCLUDE_FILES(fastcgi.h HAVE_FASTCGI_H) - CHECK_INCLUDE_FILES(fastcgi/fastcgi.h HAVE_FASTCGI_FASTCGI_H) - IF(HAVE_FASTCGI_H OR HAVE_FASTCGI_FASTCGI_H) - CHECK_LIBRARY_EXISTS(fcgi FCGI_Accept "" HAVE_LIBFCGI) - ENDIF(HAVE_FASTCGI_H OR HAVE_FASTCGI_FASTCGI_H) -ENDIF(WITH_LIBFCGI) - -CHECK_INCLUDE_FILES(stddef.h HAVE_STDDEF_H) -CHECK_INCLUDE_FILES(stdint.h HAVE_STDINT_H) -CHECK_INCLUDE_FILES(syslog.h HAVE_SYSLOG_H) -IF(WITH_UUID) - CHECK_INCLUDE_FILES(uuid/uuid.h HAVE_UUID_H) - CHECK_LIBRARY_EXISTS(uuid uuid_generate "" NEED_LIBUUID) - IF(NOT NEED_LIBUUID) - CHECK_FUNCTION_EXISTS(uuid_generate HAVE_LIBUUID) - ELSE(NOT NEED_LIBUUID) - SET(HAVE_LIBUUID 1) - ENDIF(NOT NEED_LIBUUID) -ENDIF(WITH_UUID) - -CHECK_INCLUDE_FILES(sys/inotify.h HAVE_SYS_INOTIFY_H) -IF(HAVE_SYS_INOTIFY_H) - CHECK_FUNCTION_EXISTS(inotify_init HAVE_INOTIFY_INIT) -ENDIF(HAVE_SYS_INOTIFY_H) - -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(NOT WIN32) - 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(ZLIB_INCLUDE_DIR AND ZLIB_LIBRARY) - ENDIF(NOT WIN32) -ENDIF(WITH_ZLIB) - -IF(WITH_LUA) - PKGCONFIG(lua LUA_INCDIR LUA_LIBDIR LUA_LDFLAGS LUA_CFLAGS) - IF(NOT LUA_LDFLAGS) - PKGCONFIG(lua5.1 LUA_INCDIR LUA_LIBDIR LUA_LDFLAGS LUA_CFLAGS) - ENDIF(NOT LUA_LDFLAGS) - IF(NOT LUA_LDFLAGS) - PKGCONFIG(lua-5.1 LUA_INCDIR LUA_LIBDIR LUA_LDFLAGS LUA_CFLAGS) - ENDIF(NOT LUA_LDFLAGS) - MESSAGE(STATUS "found lua at: INCDIR: ${LUA_INCDIR} LIBDIR: ${LUA_LIBDIR} LDFLAGS: ${LUA_LDFLAGS} CFLAGS: ${LUA_CFLAGS}") - IF(LUA_LDFLAGS) - SET(HAVE_LIBLUA 1 "Have liblua") - INCLUDE_DIRECTORIES(${LUA_INCDIR}) - - SET(CMAKE_REQUIRED_INCLUDES ${LUA_INCDIR}) - SET(CMAKE_REQUIRED_FLAGS ${LUA_CFLAGS}) - CHECK_INCLUDE_FILES(lua.h HAVE_LUA_H) - SET(CMAKE_REQUIRED_INCLUDES) - SET(CMAKE_REQUIRED_FLAGS) - ELSE(LUA_LDFLAGS) - SET(HAVE_LIBLUA "" "Have liblua") - SET(HAVE_LUA_H "" "Have liblua header") - ENDIF(LUA_LDFLAGS) -ENDIF(WITH_LUA) - -CHECK_INCLUDE_FILES(crypt.h HAVE_CRYPT_H) -IF(NOT BUILD_STATIC) - CHECK_INCLUDE_FILES(dlfcn.h HAVE_DLFCN_H) -ENDIF(NOT BUILD_STATIC) - -SET(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h) -CHECK_TYPE_SIZE(socklen_t HAVE_SOCKLEN_T) -SET(CMAKE_EXTRA_INCLUDE_FILES) - -CHECK_TYPE_SIZE(long SIZEOF_LONG) -CHECK_TYPE_SIZE(off_t SIZEOF_OFF_T) - -CHECK_FUNCTION_EXISTS(chroot HAVE_CHROOT) -CHECK_FUNCTION_EXISTS(crypt HAVE_CRYPT) -CHECK_FUNCTION_EXISTS(epoll_ctl HAVE_EPOLL_CTL) -CHECK_FUNCTION_EXISTS(fork HAVE_FORK) -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(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(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(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(strptime HAVE_STRPTIME) -CHECK_FUNCTION_EXISTS(strtoll HAVE_STRTOLL) -CHECK_FUNCTION_EXISTS(syslog HAVE_SYSLOG) -CHECK_FUNCTION_EXISTS(writev HAVE_WRITEV) -CHECK_FUNCTION_EXISTS(inet_aton HAVE_INET_ATON) -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_FUNCTION_EXISTS(issetugid HAVE_ISSETUGID) - -IF(NOT HAVE_CRYPT) - ## check if we need libcrypt for crypt() - CHECK_LIBRARY_EXISTS(crypt crypt "" HAVE_LIBCRYPT) -ENDIF(NOT HAVE_CRYPT) - -IF(HAVE_DLFCN_H) - CHECK_LIBRARY_EXISTS(dl dlopen "" HAVE_LIBDL) -ENDIF(HAVE_DLFCN_H) - -ADD_DEFINITIONS( - -DLIGHTTPD_VERSION_ID=10500 - -DPACKAGE_NAME="\\"${CMAKE_PROJECT_NAME}\\"" - -DPACKAGE_VERSION="\\"${CPACK_PACKAGE_VERSION}\\"" - ) - -IF(NOT SBINDIR) - SET(SBINDIR "sbin") -ENDIF(NOT SBINDIR) - -SET(LIGHTTPD_MODULES_DIR "lib${LIB_SUFFIX}/lighttpd") -IF(NOT WIN32) -ADD_DEFINITIONS( - -DLIBRARY_DIR="\\"${CMAKE_INSTALL_PREFIX}/${LIGHTTPD_MODULES_DIR}\\"" -) -ELSE(NOT WIN32) -## We use relative path in windows -ADD_DEFINITIONS( - -DLIBRARY_DIR="\\"lib\\"" -) -ENDIF(NOT WIN32) - -## 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 - buffer.c log.c - keyvalue.c chunk.c - stream.c fdevent.c - stat_cache.c plugin.c joblist.c etag.c array.c - data_string.c data_count.c data_array.c - data_integer.c md5.c - fdevent_select.c fdevent_linux_rtsig.c - fdevent_poll.c fdevent_linux_sysepoll.c - fdevent_solaris_devpoll.c fdevent_freebsd_kqueue.c - data_config.c bitset.c - inet_ntop_cache.c crc32.c - connections-glue.c iosocket.c - configfile-glue.c - http-header-glue.c status_counter.c - network_writev.c - network_write.c - network_linux_sendfile.c - network_freebsd_sendfile.c - network_win32_send.c - network_solaris_sendfilev.c - network_openssl.c - network_linux_aio.c - network_posix_aio.c - network_gthread_aio.c - network_gthread_sendfile.c - network_gthread_freebsd_sendfile.c - http_resp.c - http_resp_parser.c - http_req.c - http_req_parser.c - http_req_range.c - http_req_range_parser.c - sys-files.c - sys-socket.c - filter.c - timing.c -) - -IF(WIN32) - MESSAGE(STATUS "Adding local getopt implementation.") - SET(COMMON_SRC ${COMMON_SRC} xgetopt.c) -ENDIF(WIN32) - -ADD_EXECUTABLE(lemon lemon.c) - -## Build parsers by using lemon... -LEMON_PARSER(configparser.y) -LEMON_PARSER(http_req_parser.y) -LEMON_PARSER(http_req_range_parser.y) -LEMON_PARSER(http_resp_parser.y) -LEMON_PARSER(mod_ssi_exprparser.y) - -SET(L_INSTALL_TARGETS) - -IF(HAVE_LIBFCGI) - ADD_EXECUTABLE(fcgi-stat-accel fcgi-stat-accel.c) - TARGET_LINK_LIBRARIES(fcgi-stat-accel fcgi) - SET_TARGET_PROPERTIES(fcgi-stat-accel PROPERTIES LINK_FLAGS "-pthread") - SET(L_INSTALL_TARGETS ${L_INSTALL_TARGETS} fcgi-stat-accel) -ENDIF(HAVE_LIBFCGI) - -ADD_EXECUTABLE(lighttpd - server.c - network.c - configfile.c - configparser.c - connections.c - proc_open.c - request.c - response.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_alias mod_alias.c) -ADD_AND_INSTALL_LIBRARY(mod_dirlisting mod_dirlisting.c) -ADD_AND_INSTALL_LIBRARY(mod_staticfile mod_staticfile.c) - -ADD_AND_INSTALL_LIBRARY(mod_indexfile mod_indexfile.c) -ADD_AND_INSTALL_LIBRARY(mod_setenv mod_setenv.c) -ADD_AND_INSTALL_LIBRARY(mod_rrdtool mod_rrdtool.c) -ADD_AND_INSTALL_LIBRARY(mod_usertrack mod_usertrack.c) -ADD_AND_INSTALL_LIBRARY(mod_proxy_core "mod_proxy_core.c;mod_proxy_core_pool.c;mod_proxy_core_backend.c;mod_proxy_core_address.c;mod_proxy_core_backlog.c;mod_proxy_core_protocol.c;mod_proxy_core_rewrites.c") -ADD_AND_INSTALL_LIBRARY(mod_proxy_backend_http mod_proxy_backend_http.c) -ADD_AND_INSTALL_LIBRARY(mod_proxy_backend_fastcgi mod_proxy_backend_fastcgi.c) -ADD_AND_INSTALL_LIBRARY(mod_proxy_backend_scgi mod_proxy_backend_scgi.c) -ADD_AND_INSTALL_LIBRARY(mod_proxy_backend_ajp13 mod_proxy_backend_ajp13.c) -ADD_AND_INSTALL_LIBRARY(mod_userdir mod_userdir.c) -ADD_AND_INSTALL_LIBRARY(mod_secdownload mod_secure_download.c) -ADD_AND_INSTALL_LIBRARY(mod_accesslog mod_accesslog.c) -ADD_AND_INSTALL_LIBRARY(mod_simple_vhost mod_simple_vhost.c) -ADD_AND_INSTALL_LIBRARY(mod_evhost mod_evhost.c) -ADD_AND_INSTALL_LIBRARY(mod_expire mod_expire.c) -ADD_AND_INSTALL_LIBRARY(mod_status mod_status.c) -ADD_AND_INSTALL_LIBRARY(mod_compress mod_compress.c) -ADD_AND_INSTALL_LIBRARY(mod_redirect mod_redirect.c) -ADD_AND_INSTALL_LIBRARY(mod_rewrite mod_rewrite.c) -ADD_AND_INSTALL_LIBRARY(mod_auth "mod_auth.c;http_auth_digest.c;http_auth.c") -ADD_AND_INSTALL_LIBRARY(mod_sql_vhost_core mod_sql_vhost_core.c) -ADD_AND_INSTALL_LIBRARY(mod_mysql_vhost mod_mysql_vhost.c) -ADD_AND_INSTALL_LIBRARY(mod_postgresql_vhost mod_postgresql_vhost.c) -ADD_AND_INSTALL_LIBRARY(mod_trigger_b4_dl mod_trigger_b4_dl.c) -ADD_AND_INSTALL_LIBRARY(mod_uploadprogress mod_uploadprogress.c) -ADD_AND_INSTALL_LIBRARY(mod_evasive mod_evasive.c) -ADD_AND_INSTALL_LIBRARY(mod_ssi "mod_ssi_exprparser.c;mod_ssi_expr.c;mod_ssi.c") -ADD_AND_INSTALL_LIBRARY(mod_flv_streaming mod_flv_streaming.c) -ADD_AND_INSTALL_LIBRARY(mod_chunked mod_chunked.c) -ADD_AND_INSTALL_LIBRARY(mod_magnet "mod_magnet.c;mod_magnet_cache.c") -ADD_AND_INSTALL_LIBRARY(mod_deflate mod_deflate.c) -ADD_AND_INSTALL_LIBRARY(mod_webdav mod_webdav.c) - -IF(NOT WIN32) -ADD_AND_INSTALL_LIBRARY(mod_cgi mod_cgi.c) -ENDIF(NOT WIN32) - -IF(HAVE_PCRE_H) - TARGET_LINK_LIBRARIES(lighttpd ${PCRE_LIBRARY}) - TARGET_LINK_LIBRARIES(mod_rewrite ${PCRE_LIBRARY}) - TARGET_LINK_LIBRARIES(mod_dirlisting ${PCRE_LIBRARY}) - TARGET_LINK_LIBRARIES(mod_redirect ${PCRE_LIBRARY}) - TARGET_LINK_LIBRARIES(mod_ssi ${PCRE_LIBRARY}) - TARGET_LINK_LIBRARIES(mod_trigger_b4_dl ${PCRE_LIBRARY}) - TARGET_LINK_LIBRARIES(mod_proxy_core ${PCRE_LIBRARY}) -ENDIF(HAVE_PCRE_H) - -ADD_TARGET_PROPERTIES(mod_magnet LINK_FLAGS "${LUA_LDFLAGS}") -ADD_TARGET_PROPERTIES(mod_magnet COMPILE_FLAGS "${LUA_CFLAGS}") - -IF(HAVE_MYSQL_H AND HAVE_LIBMYSQL) - TARGET_LINK_LIBRARIES(mod_mysql_vhost mysqlclient) - INCLUDE_DIRECTORIES(/usr/include/mysql) -ENDIF(HAVE_MYSQL_H AND HAVE_LIBMYSQL) - -IF(HAVE_LIBPQ_FE_H AND HAVE_LIBPQ) - TARGET_LINK_LIBRARIES(mod_postgresql_vhost pq) - INCLUDE_DIRECTORIES(/usr/include/pgsql) -ENDIF(HAVE_LIBPQ_FE_H AND HAVE_LIBPQ) - -SET(L_MOD_WEBDAV) -IF(HAVE_SQLITE3_H) - SET(L_MOD_WEBDAV ${L_MOD_WEBDAV} sqlite3) -ENDIF(HAVE_SQLITE3_H) -IF(HAVE_LIBXML_H) - SET_TARGET_PROPERTIES(mod_webdav PROPERTIES LINK_FLAGS ${XML2_LDFLAGS}) -ENDIF(HAVE_LIBXML_H) -IF(HAVE_UUID_H) - IF(NEED_LIBUUID) - SET(L_MOD_WEBDAV ${L_MOD_WEBDAV} uuid) - ENDIF(NEED_LIBUUID) -ENDIF(HAVE_UUID_H) - -TARGET_LINK_LIBRARIES(mod_webdav ${L_MOD_WEBDAV}) - -SET(L_MOD_AUTH) -IF(HAVE_LIBCRYPT) - SET(L_MOD_AUTH ${L_MOD_AUTH} crypt) -ENDIF(HAVE_LIBCRYPT) - -IF(HAVE_LDAP_H) - SET(L_MOD_AUTH ${L_MOD_AUTH} ldap lber) -ENDIF(HAVE_LDAP_H) -TARGET_LINK_LIBRARIES(mod_auth ${L_MOD_AUTH}) - -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(HAVE_BZLIB_H) - TARGET_LINK_LIBRARIES(mod_compress ${ZLIB_LIBRARY}) - TARGET_LINK_LIBRARIES(mod_deflate ${ZLIB_LIBRARY}) - ENDIF(HAVE_BZLIB_H) -ENDIF(HAVE_ZLIB_H) - -IF(CMAKE_COMPILER_IS_GNUCC) - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -g -Wshadow -W -pedantic ${WARN_FLAGS}") - 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") - ADD_DEFINITIONS(-D_GNU_SOURCE) -ENDIF(CMAKE_COMPILER_IS_GNUCC) - -ADD_TARGET_PROPERTIES(lighttpd LINK_FLAGS "-export-dynamic ${GTHREAD_LDFLAGS} ${XML2_LDFLAGS}") -IF(CMAKE_SYSTEM MATCHES "Linux") - ## on linux we need pthread and librt for posix-aio - ADD_TARGET_PROPERTIES(lighttpd LINK_FLAGS "-lrt") -ENDIF(CMAKE_SYSTEM MATCHES "Linux") - -ADD_TARGET_PROPERTIES(lighttpd COMPILE_FLAGS "${GTHREAD_CFLAGS}") -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_core ws2_32) - TARGET_LINK_LIBRARIES(mod_proxy_backend_ajp13 ws2_32) - TARGET_LINK_LIBRARIES(mod_proxy_backend_fastcgi ws2_32) - TARGET_LINK_LIBRARIES(mod_proxy_backend_scgi ws2_32) - TARGET_LINK_LIBRARIES(mod_ssi ws2_32) - # required for mingw gcc/ld - IF(WITH_GLIB) - TARGET_LINK_LIBRARIES(lighttpd glib-2.0 gthread-2.0) - ENDIF(WITH_GLIB) - - IF(MINGW) - TARGET_LINK_LIBRARIES(lighttpd msvcr70) - ADD_TARGET_PROPERTIES(lighttpd LINK_FLAGS "-Wl,-subsystem,console") - ENDIF(MINGW) -ENDIF(WIN32) - -IF(NOT BUILD_STATIC) - IF(HAVE_LIBDL) - TARGET_LINK_LIBRARIES(lighttpd dl) - ENDIF(HAVE_LIBDL) -ENDIF(NOT BUILD_STATIC) - -IF(HAVE_LIBAIO_H) - TARGET_LINK_LIBRARIES(lighttpd aio) -ENDIF(HAVE_LIBAIO_H) - -IF(HAVE_LIBSSL AND HAVE_LIBCRYPTO) - TARGET_LINK_LIBRARIES(lighttpd ssl) - TARGET_LINK_LIBRARIES(lighttpd crypto) -ENDIF(HAVE_LIBSSL AND HAVE_LIBCRYPTO) - -IF(NOT WIN32) -INSTALL(TARGETS ${L_INSTALL_TARGETS} - RUNTIME DESTINATION ${SBINDIR} - LIBRARY DESTINATION ${LIGHTTPD_MODULES_DIR} - ARCHIVE DESTINATION ${LIGHTTPD_MODULES_DIR}/static) -ELSE(NOT WIN32) -## 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(NOT WIN32) diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index eb50e379..00000000 --- a/src/Makefile.am +++ /dev/null @@ -1,365 +0,0 @@ -AM_CFLAGS = $(GTHREAD_CFLAGS) - -noinst_PROGRAMS=proc_open lemon # simple-fcgi -if CHECK_WITH_FASTCGI -sbin_PROGRAMS=lighttpd fcgi-stat-accel -else -sbin_PROGRAMS=lighttpd -endif -LEMON=$(top_builddir)/src/lemon - -lemon_SOURCES=lemon.c - -#simple_fcgi_SOURCES=simple-fcgi.c -#simple_fcgi_LDADD=-lfcgi - -if CROSS_COMPILING -configparser.c configparser.h: -mod_ssi_exprparser.c mod_ssi_exprparser.h: -http_req_parser.c http_req_parser.h: -http_req_range_parser.c http_req_range_parser.h: -mod_ssi_exprparser.c mod_ssi_exprparser.h: -else -configparser.c configparser.h: $(srcdir)/configparser.y $(srcdir)/lempar.c | $(LEMON) - rm -f configparser.h - $(LEMON) -q $(srcdir)/configparser.y $(srcdir)/lempar.c - -http_resp_parser.c http_resp_parser.h: $(srcdir)/http_resp_parser.y $(srcdir)/lempar.c | $(LEMON) - rm -f http_resp_parser.h - $(LEMON) -q $(srcdir)/http_resp_parser.y $(srcdir)/lempar.c - -http_req_parser.c http_req_parser.h: $(srcdir)/http_req_parser.y $(srcdir)/lempar.c | $(LEMON) - rm -f http_req_parser.h - $(LEMON) -q $(srcdir)/http_req_parser.y $(srcdir)/lempar.c - -http_req_range_parser.c http_req_range_parser.h: $(srcdir)/http_req_range_parser.y $(srcdir)/lempar.c | $(LEMON) - rm -f http_req_range_parser.h - $(LEMON) -q $(srcdir)/http_req_range_parser.y $(srcdir)/lempar.c - -mod_ssi_exprparser.c mod_ssi_exprparser.h: $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c | $(LEMON) - rm -f mod_ssi_exprparser.h - $(LEMON) -q $(srcdir)/mod_ssi_exprparser.y $(srcdir)/lempar.c -endif - -BUILT_SOURCES = configparser.c configparser.h \ - http_resp_parser.c http_resp_parser.h \ - http_req_parser.c http_req_parser.h \ - http_req_range_parser.c http_req_range_parser.h \ - mod_ssi_exprparser.c mod_ssi_exprparser.h - -common_src=buffer.c log.c \ - keyvalue.c chunk.c filter.c \ - stream.c fdevent.c \ - stat_cache.c plugin.c joblist.c etag.c array.c \ - data_string.c data_count.c data_array.c \ - data_integer.c md5.c \ - fdevent_select.c fdevent_linux_rtsig.c \ - fdevent_poll.c fdevent_linux_sysepoll.c \ - fdevent_solaris_devpoll.c fdevent_freebsd_kqueue.c \ - data_config.c bitset.c \ - inet_ntop_cache.c crc32.c \ - connections-glue.c iosocket.c \ - configfile-glue.c status_counter.c \ - http-header-glue.c \ - network_write.c network_linux_sendfile.c \ - network_freebsd_sendfile.c network_writev.c \ - network_solaris_sendfilev.c network_openssl.c \ - network_linux_aio.c \ - network_posix_aio.c \ - network_gthread_aio.c network_gthread_sendfile.c \ - network_gthread_freebsd_sendfile.c \ - http_resp.c http_resp_parser.c \ - http_req.c http_req_parser.c \ - http_req_range.c http_req_range_parser.c timing.c - -src = server.c response.c connections.c network.c \ - configfile.c configparser.c request.c proc_open.c - -if CHECK_WITH_FASTCGI -fcgi_stat_accel_SOURCES=fcgi-stat-accel.c -fcgi_stat_accel_LDADD = -lfcgi -endif - -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 -lib_LTLIBRARIES += liblightcomp.la -liblightcomp_la_SOURCES=$(common_src) -liblightcomp_la_CFLAGS=$(AM_CFLAGS) -liblightcomp_la_LDFLAGS = -avoid-version -no-undefined -liblightcomp_la_LIBADD = $(PCRE_LIB) $(SSL_LIB) $(GTHREAD_LIBS) -common_libadd = liblightcomp.la -else -src += $(common_src) -common_libadd = -endif - -lib_LTLIBRARIES += mod_flv_streaming.la -mod_flv_streaming_la_SOURCES = mod_flv_streaming.c -mod_flv_streaming_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_flv_streaming_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_uploadprogress.la -mod_uploadprogress_la_SOURCES = mod_uploadprogress.c -mod_uploadprogress_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_uploadprogress_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_evasive.la -mod_evasive_la_SOURCES = mod_evasive.c -mod_evasive_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -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 = -module -export-dynamic -avoid-version -no-undefined -mod_webdav_la_LIBADD = $(common_libadd) $(XML_LIBS) $(SQLITE_LIBS) $(UUID_LIB) - -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 = -module -export-dynamic -avoid-version -no-undefined -mod_magnet_la_LIBADD = $(MEMCACHE_LIB) $(common_libadd) $(LUA_LIBS) -lm - -lib_LTLIBRARIES += mod_trigger_b4_dl.la -mod_trigger_b4_dl_la_SOURCES = mod_trigger_b4_dl.c -mod_trigger_b4_dl_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_trigger_b4_dl_la_LIBADD = $(GDBM_LIB) $(MEMCACHE_LIB) $(PCRE_LIB) $(common_libadd) - -lib_LTLIBRARIES += mod_mysql_vhost.la -mod_mysql_vhost_la_SOURCES = mod_mysql_vhost.c -mod_mysql_vhost_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_mysql_vhost_la_LIBADD = $(MYSQL_LIBS) $(common_libadd) -mod_mysql_vhost_la_CPPFLAGS = $(MYSQL_INCLUDE) - -lib_LTLIBRARIES += mod_postgresql_vhost.la -mod_postgresql_vhost_la_SOURCES = mod_postgresql_vhost.c -mod_postgresql_vhost_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_postgresql_vhost_la_LIBADD = $(POSTGRESQL_LIBS) $(common_libadd) -mod_postgresql_vhost_la_CPPFLAGS = $(POSTGRESQL_INCLUDE) - - -lib_LTLIBRARIES += mod_sql_vhost_core.la -mod_sql_vhost_core_la_SOURCES = mod_sql_vhost_core.c -mod_sql_vhost_core_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_sql_vhost_core_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_cgi.la -mod_cgi_la_SOURCES = mod_cgi.c -mod_cgi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_cgi_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_staticfile.la -mod_staticfile_la_SOURCES = mod_staticfile.c -mod_staticfile_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_staticfile_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_deflate.la -mod_deflate_la_SOURCES = mod_deflate.c -mod_deflate_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_deflate_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd) - -lib_LTLIBRARIES += mod_chunked.la -mod_chunked_la_SOURCES = mod_chunked.c -mod_chunked_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_chunked_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_dirlisting.la -mod_dirlisting_la_SOURCES = mod_dirlisting.c -mod_dirlisting_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_dirlisting_la_LIBADD = $(common_libadd) $(PCRE_LIB) - -lib_LTLIBRARIES += mod_indexfile.la -mod_indexfile_la_SOURCES = mod_indexfile.c -mod_indexfile_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_indexfile_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_setenv.la -mod_setenv_la_SOURCES = mod_setenv.c -mod_setenv_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_setenv_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_alias.la -mod_alias_la_SOURCES = mod_alias.c -mod_alias_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_alias_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_userdir.la -mod_userdir_la_SOURCES = mod_userdir.c -mod_userdir_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_userdir_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_rrdtool.la -mod_rrdtool_la_SOURCES = mod_rrdtool.c -mod_rrdtool_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_rrdtool_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_usertrack.la -mod_usertrack_la_SOURCES = mod_usertrack.c -mod_usertrack_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_usertrack_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_proxy_core.la -mod_proxy_core_la_SOURCES = mod_proxy_core.c mod_proxy_core_pool.c \ - mod_proxy_core_backend.c mod_proxy_core_address.c \ - mod_proxy_core_backlog.c mod_proxy_core_rewrites.c \ - mod_proxy_core_protocol.c -mod_proxy_core_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_proxy_core_la_LIBADD = $(common_libadd) $(PCRE_LIB) - -lib_LTLIBRARIES += mod_proxy_backend_http.la -mod_proxy_backend_http_la_SOURCES = mod_proxy_backend_http.c -mod_proxy_backend_http_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_proxy_backend_http_la_LIBADD = $(common_libadd) $(PCRE_LIB) - -lib_LTLIBRARIES += mod_proxy_backend_fastcgi.la -mod_proxy_backend_fastcgi_la_SOURCES = mod_proxy_backend_fastcgi.c -mod_proxy_backend_fastcgi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_proxy_backend_fastcgi_la_LIBADD = $(common_libadd) $(PCRE_LIB) - -lib_LTLIBRARIES += mod_proxy_backend_scgi.la -mod_proxy_backend_scgi_la_SOURCES = mod_proxy_backend_scgi.c -mod_proxy_backend_scgi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_proxy_backend_scgi_la_LIBADD = $(common_libadd) $(PCRE_LIB) - -lib_LTLIBRARIES += mod_proxy_backend_ajp13.la -mod_proxy_backend_ajp13_la_SOURCES = mod_proxy_backend_ajp13.c -mod_proxy_backend_ajp13_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_proxy_backend_ajp13_la_LIBADD = $(common_libadd) $(PCRE_LIB) - - -lib_LTLIBRARIES += mod_ssi.la -mod_ssi_la_SOURCES = mod_ssi_exprparser.c mod_ssi_expr.c mod_ssi.c -mod_ssi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_ssi_la_LIBADD = $(common_libadd) $(PCRE_LIB) - -lib_LTLIBRARIES += mod_secdownload.la -mod_secdownload_la_SOURCES = mod_secure_download.c -mod_secdownload_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_secdownload_la_LIBADD = $(common_libadd) - -#lib_LTLIBRARIES += mod_httptls.la -#mod_httptls_la_SOURCES = mod_httptls.c -#mod_httptls_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -#mod_httptls_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_expire.la -mod_expire_la_SOURCES = mod_expire.c -mod_expire_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_expire_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_evhost.la -mod_evhost_la_SOURCES = mod_evhost.c -mod_evhost_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -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 = -module -export-dynamic -avoid-version -no-undefined -mod_simple_vhost_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_access.la -mod_access_la_SOURCES = mod_access.c -mod_access_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_access_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_compress.la -mod_compress_la_SOURCES = mod_compress.c -mod_compress_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_compress_la_LIBADD = $(Z_LIB) $(BZ_LIB) $(common_libadd) - -lib_LTLIBRARIES += mod_auth.la -mod_auth_la_SOURCES = mod_auth.c http_auth_digest.c http_auth.c -mod_auth_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_auth_la_LIBADD = $(CRYPT_LIB) $(LDAP_LIB) $(LBER_LIB) $(common_libadd) - -lib_LTLIBRARIES += mod_rewrite.la -mod_rewrite_la_SOURCES = mod_rewrite.c -mod_rewrite_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_rewrite_la_LIBADD = $(PCRE_LIB) $(common_libadd) - -lib_LTLIBRARIES += mod_redirect.la -mod_redirect_la_SOURCES = mod_redirect.c -mod_redirect_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_redirect_la_LIBADD = $(PCRE_LIB) $(common_libadd) - -lib_LTLIBRARIES += mod_status.la -mod_status_la_SOURCES = mod_status.c -mod_status_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_status_la_LIBADD = $(common_libadd) - -lib_LTLIBRARIES += mod_accesslog.la -mod_accesslog_la_SOURCES = mod_accesslog.c -mod_accesslog_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined -mod_accesslog_la_LIBADD = $(common_libadd) - - -hdr = server.h buffer.h network.h log.h keyvalue.h \ - response.h request.h fastcgi.h chunk.h filter.h \ - settings.h http_auth_digest.h \ - md5.h http_auth.h stream.h \ - fdevent.h connections.h base.h stat_cache.h \ - plugin.h mod_auth.h \ - etag.h joblist.h array.h crc32.h \ - network_backends.h configfile.h bitset.h \ - mod_ssi.h mod_ssi_expr.h inet_ntop_cache.h \ - configparser.h mod_ssi_exprparser.h \ - sys-mmap.h sys-socket.h \ - proc_open.h mod_sql_vhost_core.h \ - sys-files.h sys-process.h sys-strings.h \ - iosocket.h array-static.h \ - mod_proxy_core_address.h \ - mod_proxy_core_backend.h \ - mod_proxy_core_backlog.h \ - mod_proxy_core.h \ - mod_proxy_core_pool.h \ - mod_proxy_core_rewrites.h \ - status_counter.h \ - http_req.h \ - http_req_parser.h \ - http_req_range.h \ - http_req_range_parser.h \ - http_resp.h \ - http_resp_parser.h \ - http_parser.h \ - ajp13.h \ - mod_proxy_core_protocol.h \ - mod_magnet_cache.h \ - timing.h - -DEFS= @DEFS@ -DLIBRARY_DIR="\"$(libdir)\"" - -lighttpd_SOURCES = $(src) -lighttpd_LDADD = $(PCRE_LIB) $(DL_LIB) $(SENDFILE_LIB) $(ATTR_LIB) $(common_libadd) $(SSL_LIB) $(AIO_LIB) $(POSIX_AIO_LIB) $(GTHREAD_LIBS) -lighttpd_LDFLAGS = -export-dynamic - -proc_open_SOURCES = proc_open.c buffer.c -proc_open_CPPFLAGS= -DDEBUG_PROC_OPEN - -#gen_license_SOURCES = license.c md5.c buffer.c gen_license.c - -#ssl_SOURCES = ssl.c - - -#adserver_SOURCES = buffer.c iframe.c -#adserver_LDADD = -lfcgi -lmysqlclient - -#error_test_SOURCES = error_test.c - -#evalo_SOURCES = buffer.c eval.c -#bench_SOURCES = buffer.c bench.c -#ajp_SOURCES = ajp.c - -noinst_HEADERS = $(hdr) -EXTRA_DIST = mod_skeleton.c \ - configparser.y \ - mod_ssi_exprparser.y \ - lempar.c \ - http_resp_parser.y \ - http_req_parser.y \ - http_req_range_parser.y - -SUBDIRS=valgrind diff --git a/src/XGetopt.h b/src/XGetopt.h deleted file mode 100644 index 7e75f26e..00000000 --- a/src/XGetopt.h +++ /dev/null @@ -1,23 +0,0 @@ -// XGetopt.h Version 1.2 -// -// Author: Hans Dietrich -// hdietrich2@hotmail.com -// -// This software is released into the public domain. -// You are free to use it in any way you like. -// -// This software is provided "as is" with no expressed -// or implied warranty. I accept no liability for any -// damage or loss of business that this software may cause. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef XGETOPT_H -#define XGETOPT_H - -extern int optind, opterr; -extern TCHAR *optarg; - -int getopt(int argc, TCHAR *argv[], TCHAR *optstring); - -#endif //XGETOPT_H diff --git a/src/ajp13.h b/src/ajp13.h deleted file mode 100644 index 8ad6a577..00000000 --- a/src/ajp13.h +++ /dev/null @@ -1,136 +0,0 @@ - -#ifndef _AJP13_H -#define _AJP13_H -/** - * AJP13 protocol definitions. - */ -#define AJP13_MAX_PACKET_SIZE (8 * 1024) -#define AJP13_MAX_BODY_PACKET_SIZE (AJP13_MAX_PACKET_SIZE - AJP13_HEADER_LEN - 2) - -/** - * two byte maigc codes - */ -#define AJP13_SERVER_MAGIC 0x1234 -#define AJP13_CONTAINER_MAGIC 0x4142 - -/** - * packet types. - */ -typedef enum { - AJP13_TYPE_DATA = 0, /* 0 */ - AJP13_TYPE_UNKNOWN, /* 1 */ - AJP13_TYPE_FORWARD_REQUEST, /* 2 */ - AJP13_TYPE_SEND_BODY_CHUNK, /* 3 */ - AJP13_TYPE_SEND_HEADERS, /* 4 */ - AJP13_TYPE_END_RESPONSE, /* 5 */ - AJP13_TYPE_GET_BODY_CHUNK, /* 6 */ - AJP13_TYPE_SHUTDOWN, /* 7 */ - AJP13_TYPE_PING_REQUEST, /* 8 */ - AJP13_TYPE_CPONG_REPLY, /* 9 */ - AJP13_TYPE_CPING_REQUEST, /* 10 */ -} ajp13_type_t; - -/* - * http method codes - */ -typedef enum { - AJP13_METHOD_UNKNOWN = 0, /* 0 */ - AJP13_METHOD_OPTIONS, /* 1 */ - AJP13_METHOD_GET, /* 2 */ - AJP13_METHOD_HEAD, /* 3 */ - AJP13_METHOD_POST, /* 4 */ - AJP13_METHOD_PUT, /* 5 */ - AJP13_METHOD_DELETE, /* 6 */ - AJP13_METHOD_TRACE, /* 7 */ - AJP13_METHOD_PROPFIND, /* 8 */ - AJP13_METHOD_PROPPATCH, /* 9 */ - AJP13_METHOD_MKCOL, /* 10 */ - AJP13_METHOD_COPY, /* 11 */ - AJP13_METHOD_MOVE, /* 12 */ - AJP13_METHOD_LOCK, /* 13 */ - AJP13_METHOD_UNLOCK, /* 14 */ - AJP13_METHOD_ACL, /* 15 */ - AJP13_METHOD_REPORT, /* 16 */ - AJP13_METHOD_VERSION_CONTROL, /* 17 */ - AJP13_METHOD_CHECKIN, /* 18 */ - AJP13_METHOD_CHECKOUT, /* 19 */ - AJP13_METHOD_UNCHECKOUT, /* 20 */ - AJP13_METHOD_SEARCH, /* 21 */ - AJP13_METHOD_MKWORKSPACE, /* 22 */ - AJP13_METHOD_UPDATE, /* 23 */ - AJP13_METHOD_LABEL, /* 24 */ - AJP13_METHOD_MERGE, /* 25 */ - AJP13_METHOD_BASELINE_CONTROL,/* 26 */ - AJP13_METHOD_MKACTIVITY /* 27 */ -} ajp13_method_t; - -/* - * request headers - */ -typedef enum { - AJP13_REQ_ACCEPT = 0x01, /* 0x01 */ - AJP13_REQ_ACCEPT_CHARSET, /* 0x02 */ - AJP13_REQ_ACCEPT_ENCODING, /* 0x03 */ - AJP13_REQ_ACCEPT_LANGUAGE, /* 0x04 */ - AJP13_REQ_AUTHORIZATION, /* 0x05 */ - AJP13_REQ_CONNECTION, /* 0x06 */ - AJP13_REQ_CONTENT_TYPE, /* 0x07 */ - AJP13_REQ_CONTENT_LENGTH, /* 0x08 */ - AJP13_REQ_COOKIE, /* 0x09 */ - AJP13_REQ_COOKIE2, /* 0x0A */ - AJP13_REQ_HOST, /* 0x0B */ - AJP13_REQ_PRAGMA, /* 0x0C */ - AJP13_REQ_REFERER, /* 0x0D */ - AJP13_REQ_USER_AGENT /* 0x0E */ -} ajp13_request_header_t; - -/* - * request attributes - */ -typedef enum { - AJP13_ATTRIBUTE_CONTEXT = 1, /* 1 */ - AJP13_ATTRIBUTE_SERVLET_PATH, /* 2 */ - AJP13_ATTRIBUTE_REMOTE_USER, /* 3 */ - AJP13_ATTRIBUTE_AUTH_TYPE, /* 4 */ - AJP13_ATTRIBUTE_QUERY_STRING, /* 5 */ - AJP13_ATTRIBUTE_JVM_ROUTE, /* 6 */ - AJP13_ATTRIBUTE_SSL_CERT, /* 7 */ - AJP13_ATTRIBUTE_SSL_CIPHER, /* 8 */ - AJP13_ATTRIBUTE_SSL_SESSION, /* 9 */ - AJP13_ATTRIBUTE_REQ_ATTRIBUTE, /* 10 */ - AJP13_ATTRIBUTE_SSL_KEY_SIZE, /* 11 */ - AJP13_ATTRIBUTE_SECRET, /* 12 */ - AJP13_ATTRIBUTE_STORED_METHOD, /* 13 */ - AJP13_ATTRIBUTE_ARE_DONE = 0xFF, /* 0xFF */ -} ajp13_attributes_t; - -/* - * response headers - */ -typedef enum { - AJP13_RESP_CONTENT_TYPE = 0x01, /* 0x01 */ - AJP13_RESP_CONTENT_LANGUAGE, /* 0x02 */ - AJP13_RESP_CONTENT_LENGTH, /* 0x03 */ - AJP13_RESP_DATE, /* 0x04 */ - AJP13_RESP_LAST_MODIFIED, /* 0x05 */ - AJP13_RESP_LOCATION, /* 0x06 */ - AJP13_RESP_SET_COOKIE, /* 0x07 */ - AJP13_RESP_SET_COOKIE2, /* 0x08 */ - AJP13_RESP_SERVLET_ENGINE, /* 0x09 */ - AJP13_RESP_STATUS, /* 0x0A */ - AJP13_RESP_WWW_AUTHENTICATE, /* 0x0B */ -} ajp13_response_header_t; - -/* - * - */ -#define AJP13_COMMON_HEADER_CODE 0xA000 - -/* - * fixed header lengths. - */ -#define AJP13_HEADER_LEN 4 -#define AJP13_FULL_HEADER_LEN 5 - -#endif /* _AJP13_H */ - diff --git a/src/array-static.h b/src/array-static.h deleted file mode 100644 index fdeebf63..00000000 --- a/src/array-static.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _ARRAY_STATIC_H_ -#define _ARRAY_STATIC_H_ - -/* define a generic array of <type> - * */ - -#define ARRAY_STATIC_DEF(name, type, extra) \ -typedef struct { \ - type **ptr; \ - size_t used; \ - size_t size; \ - extra\ -} name - -/* all append operations need a 'resize' for the +1 */ - -#define ARRAY_STATIC_PREPARE_APPEND(a) \ - if (a->size == 0) { \ - a->size = 16; \ - a->ptr = malloc(a->size * sizeof(*(a->ptr))); \ - } else if (a->size == a->used) { \ - a->size += 16; \ - a->ptr = realloc(a->ptr, a->size * sizeof(*(a->ptr))); \ - } - -#define FOREACH(array, type, element, func) \ -do { size_t _i; for (_i = 0; _i < array->used; _i++) { type *element = array->ptr[_i]; func; } } while(0); - -#define STRUCT_INIT(type, var) \ - type *var;\ - var = calloc(1, sizeof(*var)) - -#define ARRAY_STATIC_FREE(array, type, element, func) \ - FOREACH(array, type, element, func); \ - if(array->ptr) free(array->ptr); \ - array->ptr = NULL; - -#endif diff --git a/src/array.c b/src/array.c deleted file mode 100644 index 6d740f1a..00000000 --- a/src/array.c +++ /dev/null @@ -1,398 +0,0 @@ -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <limits.h> - -#include <errno.h> -#include <assert.h> - -#include "array.h" -#include "buffer.h" - -array *array_init(void) { - array *a; - - a = calloc(1, sizeof(*a)); - assert(a); - - a->next_power_of_2 = 1; - - return a; -} - -array *array_init_array(array *src) { - size_t i; - array *a = array_init(); - - a->used = src->used; - a->size = src->size; - a->next_power_of_2 = src->next_power_of_2; - a->unique_ndx = src->unique_ndx; - - a->data = malloc(sizeof(*src->data) * src->size); - for (i = 0; i < src->size; i++) { - if (src->data[i]) a->data[i] = src->data[i]->copy(src->data[i]); - else a->data[i] = NULL; - } - - a->sorted = malloc(sizeof(*src->sorted) * src->size); - memcpy(a->sorted, src->sorted, sizeof(*src->sorted) * src->size); - return a; -} - -void array_free(array *a) { - size_t i; - if (!a) return; - - if (!a->is_weakref) { - for (i = 0; i < a->size; i++) { - if (a->data[i]) a->data[i]->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; - - if (!a->is_weakref) { - for (i = 0; i < a->used; i++) { - a->data[i]->reset(a->data[i]); - } - } - - a->used = 0; -} - -data_unset *array_pop(array *a) { - data_unset *du; - - assert(a->used != 0); - - a->used --; - du = a->data[a->used]; - a->data[a->used] = NULL; - - return du; -} - -static int array_get_index(array *a, const char *key, size_t keylen, int *rndx) { - int ndx = -1; - int i, pos = 0; - - if (key == NULL) return -1; - - /* try to find the string */ - for (i = pos = a->next_power_of_2 / 2; ; i >>= 1) { - int cmp; - - if (pos < 0) { - pos += i; - } else if (pos >= (int)a->used) { - pos -= i; - } else { - cmp = buffer_caseless_compare(key, keylen, a->data[a->sorted[pos]]->key->ptr, a->data[a->sorted[pos]]->key->used); - - if (cmp == 0) { - /* found */ - ndx = a->sorted[pos]; - break; - } else if (cmp < 0) { - pos -= i; - } else { - pos += i; - } - } - if (i == 0) break; - } - - if (rndx) *rndx = pos; - - return ndx; -} - -data_unset *array_get_element(array *a, const char *key, size_t keylen) { - int ndx; - - if (-1 != (ndx = array_get_index(a, key, keylen + 1, NULL))) { - /* found, leave here */ - - return a->data[ndx]; - } - - return NULL; -} - -data_unset *array_get_unused_element(array *a, data_type_t t) { - data_unset *ds = NULL; - - UNUSED(t); - - if (a->size == 0) return NULL; - - if (a->used == a->size) return NULL; - - if (a->data[a->used]) { - ds = a->data[a->used]; - - a->data[a->used] = NULL; - } - - return ds; -} - -void array_set_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len) { - data_string *ds_dst; - - if (NULL != (ds_dst = (data_string *)array_get_element(hdrs, key, key_len))) { - buffer_copy_string_len(ds_dst->value, value, val_len); - return; - } - - if (NULL == (ds_dst = (data_string *)array_get_unused_element(hdrs, TYPE_STRING))) { - ds_dst = data_string_init(); - } - - buffer_copy_string_len(ds_dst->key, key, key_len); - buffer_copy_string_len(ds_dst->value, value, val_len); - array_insert_unique(hdrs, (data_unset *)ds_dst); -} - -void array_append_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len) { - data_string *ds_dst; - - if (NULL == (ds_dst = (data_string *)array_get_unused_element(hdrs, TYPE_STRING))) { - ds_dst = data_string_init(); - } - - buffer_copy_string_len(ds_dst->key, key, key_len); - buffer_copy_string_len(ds_dst->value, value, val_len); - array_insert_unique(hdrs, (data_unset *)ds_dst); -} - -/* replace or insert data, return the old one with the same key */ -data_unset *array_replace(array *a, data_unset *du) { - int ndx; - - if (-1 == (ndx = array_get_index(a, du->key->ptr, du->key->used, NULL))) { - array_insert_unique(a, du); - return NULL; - } else { - data_unset *old = a->data[ndx]; - a->data[ndx] = du; - return old; - } -} - -int array_insert_unique(array *a, data_unset *str) { - int ndx = -1; - int pos = 0; - size_t j; - - /* generate unique index if necessary */ - if (str->key->used == 0 || str->is_index_key) { - buffer_copy_long(str->key, a->unique_ndx++); - str->is_index_key = 1; - } - - /* try to find the string */ - if (-1 != (ndx = array_get_index(a, str->key->ptr, str->key->used, &pos))) { - /* found, leave here */ - if (a->data[ndx]->type == str->type) { - str->insert_dup(a->data[ndx], str); - } else { - fprintf(stderr, "a\n"); - } - return 0; - } - - /* insert */ - - if (a->used+1 > INT_MAX) { - /* we can't handle more then INT_MAX entries: see array_get_index() */ - return -1; - } - - if (a->size == 0) { - a->size = 16; - a->data = malloc(sizeof(*a->data) * a->size); - a->sorted = malloc(sizeof(*a->sorted) * a->size); - assert(a->data); - 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); - assert(a->data); - assert(a->sorted); - for (j = a->used; j < a->size; j++) a->data[j] = NULL; - } - - ndx = (int) a->used; - - a->data[a->used++] = str; - - if (pos != ndx && - ((pos < 0) || - buffer_caseless_compare(str->key->ptr, str->key->used, a->data[a->sorted[pos]]->key->ptr, a->data[a->sorted[pos]]->key->used) > 0)) { - pos++; - } - - /* 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; - - if (a->next_power_of_2 == (size_t)ndx) a->next_power_of_2 <<= 1; - - return 0; -} - -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 = strlen(du->key->ptr); - - 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: - case TYPE_COUNT: - 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->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, "# %zd\n", i); - array_print_indent(depth + 1); - } - fprintf(stdout, "\"%s\"", du->key->ptr); - for (j = maxlen - strlen(du->key->ptr); j > 0; j --) { - fprintf(stdout, " "); - } - fprintf(stdout, " => "); - } - du->print(du, depth + 1); - fprintf(stdout, ",\n"); - } - if (!(i && (i - 1 % 5) == 0)) { - array_print_indent(depth + 1); - fprintf(stdout, "# %zd\n", i); - } - array_print_indent(depth); - fprintf(stdout, ")"); - - return 0; -} - -#ifdef DEBUG_ARRAY -int main (int argc, char **argv) { - array *a; - data_string *ds; - data_count *dc; - - UNUSED(argc); - UNUSED(argv); - - a = array_init(); - - ds = data_string_init(); - buffer_copy_string_len(ds->key, CONST_STR_LEN("abc")); - buffer_copy_string_len(ds->value, CONST_STR_LEN("alfrag")); - - array_insert_unique(a, (data_unset *)ds); - - ds = data_string_init(); - buffer_copy_string_len(ds->key, CONST_STR_LEN("abc")); - buffer_copy_string_len(ds->value, CONST_STR_LEN("hameplman")); - - array_insert_unique(a, (data_unset *)ds); - - ds = data_string_init(); - buffer_copy_string_len(ds->key, CONST_STR_LEN("123")); - buffer_copy_string_len(ds->value, CONST_STR_LEN("alfrag")); - - array_insert_unique(a, (data_unset *)ds); - - dc = data_count_init(); - buffer_copy_string_len(dc->key, CONST_STR_LEN("def")); - - array_insert_unique(a, (data_unset *)dc); - - dc = data_count_init(); - buffer_copy_string_len(dc->key, CONST_STR_LEN("def")); - - array_insert_unique(a, (data_unset *)dc); - - array_print(a, 0); - - array_free(a); - - fprintf(stderr, "%d\n", - buffer_caseless_compare(CONST_STR_LEN("Content-Type"), CONST_STR_LEN("Content-type"))); - - return 0; -} -#endif diff --git a/src/array.h b/src/array.h deleted file mode 100644 index 0fa73e87..00000000 --- a/src/array.h +++ /dev/null @@ -1,160 +0,0 @@ -#ifndef ARRAY_H -#define ARRAY_H - -#include "settings.h" - -#include <stdlib.h> -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#ifdef HAVE_PCRE_H -# include <pcre.h> -#endif -#include "buffer.h" - -#define DATA_IS_STRING(x) (x->type == TYPE_STRING) - -typedef enum { TYPE_UNSET, TYPE_STRING, TYPE_COUNT, TYPE_ARRAY, TYPE_INTEGER, TYPE_FASTCGI, TYPE_CONFIG } data_type_t; -#define DATA_UNSET \ - data_type_t type; \ - buffer *key; \ - int is_index_key; /* 1 if key is an array index (auto-generated keys) */ \ - struct data_unset *(*copy)(const struct data_unset *src); \ - void (* free)(struct data_unset *p); \ - void (* reset)(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 struct data_unset { - DATA_UNSET; -} data_unset; - -typedef struct { - data_unset **data; - - size_t *sorted; - - size_t used; - size_t size; - - size_t unique_ndx; - - size_t next_power_of_2; - int is_weakref; /* data is weakref, don't bother the data */ -} array; - -typedef struct { - DATA_UNSET; - - int count; -} data_count; - -LI_API data_count* data_count_init(void); - -typedef struct { - DATA_UNSET; - - buffer *value; -} data_string; - -LI_API data_string* data_string_init(void); -LI_API data_string* data_response_init(void); - -typedef struct { - DATA_UNSET; - - array *value; -} data_array; - -LI_API data_array* data_array_init(void); - -/** - * 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_t; - -/** - * possible fields to match against - */ -typedef enum { - COMP_UNSET, - COMP_SERVER_SOCKET, - COMP_HTTP_URL, - COMP_HTTP_HOST, - COMP_HTTP_REFERER, - COMP_HTTP_USER_AGENT, - COMP_HTTP_COOKIE, - COMP_HTTP_SCHEME, - COMP_HTTP_REMOTE_IP, - COMP_HTTP_QUERY_STRING, - COMP_HTTP_REQUEST_METHOD, - COMP_PHYSICAL_PATH, - COMP_PHYSICAL_PATH_EXISTS, - - COMP_LAST_ELEMENT -} comp_key_t; - -/* $HTTP["host"] == "incremental.home.kneschke.de" { ... } - * for print: comp_key op string - * for compare: comp cond string/regex - */ - -typedef struct _data_config data_config; -struct _data_config { - DATA_UNSET; - - array *value; - - buffer *comp_key; - comp_key_t comp; - - config_cond_t cond; - buffer *op; - - int context_ndx; /* more or less like an id */ - array *childs; - /* nested */ - data_config *parent; - /* for chaining only */ - data_config *prev; - data_config *next; - - buffer *string; -#ifdef HAVE_PCRE_H - pcre *regex; - pcre_extra *regex_study; -#endif -}; - -LI_API data_config* data_config_init(void); - -typedef struct { - DATA_UNSET; - - int value; -} data_integer; - -LI_API data_integer* data_integer_init(void); -LI_API array* array_init(void); -LI_API array* array_init_array(array *a); -LI_API void array_free(array *a); -LI_API void array_reset(array *a); -LI_API int array_insert_unique(array *a, data_unset *str); -LI_API data_unset* array_pop(array *a); -LI_API int array_print(array *a, int depth); -LI_API data_unset* array_get_unused_element(array *a, data_type_t t); -LI_API data_unset* array_get_element(array *a, const char *key, size_t key_len); -LI_API void array_set_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len); -LI_API void array_append_key_value(array *hdrs, const char *key, size_t key_len, const char *value, size_t val_len); -LI_API data_unset* array_replace(array *a, data_unset *du); -LI_API int array_strcasecmp(const char *a, size_t a_len, const char *b, size_t b_len); -LI_API void array_print_indent(int depth); -LI_API size_t array_get_max_key_length(array *a); - -#endif diff --git a/src/base.h b/src/base.h deleted file mode 100644 index a61a755a..00000000 --- a/src/base.h +++ /dev/null @@ -1,731 +0,0 @@ -#ifndef _BASE_H_ -#define _BASE_H_ - -#include <sys/types.h> -#include <sys/stat.h> - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <limits.h> -#ifdef HAVE_STDINT_H -# include <stdint.h> -#endif -#ifdef HAVE_INTTYPES_H -# include <inttypes.h> -#endif - -#include "settings.h" - -#ifdef HAVE_GLIB_H -/* include glib.h before our buffer.h and array.h to make sure their parameter-names - * don't clash with our type-names */ -#include <glib.h> -#endif - -#include "buffer.h" -#include "array.h" -#include "chunk.h" -#include "filter.h" -#include "keyvalue.h" -#include "settings.h" -#include "fdevent.h" -#include "sys-socket.h" -#include "http_req.h" -#include "etag.h" - -#if defined HAVE_LIBSSL && defined HAVE_OPENSSL_SSL_H -# define USE_OPENSSL -# include <openssl/ssl.h> -# if ! defined OPENSSL_NO_TLSEXT && ! defined SSL_CTRL_SET_TLSEXT_HOSTNAME -# define OPENSSL_NO_TLSEXT -# endif -#endif - -#ifdef HAVE_SYS_INOTIFY_H -# include <sys/inotify.h> -#endif - -#ifdef USE_LINUX_AIO_SENDFILE -# include <libaio.h> -#endif - -#ifdef USE_POSIX_AIO -# include <aio.h> -#endif - -/** some compat */ -#ifndef O_BINARY -# define O_BINARY 0 -#endif - -#ifndef SIZE_MAX -# ifdef SIZE_T_MAX -# define SIZE_MAX SIZE_T_MAX -# else -# define SIZE_MAX ((size_t)~0) -# endif -#endif - -#ifndef SSIZE_MAX -# define SSIZE_MAX ((size_t)~0 >> 1) -#endif - -#ifdef __APPLE__ -#include <crt_externs.h> -#define environ (* _NSGetEnviron()) -#elif !defined(_WIN32) -extern char **environ; -#endif - -/* for solaris 2.5 and NetBSD 1.3.x */ -#ifndef HAVE_SOCKLEN_T -typedef int socklen_t; -#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 */ -typedef unsigned __int32 uint32_t; -#endif - - -#ifndef SHUT_WR -# define SHUT_WR 1 -#endif - -#include "settings.h" - -typedef enum { - TIME_CONNECTION_ACCEPT, - TIME_REQUEST_START, - TIME_BACKEND_CONNECT, - - TIME_BACKEND_SEND_HEADER_START, - TIME_BACKEND_SEND_HEADER_END, - TIME_BACKEND_SEND_CONTENT_START, - TIME_BACKEND_SEND_CONTENT_END, - - TIME_BACKEND_RECV_HEADER_START, - TIME_BACKEND_RECV_HEADER_END, - TIME_BACKEND_RECV_CONTENT_START, - TIME_BACKEND_RECV_CONTENT_END, - - TIME_BACKEND_DISCONNECT, - - TIME_SEND_HEADER_START, - TIME_SEND_HEADER_END, - - TIME_SEND_CONTENT_START, - - TIME_SEND_ASYNC_READ_QUEUED, /* for async-io read */ - TIME_SEND_ASYNC_READ_START, /* for async-io read */ - TIME_SEND_ASYNC_READ_END, /* for async-io read */ - TIME_SEND_ASYNC_READ_END_QUEUED, /* for async-io read */ - - TIME_SEND_WRITE_START, - TIME_SEND_WRITE_END, - - TIME_SEND_CONTENT_END, - - TIME_REQUEST_END, - TIME_CONNECTION_CLOSE, - - TIME_LAST_ELEMENT -} connection_time_field_t; - -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; - -typedef enum { DIRECT, EXTERNAL } connection_type; - -typedef struct { - char *key; - connection_type type; - char *value; -} request_handler; - -typedef struct { - char *key; - char *host; - unsigned short port; - int used; - short factor; -} fcgi_connections; - -typedef struct { - /** HEADER */ - /* the request-line */ - buffer *request; - buffer *uri; - - buffer *orig_uri; - - http_method_t http_method; - http_version_t http_version; - - buffer *http_host; - - array *headers; - - /* CONTENT */ - off_t content_length; /* returned by strtoul() */ - - /* internal representation */ - int accept_encoding; - - /* internal */ - buffer *pathinfo; -} request; - -typedef struct { - off_t content_length; - int keep_alive; /* used by the subrequests in proxy, cgi and fcgi to say whether the subrequest was keep-alive or not */ - - array *headers; - - enum { - HTTP_TRANSFER_ENCODING_IDENTITY, HTTP_TRANSFER_ENCODING_CHUNKED - } transfer_encoding; -} 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 { - buffer *name; - buffer *etag; - - struct stat st; - - time_t stat_ts; - - enum { - STAT_CACHE_ENTRY_UNSET, - STAT_CACHE_ENTRY_ASYNC_STAT, - STAT_CACHE_ENTRY_STAT_FINISHED - } state; - -#ifdef HAVE_LSTAT - char is_symlink; -#endif - -#if defined(HAVE_SYS_INOTIFY_H) - int dir_version; /* when this entry was created the dir had this version */ - int dir_ndx; -#endif - - buffer *content_type; -} stat_cache_entry; - -typedef struct { -#ifdef HAVE_GLIB_H - GHashTable *files; /* a HashTable of stat_cache_entries for the files */ - GHashTable *dirs; /* a HashTable of stat_cache_entries for the dirs */ -#endif - - buffer *dir_name; /* for building the dirname from the filename */ - buffer *hash_key; /* tmp-buf for building the hash-key */ - -#if defined(HAVE_SYS_INOTIFY_H) - iosocket *sock; /* socket to the inotify fd (this should be in a backend struct */ -#endif -} stat_cache; - -typedef struct { - array *mimetypes; - - /* virtual-servers */ - buffer *document_root; - buffer *server_name; - buffer *error_handler; - buffer *server_tag; - buffer *dirlist_encoding; - buffer *errorfile_prefix; - - unsigned short max_keep_alive_requests; - unsigned short max_keep_alive_idle; - unsigned short max_read_idle; - unsigned short max_write_idle; - unsigned short max_connection_idle; - unsigned short use_xattr; - unsigned short follow_symlink; - unsigned short range_requests; - - /* 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_condition_cache_handling; - unsigned short log_ssl_noise; - unsigned short log_timeouts; - - - /* server wide */ - buffer *ssl_pemfile; - buffer *ssl_ca_file; - buffer *ssl_cipher_list; - unsigned short ssl_use_sslv2; - unsigned short ssl_verifyclient; - unsigned short ssl_verifyclient_enforce; - unsigned short ssl_verifyclient_depth; - buffer *ssl_verifyclient_username; - unsigned short ssl_verifyclient_export_cert; - - unsigned short use_ipv6; - unsigned short is_ssl; - 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 max_request_size; - - 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 - * per 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 lose our "we are writable" signal - * on the way. - * - */ - off_t *global_bytes_per_second_cnt_ptr; /* */ - -#ifdef USE_OPENSSL - SSL_CTX *ssl_ctx; -#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, /** we are wait for a connect */ - CON_STATE_REQUEST_START, /** after the connect, the request is initialized, keep-alive starts here again */ - CON_STATE_READ_REQUEST_HEADER, /** loop in the read-request-header until the full header is received */ - CON_STATE_VALIDATE_REQUEST_HEADER, /** validate the request-header */ - CON_STATE_HANDLE_REQUEST_HEADER, /** find a handler for the request */ - CON_STATE_READ_REQUEST_CONTENT, /** forward the request content to the handler */ - CON_STATE_HANDLE_RESPONSE_HEADER, /** the backend bounces the response back to the client */ - CON_STATE_WRITE_RESPONSE_HEADER, - CON_STATE_WRITE_RESPONSE_CONTENT, - CON_STATE_RESPONSE_END, - CON_STATE_ERROR, - CON_STATE_CLOSE -} connection_state_t; - -typedef enum { COND_RESULT_UNSET, COND_RESULT_FALSE, COND_RESULT_TRUE } cond_result_t; -typedef struct { - cond_result_t result; - int patterncount; - int matches[3 * 10]; - buffer *comp_value; /* just a pointer */ - - comp_key_t comp_type; -} cond_cache_t; - -typedef struct { - 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 timeval start_tv; - - 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 - */ - - iosocket *sock; - 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 others just disable */ - int keep_alive_idle; /* remember max_keep_alive_idle from config */ - - int file_started; - - chunkqueue *send; /* the response-content before filters are applied */ - chunkqueue *recv; /* the request-content, without encoding */ - - filter_chain *send_filters; /* the chain of filters to apply to response-content. */ - chunkqueue *send_raw; /* the full response (HTTP-Header + compression + chunking ) */ - chunkqueue *recv_raw; /* the full request (HTTP-Header + chunking ) */ - - 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; - - http_req *http_req; - request request; - request_uri uri; - physical physical; - response response; - - size_t header_len; - - buffer *authed_user; - array *environment; /* used to pass lighttpd internal stuff to the FastCGI/CGI apps, setenv does that */ - - /* response */ - int got_response; - - int in_joblist; - - connection_type mode; - - void **plugin_ctx; /* plugin connection specific config */ - - specific_config conf; /* global connection specific config */ - cond_cache_t *cond_cache; - - buffer *server_name; - - /* error-handler */ - buffer *error_handler; - int error_handler_saved_status; - int in_error_handler; - - void *srv_socket; /* reference to the server-socket (typecast to server_socket) */ - - /* etag handling */ - etag_flags_t etag_flags; - - -#ifdef HAVE_GLIB_H - GTimeVal timestamps[TIME_LAST_ELEMENT]; /**< used by timing.h */ -#endif - - int conditional_is_valid[COMP_LAST_ELEMENT]; -} connection; - -typedef struct { - connection **ptr; - size_t size; - size_t used; -} connections; - - -#ifdef HAVE_IPV6 -typedef struct { - int family; - union { - struct in6_addr ipv6; - struct in_addr ipv4; - } addr; - char b2[INET6_ADDRSTRLEN + 1]; - time_t ts; -} inet_ntop_cache_type; -#endif - - -typedef struct { - buffer *uri; - time_t mtime; - int http_status; -} realpath_cache_type; - -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 enum { - NETWORK_STATUS_UNSET, - NETWORK_STATUS_SUCCESS, - NETWORK_STATUS_FATAL_ERROR, - NETWORK_STATUS_CONNECTION_CLOSE, - NETWORK_STATUS_WAIT_FOR_EVENT, - NETWORK_STATUS_WAIT_FOR_AIO_EVENT, - NETWORK_STATUS_WAIT_FOR_FD, - NETWORK_STATUS_INTERRUPTED -} network_status_t; - -typedef struct { - unsigned short port; - buffer *bindhost; - - unsigned short dont_daemonize; - unsigned short daemonize_on_shutdown; - buffer *changeroot; - buffer *username; - buffer *groupname; - - buffer *pid_file; - - buffer *event_handler; - - buffer *modules_dir; - buffer *network_backend; - array *modules; - array *upload_tempdirs; - - unsigned short use_noatime; - - unsigned short max_worker; - unsigned short max_fds; - unsigned short max_conns; - unsigned int max_request_size; - - unsigned short log_request_header_on_error; - unsigned short log_state_handling; - unsigned short log_timing; - - enum { STAT_CACHE_ENGINE_UNSET, - STAT_CACHE_ENGINE_NONE, - STAT_CACHE_ENGINE_SIMPLE, - STAT_CACHE_ENGINE_FAM, - STAT_CACHE_ENGINE_INOTIFY - } stat_cache_engine; - unsigned short enable_cores; - - buffer *errorlog_file; - unsigned short errorlog_use_syslog; - buffer *breakagelog_file; - - unsigned short max_stat_threads; - unsigned short max_read_threads; -} server_config; - -typedef enum { - NETWORK_BACKEND_UNSET, - - NETWORK_BACKEND_WRITE, - NETWORK_BACKEND_WRITEV, - - NETWORK_BACKEND_LINUX_SENDFILE, - NETWORK_BACKEND_LINUX_AIO_SENDFILE, - NETWORK_BACKEND_POSIX_AIO, - NETWORK_BACKEND_GTHREAD_AIO, - NETWORK_BACKEND_GTHREAD_SENDFILE, - NETWORK_BACKEND_GTHREAD_FREEBSD_SENDFILE, - - NETWORK_BACKEND_FREEBSD_SENDFILE, - NETWORK_BACKEND_SOLARIS_SENDFILEV, - - NETWORK_BACKEND_WIN32_SEND, - NETWORK_BACKEND_WIN32_TRANSMITFILE, - -} network_backend_t; - - -typedef struct { - sock_addr addr; - iosocket *sock; - - buffer *ssl_pemfile; - buffer *ssl_ca_file; - buffer *ssl_cipher_list; - unsigned short ssl_use_sslv2; - unsigned short use_ipv6; - unsigned short is_ssl; - - buffer *srv_token; - -#ifdef USE_OPENSSL - SSL_CTX *ssl_ctx; -#endif -} server_socket; - -typedef struct { - server_socket **ptr; - - size_t size; - size_t used; -} server_socket_array; - -typedef struct server { - server_socket_array srv_sockets; - - fdevents *ev, *ev_ins; - - buffer_plugin plugins; - void *plugin_slots; - - /* counters */ - int con_opened; - int con_read; - int con_written; - int con_closed; - - int ssl_is_init; - - int max_fds; /* max possible 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 */ -#ifdef HAVE_IPV6 - inet_ntop_cache_type inet_ntop_cache[INET_NTOP_CACHE_MAX]; -#endif - 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; - - char entropy[8]; /* from /dev/[u]random if possible, otherwise rand() */ - char is_real_entropy; /* whether entropy is from /dev/[u]random */ - - buffer *ts_debug_str; - buffer *ts_date_str; - - /* config-file */ - array *config; - array *config_touched; - - array *config_context; - specific_config **config_storage; - - server_config srvconf; - - short unsigned config_deprecated; - short unsigned config_unsupported; - - connections *conns; - connections *joblist; - connections *joblist_prev; - connections *fdwaitqueue; - - stat_cache *stat_cache; - - fdevent_handler_t event_handler; - - network_status_t (* network_backend_write)(struct server *srv, connection *con, iosocket *sock, chunkqueue *cq); - network_status_t (* network_backend_read)(struct server *srv, connection *con, iosocket *sock, chunkqueue *cq); -#ifdef USE_OPENSSL - network_status_t (* network_ssl_backend_write)(struct server *srv, connection *con, iosocket *sock, chunkqueue *cq); - network_status_t (* network_ssl_backend_read)(struct server *srv, connection *con, iosocket *sock, chunkqueue *cq); -#endif - -#ifdef HAVE_PWD_H - uid_t uid; - gid_t gid; -#endif -#ifdef USE_GTHREAD -#ifdef USE_LINUX_AIO_SENDFILE - io_context_t linux_io_ctx; - - struct iocb *linux_io_iocbs; - -#endif -#ifdef USE_POSIX_AIO - struct aiocb *posix_aio_iocbs; -#endif - - GAsyncQueue *stat_queue; /* send a stat_job into this queue and joblist_queue will get a wakeup when the stat is finished */ - GAsyncQueue *joblist_queue; - GAsyncQueue *aio_write_queue; - - int did_wakeup; - int wakeup_pipe[2]; - iosocket *wakeup_iosocket; -#endif - network_backend_t network_backend; - int is_shutdown; -} server; - -int server_out_of_fds(server *srv, connection *con); - -LI_EXPORT unsigned short sock_addr_get_port(sock_addr *addr); /* configfile-glue.c */ - -#endif diff --git a/src/bitset.c b/src/bitset.c deleted file mode 100644 index 9e7e38b2..00000000 --- a/src/bitset.c +++ /dev/null @@ -1,69 +0,0 @@ -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <assert.h> - -#include "bitset.h" -#include "buffer.h" -#include "log.h" - -#define BITSET_BITS \ - ( CHAR_BIT * sizeof(size_t) ) - -#define BITSET_MASK(pos) \ - ( ((size_t)1) << ((pos) % BITSET_BITS) ) - -#define BITSET_WORD(set, pos) \ - ( (set)->bits[(pos) / BITSET_BITS] ) - -#define BITSET_USED(nbits) \ - ( ((nbits) + (BITSET_BITS - 1)) / BITSET_BITS ) - -bitset *bitset_init(size_t nbits) { - bitset *set; - - set = malloc(sizeof(*set)); - assert(set); - - set->bits = calloc(BITSET_USED(nbits), sizeof(*set->bits)); - set->nbits = nbits; - - assert(set->bits); - - return set; -} - -void bitset_reset(bitset *set) { - memset(set->bits, 0, BITSET_USED(set->nbits) * sizeof(*set->bits)); -} - -void bitset_free(bitset *set) { - free(set->bits); - free(set); -} - -void bitset_clear_bit(bitset *set, size_t pos) { - if (pos >= set->nbits) { - SEGFAULT("pos >= set->nbits: %zd >= %zd", pos, set->nbits); - } - - BITSET_WORD(set, pos) &= ~BITSET_MASK(pos); -} - -void bitset_set_bit(bitset *set, size_t pos) { - if (pos >= set->nbits) { - SEGFAULT("pos >= set->nbits: %zd >= %zd", pos, set->nbits); - } - - BITSET_WORD(set, pos) |= BITSET_MASK(pos); -} - -int bitset_test_bit(bitset *set, size_t pos) { - if (pos >= set->nbits) { - SEGFAULT("pos >= set->nbits: %zd >= %zd", pos, set->nbits); - } - - return (BITSET_WORD(set, pos) & BITSET_MASK(pos)) != 0; -} - diff --git a/src/bitset.h b/src/bitset.h deleted file mode 100644 index 45f9a9d5..00000000 --- a/src/bitset.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _BITSET_H_ -#define _BITSET_H_ - -#include <stddef.h> - -#include "settings.h" - -typedef struct { - size_t *bits; - size_t nbits; -} bitset; - -LI_API bitset* bitset_init(size_t nbits); -LI_API void bitset_reset(bitset *set); -LI_API void bitset_free(bitset *set); - -LI_API void bitset_clear_bit(bitset *set, size_t pos); -LI_API void bitset_set_bit(bitset *set, size_t pos); -LI_API int bitset_test_bit(bitset *set, size_t pos); - -#endif diff --git a/src/buffer.c b/src/buffer.c deleted file mode 100644 index 7354bda5..00000000 --- a/src/buffer.c +++ /dev/null @@ -1,1169 +0,0 @@ -#include <stdlib.h> -#include <string.h> - -#include <stdio.h> -#include <assert.h> -#include <ctype.h> - -#include "buffer.h" - - -static const char hex_chars[] = "0123456789abcdef"; - - -/** - * init the buffer - * - */ - -buffer* buffer_init(void) { - buffer *b; - - b = malloc(sizeof(*b)); - assert(b); - - b->ptr = NULL; - b->size = 0; - b->used = 0; - - return b; -} - -buffer *buffer_init_buffer(buffer *src) { - buffer *b = buffer_init(); - buffer_copy_string_buffer(b, src); - return b; -} - -/** - * free the buffer - * - */ - -void buffer_free(buffer *b) { - if (!b) return; - - free(b->ptr); - free(b); -} - -void buffer_reset(buffer *b) { - if (!b) return; - - /* limit don't reuse buffer larger than ... bytes */ - if (b->size > BUFFER_MAX_REUSE_SIZE) { - free(b->ptr); - b->ptr = NULL; - b->size = 0; - } else if (b->size) { - b->ptr[0] = '\0'; - } - - b->used = 0; -} - - -/** - * - * allocate (if necessary) enough space for 'size' (+1, if 'size' > 0) bytes and - * set the 'used' counter to 0 - * - */ - -#define BUFFER_PIECE_SIZE 64 - -int buffer_prepare_copy(buffer *b, size_t size) { - if (!b) return -1; - - if ((0 == b->size) || - (size >= b->size)) { - if (b->size) free(b->ptr); - - b->size = size; - - /* always allocate a multiple of BUFFER_PIECE_SIZE */ - /* adds a least 1 byte */ - b->size += BUFFER_PIECE_SIZE - (b->size % BUFFER_PIECE_SIZE); - - b->ptr = malloc(b->size); - assert(b->ptr); - } - b->used = 0; - return 0; -} - -/** - * - * increase the internal buffer (if necessary) to append another 'size' byte - * ->used isn't changed - * - */ - -int buffer_prepare_append(buffer *b, size_t size) { - if (!b) return -1; - - if (0 == b->size) { - b->size = size; - - /* always allocate a multiple of BUFFER_PIECE_SIZE */ - b->size += BUFFER_PIECE_SIZE - (b->size % BUFFER_PIECE_SIZE); - - b->ptr = malloc(b->size); - b->used = 0; - assert(b->ptr); - } else if (b->used + size >= b->size) { - b->size += size; - - /* always allocate a multiple of BUFFER_PIECE_SIZE */ - b->size += BUFFER_PIECE_SIZE - (b->size % BUFFER_PIECE_SIZE); - - b->ptr = realloc(b->ptr, b->size); - assert(b->ptr); - } - return 0; -} - -int buffer_copy_string(buffer *b, const char *s) { - size_t s_len; - - if (!s || !b) return -1; - - s_len = strlen(s) + 1; - buffer_prepare_copy(b, s_len); - - memcpy(b->ptr, s, s_len); - b->used = s_len; - - return 0; -} - -int buffer_copy_string_len(buffer *b, const char *s, size_t s_len) { - if (!s || !b) return -1; -#if 0 - /* removed optimization as we have to keep the empty string - * in some cases for the config handling - * - * url.access-deny = ( "" ) - */ - if (s_len == 0) return 0; -#endif - buffer_prepare_copy(b, s_len + 1); - - memcpy(b->ptr, s, s_len); - b->ptr[s_len] = '\0'; - b->used = s_len + 1; - - return 0; -} - -int buffer_copy_string_buffer(buffer *b, const buffer *src) { - if (!src) return -1; - - if (src->used == 0) { - buffer_reset(b); - return 0; - } - return buffer_copy_string_len(b, src->ptr, src->used - 1); -} - -int buffer_append_string(buffer *b, const char *s) { - size_t s_len; - - if (!s || !b) return -1; - - s_len = strlen(s); - buffer_prepare_append(b, s_len + 1); - if (b->used == 0) - b->used++; - - memcpy(b->ptr + b->used - 1, s, s_len + 1); - b->used += s_len; - - return 0; -} - -int buffer_append_string_rfill(buffer *b, const char *s, size_t maxlen) { - size_t s_len; - - if (!s || !b) return -1; - - s_len = strlen(s); - if (s_len > maxlen) s_len = maxlen; - buffer_prepare_append(b, maxlen + 1); - if (b->used == 0) - b->used++; - - memcpy(b->ptr + b->used - 1, s, s_len); - if (maxlen > s_len) { - memset(b->ptr + b->used - 1 + s_len, ' ', maxlen - s_len); - } - - b->used += maxlen; - b->ptr[b->used - 1] = '\0'; - return 0; -} - -/** - * append a string to the end of the buffer - * - * the resulting buffer is terminated with a '\0' - * s is treated as an un-terminated string (a \0 is handled as a normal character) - * - * @param b a buffer - * @param s the string - * @param s_len size of the string (without the terminating \0) - */ - -int buffer_append_string_len(buffer *b, const char *s, size_t s_len) { - if (!s || !b) return -1; - if (s_len == 0) return 0; - - buffer_prepare_append(b, s_len + 1); - if (b->used == 0) - b->used++; - - memcpy(b->ptr + b->used - 1, s, s_len); - b->used += s_len; - b->ptr[b->used - 1] = '\0'; - - return 0; -} - -int buffer_append_string_buffer(buffer *b, const buffer *src) { - if (!src) return -1; - if (src->used == 0) return 0; - - return buffer_append_string_len(b, src->ptr, src->used - 1); -} - -int buffer_append_memory(buffer *b, const char *s, size_t s_len) { - if (!s || !b) return -1; - if (s_len == 0) return 0; - - buffer_prepare_append(b, s_len); - memcpy(b->ptr + b->used, s, s_len); - b->used += s_len; - - return 0; -} - -int buffer_copy_memory(buffer *b, const char *s, size_t s_len) { - if (!s || !b) return -1; - - b->used = 0; - - return buffer_append_memory(b, s, s_len); -} - -int buffer_append_long_hex(buffer *b, unsigned long value) { - char *buf; - int shift = 0; - unsigned long copy = value; - - while (copy) { - copy >>= 4; - shift++; - } - if (shift == 0) - shift++; - if (shift & 0x01) - shift++; - - buffer_prepare_append(b, shift + 1); - if (b->used == 0) - b->used++; - buf = b->ptr + (b->used - 1); - b->used += shift; - - shift <<= 2; - while (shift > 0) { - shift -= 4; - *(buf++) = hex_chars[(value >> shift) & 0x0F]; - } - *buf = '\0'; - - return 0; -} - -int LI_ltostr(char *buf, long val) { - char swap; - char *end; - int len = 1; - - if (val < 0) { - len++; - *(buf++) = '-'; - val = -val; - } - - end = buf; - while (val > 9) { - *(end++) = '0' + (val % 10); - val = val / 10; - } - *(end) = '0' + val; - *(end + 1) = '\0'; - len += end - buf; - - while (buf < end) { - swap = *end; - *end = *buf; - *buf = swap; - - buf++; - end--; - } - - return len; -} - -int buffer_append_long(buffer *b, long val) { - if (!b) return -1; - - buffer_prepare_append(b, 32); - if (b->used == 0) - b->used++; - - b->used += LI_ltostr(b->ptr + (b->used - 1), val); - return 0; -} - -int buffer_copy_long(buffer *b, long val) { - if (!b) return -1; - - b->used = 0; - return buffer_append_long(b, val); -} - -#if !defined(SIZEOF_LONG) || (SIZEOF_LONG != SIZEOF_OFF_T) -int buffer_append_off_t(buffer *b, off_t val) { - char swap; - char *end; - char *start; - int len = 1; - - if (!b) return -1; - - buffer_prepare_append(b, 32); - if (b->used == 0) - b->used++; - - start = b->ptr + (b->used - 1); - if (val < 0) { - len++; - *(start++) = '-'; - val = -val; - } - - end = start; - while (val > 9) { - *(end++) = '0' + (val % 10); - val = val / 10; - } - *(end) = '0' + val; - *(end + 1) = '\0'; - len += end - start; - - while (start < end) { - swap = *end; - *end = *start; - *start = swap; - - start++; - end--; - } - - b->used += len; - return 0; -} - -int buffer_copy_off_t(buffer *b, off_t val) { - if (!b) return -1; - - b->used = 0; - return buffer_append_off_t(b, val); -} -#endif /* !defined(SIZEOF_LONG) || (SIZEOF_LONG != SIZEOF_OFF_T) */ - -char int2hex(char c) { - return hex_chars[(c & 0x0F)]; -} - -/* converts hex char (0-9, A-Z, a-z) to decimal. - * returns 0xFF on invalid input. - */ -char hex2int(unsigned char hex) { - hex = hex - '0'; - if (hex > 9) { - hex = (hex + '0' - 1) | 0x20; - hex = hex - 'a' + 11; - } - if (hex > 15) - hex = 0xFF; - - return hex; -} - - -/** - * init the ptr buffer - * - */ -buffer_ptr *buffer_ptr_init(buffer_ptr_free_t freer) -{ - buffer_ptr *l = calloc(1, sizeof(buffer_ptr)); - l->free = freer; - - return l; -} - -/** - * free the buffer_array - * - */ -void buffer_ptr_free(buffer_ptr *l) -{ - if (NULL != l) { - buffer_ptr_clear(l); - free(l); - } -} - -void buffer_ptr_clear(buffer_ptr *l) -{ - assert(NULL != l); - - if (l->free && l->used) { - size_t i; - for (i = 0; i < l->used; i ++) { - l->free(l->ptr[i]); - } - } - - if (l->ptr) { - free(l->ptr); - l->ptr = NULL; - } - l->used = 0; - l->size = 0; -} - -void buffer_ptr_append(buffer_ptr* l, void *item) -{ - assert(NULL != l); - if (l->ptr == NULL) { - l->size = 16; - l->ptr = (void **)malloc(sizeof(void *) * l->size); - } - else if (l->used == l->size) { - l->size += 16; - l->ptr = realloc(l->ptr, sizeof(void *) * l->size); - } - l->ptr[l->used++] = item; -} - -void *buffer_ptr_pop(buffer_ptr* l) -{ - assert(NULL != l && l->used > 0); - return l->ptr[--l->used]; -} - -void *buffer_ptr_top(buffer_ptr* l) -{ - assert(NULL != l && l->used > 0); - return l->ptr[l->used-1]; -} - -/** - * init the buffer - * - */ - -buffer_array* buffer_array_init(void) { - buffer_array *b; - - b = malloc(sizeof(*b)); - - assert(b); - b->ptr = NULL; - b->size = 0; - b->used = 0; - - return b; -} - -void buffer_array_reset(buffer_array *b) { - size_t i; - - if (!b) return; - - /* if they are too large, reduce them */ - for (i = 0; i < b->used; i++) { - buffer_reset(b->ptr[i]); - } - - b->used = 0; -} - - -/** - * free the buffer_array - * - */ - -void buffer_array_free(buffer_array *b) { - size_t i; - if (!b) return; - - for (i = 0; i < b->size; i++) { - if (b->ptr[i]) buffer_free(b->ptr[i]); - } - free(b->ptr); - free(b); -} - -buffer *buffer_array_append_get_buffer(buffer_array *b) { - size_t i; - - if (b->size == 0) { - b->size = 16; - b->ptr = malloc(sizeof(*b->ptr) * b->size); - assert(b->ptr); - for (i = 0; i < b->size; i++) { - b->ptr[i] = NULL; - } - } else if (b->size == b->used) { - b->size += 16; - b->ptr = realloc(b->ptr, sizeof(*b->ptr) * b->size); - assert(b->ptr); - for (i = b->used; i < b->size; i++) { - b->ptr[i] = NULL; - } - } - - if (b->ptr[b->used] == NULL) { - b->ptr[b->used] = buffer_init(); - } - - b->ptr[b->used]->used = 0; - - return b->ptr[b->used++]; -} - - -char * buffer_search_string_len(buffer *b, const char *needle, size_t len) { - size_t i; - if (len == 0) return NULL; - if (needle == NULL) return NULL; - - if (b->used < len) return NULL; - - for(i = 0; i < b->used - len; i++) { - if (0 == memcmp(b->ptr + i, needle, len)) { - return b->ptr + i; - } - } - - return NULL; -} - -buffer *buffer_init_string(const char *str) { - buffer *b = buffer_init(); - - buffer_copy_string(b, str); - - return b; -} - -int buffer_is_empty(buffer *b) { - if (!b) return 1; - - return (b->used == 0); -} - -/** - * check if two buffers contain the same data - * - * HISTORY: this function was pretty much optimized, but didn't handled - * alignment properly. - */ - -int buffer_is_equal(buffer *a, buffer *b) { - if (a->used != b->used) return 0; - if (a->used == 0) return 1; - - return (0 == strncmp(a->ptr, b->ptr, a->used - 1)); -} - -int buffer_is_equal_string(buffer *a, const char *s, size_t b_len) { - buffer b; - - b.ptr = (char *)s; - b.used = b_len + 1; - - return buffer_is_equal(a, &b); -} - -/* simple-assumption: - * - * most parts are equal and doing a case conversion takes time - * - */ -int buffer_caseless_compare(const char *a, size_t a_len, const char *b, size_t b_len) { - size_t ndx = 0, max_ndx; - size_t *al, *bl; - size_t mask = sizeof(*al) - 1; - - al = (size_t *)a; - bl = (size_t *)b; - - /* is the alignment correct? */ - if ( ((size_t)al & mask) == 0 && - ((size_t)bl & mask) == 0 ) { - - max_ndx = ((a_len < b_len) ? a_len : b_len) & ~mask; - - for (; ndx < max_ndx; ndx += sizeof(*al)) { - if (*al != *bl) break; - al++; bl++; - - } - - } - - a = (char *)al; - b = (char *)bl; - - max_ndx = ((a_len < b_len) ? a_len : b_len); - - for (; ndx < max_ndx; ndx++) { - int a1 = *a++, b1 = *b++; - - if (a1 != b1) { - /* always lowercase for transitive results */ - if (a1 >= 'A' && a1 <= 'Z') a1 |= 32; - if (b1 >= 'A' && b1 <= 'Z') b1 |= 32; - - if ((a1 - b1) != 0) return (a1 - b1); - } - } - - /* all chars are the same, and the length match too - * - * they are the same */ - if (a_len == b_len) return 0; - - /* if a is shorter then b, then b is larger */ - return (a_len - b_len); -} - - -/** - * check if the rightmost bytes of the string are equal. - * - * - */ - -int buffer_is_equal_right_len(buffer *b1, 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; - - if (0 == strncmp(b1->ptr + b1->used - 1 - len, - b2->ptr + b2->used - 1 - len, len)) { - return 1; - } - - return 0; -} - -int buffer_copy_string_hex(buffer *b, const char *in, size_t in_len) { - size_t i; - - /* BO protection */ - if (in_len * 2 < in_len) return -1; - - buffer_prepare_copy(b, in_len * 2 + 1); - - for (i = 0; i < in_len; i++) { - b->ptr[b->used++] = hex_chars[(in[i] >> 4) & 0x0F]; - b->ptr[b->used++] = hex_chars[in[i] & 0x0F]; - } - b->ptr[b->used++] = '\0'; - - return 0; -} - -/* everything except: ! ( ) * - . 0-9 A-Z _ a-z */ -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, 1, 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 */ -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, 1, 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 */ -}; - -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, 0, 0, 0, 0, 1, 0, 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 */ - 0, 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 */ -}; - -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, 0, 0, 0, 0, 1, 0, 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 */ - 0, 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 */ -}; - -const char encoded_chars_hex[] = { - /* - 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 70 - 7F */ - 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 */ -}; - - -int 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; - - if (!s || !b) return -1; - if (b->used == 0) return -1; - - if (b->ptr[b->used - 1] != '\0') return -1; - - if (s_len == 0) return 0; - - 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; - case ENCODING_HEX: - map = encoded_chars_hex; - break; - case ENCODING_UNSET: - return buffer_append_string_len(b, s, s_len); - } - - assert(map != NULL); - - /* 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; - case ENCODING_HEX: - d_len += 2; - break; - case ENCODING_UNSET: - break; - } - } else { - d_len ++; - } - } - - buffer_prepare_append(b, d_len); - - for (ds = (unsigned char *)s, d = (unsigned char *)b->ptr + b->used - 1, 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[((*ds) >> 4) & 0x0F]; - d[d_len++] = hex_chars[(*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[((*ds) >> 4) & 0x0F]; - d[d_len++] = hex_chars[(*ds) & 0x0F]; - d[d_len++] = ';'; - break; - case ENCODING_HEX: - d[d_len++] = hex_chars[((*ds) >> 4) & 0x0F]; - d[d_len++] = hex_chars[(*ds) & 0x0F]; - break; - case ENCODING_UNSET: - break; - } - } else { - d[d_len++] = *ds; - } - } - - /* terminate buffer and calculate new length */ - b->ptr[b->used + d_len - 1] = '\0'; - - b->used += d_len; - - return 0; -} - - -/* decodes url-special chars in-place. - * replaces non-printable characters with '_' - */ - -static int buffer_urldecode_internal(buffer *url, int is_query) { - unsigned char high, low; - const char *src; - char *dst; - - if (!url || !url->ptr) return -1; - - src = (const char*) url->ptr; - dst = (char*) url->ptr; - - while ((*src) != '\0') { - if (is_query && *src == '+') { - *dst = ' '; - } else if (*src == '%') { - *dst = '%'; - - high = hex2int(*(src + 1)); - if (high != 0xFF) { - low = hex2int(*(src + 2)); - if (low != 0xFF) { - high = (high << 4) | low; - - /* map out control characters */ - if (high < 32 || high == 127) high = '_'; - - *dst = high; - src += 2; - } - } - } else { - *dst = *src; - } - - dst++; - src++; - } - - *dst = '\0'; - url->used = (dst - url->ptr) + 1; - - return 0; -} - -int buffer_urldecode_path(buffer *url) { - return buffer_urldecode_internal(url, 0); -} - -int buffer_urldecode_query(buffer *url) { - return buffer_urldecode_internal(url, 1); -} - -/* Remove "/../", "//", "/./" parts from path. - * - * /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. - */ - -int buffer_path_simplify(buffer *dest, buffer *src) -{ - int toklen; - char c, pre1; - char *start, *slash, *walk, *out; - unsigned short pre; - - if (src == NULL || src->ptr == NULL || dest == NULL) - return -1; - - if (src == dest) - buffer_prepare_append(dest, 1); - else - buffer_prepare_copy(dest, src->used + 1); - - walk = src->ptr; - start = dest->ptr; - out = dest->ptr; - slash = dest->ptr; - while (*walk == ' ') { - walk++; - } - - pre1 = *(walk++); - c = *(walk++); - pre = pre1; - if (pre1 != '/') { - pre = ('/' << 8) | pre1; - *(out++) = '/'; - } - *(out++) = pre1; - - if (pre1 == '\0') { - dest->used = (out - start) + 1; - return 0; - } - - while (1) { - if (c == '/' || c == '\0') { - toklen = out - slash; - if (toklen == 3 && pre == (('.' << 8) | '.')) { - out = slash; - if (out > start) { - out--; - while (out > start && *out != '/') { - out--; - } - } - - if (c == '\0') - out++; - } else if (toklen == 1 || pre == (('/' << 8) | '.')) { - out = slash; - if (c == '\0') - out++; - } - - slash = out; - } - - if (c == '\0') - break; - - pre1 = c; - pre = (pre << 8) | pre1; - c = *walk; - *out = pre1; - - out++; - walk++; - } - - *out = '\0'; - dest->used = (out - start) + 1; - - return 0; -} - -int light_isdigit(int c) { - return (c >= '0' && c <= '9'); -} - -int light_isxdigit(int c) { - if (light_isdigit(c)) return 1; - - c |= 32; - return (c >= 'a' && c <= 'f'); -} - -int light_isalpha(int c) { - c |= 32; - return (c >= 'a' && c <= 'z'); -} - -int light_isalnum(int c) { - return light_isdigit(c) || light_isalpha(c); -} - -#undef BUFFER_CTYPE_FUNC -#define BUFFER_CTYPE_FUNC(type) \ - int buffer_is##type(buffer *b) { \ - size_t i, len; \ - if (b->used < 2) return 0; \ - /* strlen */ \ - len = b->used - 1; \ - /* c-string only */ \ - if (b->ptr[len] != '\0') { \ - return 0; \ - } \ - /* check on the whole string */ \ - for (i = 0; i < len; i ++) { \ - if (!light_is##type(b->ptr[i])) { \ - return 0; \ - } \ - } \ - return 1; \ - } - -BUFFER_CTYPE_FUNC(digit) -BUFFER_CTYPE_FUNC(xdigit) -BUFFER_CTYPE_FUNC(alpha) -BUFFER_CTYPE_FUNC(alnum) - -int buffer_to_lower(buffer *b) { - char *c; - - if (b->used == 0) return 0; - - for (c = b->ptr; *c; c++) { - if (*c >= 'A' && *c <= 'Z') { - *c |= 32; - } - } - - return 0; -} - - -int buffer_to_upper(buffer *b) { - char *c; - - if (b->used == 0) return 0; - - for (c = b->ptr; *c; c++) { - if (*c >= 'a' && *c <= 'z') { - *c &= ~32; - } - } - - return 0; -} - -buffer_pool *buffer_pool_init() { - buffer_pool *bp; - - bp = calloc(1, sizeof(*bp)); - - return bp; -} - -void buffer_pool_free(buffer_pool *bp) { - if (!bp) return; - - ARRAY_STATIC_FREE(bp, buffer, b, buffer_free(b)); - - free(bp); - - return; -} - -buffer *buffer_pool_get(buffer_pool *bp) { - buffer *b; - - if (bp->used == 0) { - return buffer_init(); - } - - b = bp->ptr[--bp->used]; - - buffer_reset(b); - - return b; -} - -void buffer_pool_append(buffer_pool *bp, buffer *b) { - ARRAY_STATIC_PREPARE_APPEND(bp); - - bp->ptr[bp->used++] = b; -} - - diff --git a/src/buffer.h b/src/buffer.h deleted file mode 100644 index 16d05601..00000000 --- a/src/buffer.h +++ /dev/null @@ -1,166 +0,0 @@ -#ifndef _BUFFER_H_ -#define _BUFFER_H_ - -#include "settings.h" - -#include <stdlib.h> -#include <sys/types.h> - -#include "array-static.h" - -typedef struct { - char *ptr; - - size_t used; - size_t size; -} buffer; - -typedef void (*buffer_ptr_free_t)(void *p); - -typedef struct { - void **ptr; - size_t size; - size_t used; - buffer_ptr_free_t free; -} buffer_ptr; - -typedef struct { - buffer **ptr; - - size_t used; - size_t size; -} buffer_array; - -typedef struct { - char *ptr; - - size_t offset; /* input pointer */ - - size_t used; /* output pointer */ - size_t size; -} read_buffer; - -LI_API buffer_ptr* buffer_ptr_init(buffer_ptr_free_t freer); -LI_API void buffer_ptr_free(buffer_ptr *b); -LI_API void buffer_ptr_clear(buffer_ptr *b); -LI_API void buffer_ptr_append(buffer_ptr *b, void *item); -LI_API void* buffer_ptr_pop(buffer_ptr *b); -LI_API void* buffer_ptr_top(buffer_ptr *b); - -LI_API buffer_array* buffer_array_init(void); -LI_API void buffer_array_free(buffer_array *b); -LI_API void buffer_array_reset(buffer_array *b); -LI_API buffer* buffer_array_append_get_buffer(buffer_array *b); - -LI_API buffer* buffer_init(void); -LI_API buffer* buffer_init_buffer(buffer *b); -LI_API buffer* buffer_init_string(const char *str); -LI_API void buffer_free(buffer *b); -LI_API void buffer_reset(buffer *b); - -LI_API int buffer_prepare_copy(buffer *b, size_t size); -LI_API int buffer_prepare_append(buffer *b, size_t size); - -LI_API int buffer_copy_string(buffer *b, const char *s); -LI_API int buffer_copy_string_len(buffer *b, const char *s, size_t s_len); -LI_API int buffer_copy_string_buffer(buffer *b, const buffer *src); -LI_API int buffer_copy_string_hex(buffer *b, const char *in, size_t in_len); - -LI_API int buffer_copy_long(buffer *b, long val); - -LI_API int buffer_copy_memory(buffer *b, const char *s, size_t s_len); - -LI_API int buffer_append_string(buffer *b, const char *s); -LI_API int buffer_append_string_len(buffer *b, const char *s, size_t s_len); -LI_API int buffer_append_string_buffer(buffer *b, const buffer *src); -LI_API int buffer_append_string_lfill(buffer *b, const char *s, size_t maxlen); -LI_API int buffer_append_string_rfill(buffer *b, const char *s, size_t maxlen); - -LI_API int buffer_append_long_hex(buffer *b, unsigned long len); -LI_API int buffer_append_long(buffer *b, long val); - -#if defined(SIZEOF_LONG) && (SIZEOF_LONG == SIZEOF_OFF_T) -#define buffer_copy_off_t(x, y) buffer_copy_long(x, y) -#define buffer_append_off_t(x, y) buffer_append_long(x, y) -#else -LI_API int buffer_copy_off_t(buffer *b, off_t val); -LI_API int buffer_append_off_t(buffer *b, off_t val); -#endif - -LI_API int buffer_append_memory(buffer *b, const char *s, size_t s_len); - -LI_API char* buffer_search_string_len(buffer *b, const char *needle, size_t len); - -LI_API int buffer_is_empty(buffer *b); -LI_API int buffer_is_equal(buffer *a, buffer *b); -LI_API int buffer_is_equal_right_len(buffer *a, buffer *b, size_t len); -LI_API int buffer_is_equal_string(buffer *a, const char *s, size_t b_len); -LI_API int buffer_caseless_compare(const char *a, size_t a_len, const char *b, size_t b_len); - -typedef enum { - ENCODING_UNSET, - ENCODING_REL_URI, /* for coding a rel-uri (/with space/and%percent) nicely as part of an href */ - ENCODING_REL_URI_PART, /* same as ENC_REL_URL plus encoding "/" as "%2F" */ - ENCODING_HTML, /* "&" becomes "&" and so on */ - ENCODING_MINIMAL_XML, /* minimal encoding for xml */ - ENCODING_HEX /* encode string as hex */ -} buffer_encoding_t; - -LI_API int buffer_append_string_encoded(buffer *b, const char *s, size_t s_len, buffer_encoding_t encoding); - -LI_API int buffer_urldecode_path(buffer *url); -LI_API int buffer_urldecode_query(buffer *url); -LI_API int buffer_path_simplify(buffer *dest, buffer *src); - -LI_API int buffer_to_lower(buffer *b); -LI_API int buffer_to_upper(buffer *b); - -/** deprecated */ -LI_API int LI_ltostr(char *buf, long val); -LI_API char hex2int(unsigned char c); -LI_API char int2hex(char i); - -LI_API int light_isdigit(int c); -LI_API int light_isxdigit(int c); -LI_API int light_isalpha(int c); -LI_API int light_isalnum(int c); - -#define BUFFER_CTYPE_FUNC(type) int buffer_is##type(buffer *b); -BUFFER_CTYPE_FUNC(digit) -BUFFER_CTYPE_FUNC(xdigit) -BUFFER_CTYPE_FUNC(alpha) -BUFFER_CTYPE_FUNC(alnum) - -#define BUF_STR(x) x->ptr -#define BUF_STR_LEN(x) (x->used ? x->used - 1 : 0) - -/* used on solaris as their vprintf() hates NULL for %s */ -#define SAFE_BUF_STR(x) x && x->ptr ? x->ptr : "(null)" - -#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) BUF_STR(x), x->used ? x->used - 1 : 0 - - -#define UNUSED(x) ( (void)(x) ) - -/** - * a pool of unused buffer * - */ - -ARRAY_STATIC_DEF(buffer_pool, buffer, ); - -buffer_pool* buffer_pool_init(); -void buffer_pool_free(buffer_pool* ); - -buffer *buffer_pool_get(buffer_pool *bp); -void buffer_pool_append(buffer_pool *bp, buffer *); - -#endif - - diff --git a/src/chunk.c b/src/chunk.c deleted file mode 100644 index a5742e51..00000000 --- a/src/chunk.c +++ /dev/null @@ -1,718 +0,0 @@ -/** - * the network chunk-API - * - * - */ - -#include <sys/types.h> -#include <sys/stat.h> - -#include <stdlib.h> -#include <fcntl.h> - -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <assert.h> - -#include "chunk.h" - -#include "sys-mmap.h" -#include "sys-files.h" - -#include "log.h" - -/** - * create a global pool for unused chunks - * - * the chunk is moved from queue to queue (by stealing) - * and moved back into the unused pool. - * - * Instead of having a local pool of unused chunks per queue - * we use a global pool - * - */ - -static chunk *chunkpool = NULL; -static size_t chunkpool_chunks = 0; - -chunkqueue *chunkqueue_init(void) { - chunkqueue *cq; - - cq = calloc(1, sizeof(*cq)); - - cq->first = NULL; - cq->last = NULL; - - return cq; -} - -static chunk *chunk_init(void) { - chunk *c; - - c = calloc(1, sizeof(*c)); - - c->mem = buffer_init(); - c->file.name = buffer_init(); - c->file.fd = -1; - c->file.copy.fd = -1; - c->file.mmap.start = MAP_FAILED; - c->next = NULL; - - c->async.written = -1; - - return c; -} - -static void chunk_reset(chunk *c) { - if (!c) return; - - buffer_reset(c->mem); - - if (c->file.is_temp && !buffer_is_empty(c->file.name)) { - unlink(c->file.name->ptr); - } - c->file.is_temp = 0; - - buffer_reset(c->file.name); - - if (c->file.fd != -1) { - close(c->file.fd); - c->file.fd = -1; - } - - if (c->file.copy.fd != -1) { - close(c->file.copy.fd); - c->file.copy.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.length = 0; - c->file.start = 0; - - c->file.mmap.length = 0; - c->file.mmap.offset = 0; - - c->file.copy.length = 0; - c->file.copy.offset = 0; - - c->async.written = -1; - c->async.ret_val = 0; - - c->offset = 0; - c->next = NULL; -} - -static void chunk_free(chunk *c) { - if (!c) return; - - /* make sure fd's are closed and tempfile's are deleted. */ - chunk_reset(c); - - buffer_free(c->mem); - buffer_free(c->file.name); - - free(c); -} - -/** - * mark the chunk as done - * - * @param c chunk to set done - * @return 1 if done, 0 if not - */ -void chunk_set_done(chunk *c) { - switch (c->type) { - case MEM_CHUNK: - c->offset = c->mem->used - 1; - - break; - case FILE_CHUNK: - c->offset = c->file.length; - - break; - default: - break; - } -} - -/** - * check if chunk is finished - * - * @param c chunk to set done - * @return 1 if done, 0 if not - */ -int chunk_is_done(chunk *c) { - switch (c->type) { - case MEM_CHUNK: - return ((c->mem->used == 0) || (c->offset == (off_t)c->mem->used - 1)); - case FILE_CHUNK: - return ((c->file.length == 0) || (c->offset == c->file.length)); - case UNUSED_CHUNK: - default: - return 1; - } -} - -off_t chunk_length(chunk *c) { - switch (c->type) { - case MEM_CHUNK: - if (c->mem->used == 0) return 0; - return (off_t)c->mem->used - 1 - c->offset; - case FILE_CHUNK: - return c->file.length - c->offset; - case UNUSED_CHUNK: - break; - } - return 0; -} - -void chunkpool_free(void) { - if (!chunkpool) return; - - /* free the pool */ - while(chunkpool) { - chunk *c = chunkpool->next; - chunk_free(chunkpool); - chunkpool = c; - } - chunkpool_chunks = 0; -} - -static chunk *chunkpool_get_unused_chunk(void) { - chunk *c; - - /* check if we have an unused chunk */ - if (!chunkpool) { - c = chunk_init(); - } else { - /* take the first element from the list (a stack) */ - c = chunkpool; - chunkpool = c->next; - c->next = NULL; - - chunkpool_chunks--; - } - - return c; -} - -/** - * keep unused chunks alive and store them in the chunkpool - * - * we only want to keep a small set of chunks alive to balance between - * memory-usage and mallocs - * - * each filter will ask for a chunk - */ -static void chunkpool_add_unused_chunk(chunk *c) { - if (chunkpool_chunks > 128) { - chunk_free(c); - } else { - chunk_reset(c); - - /* prepend the chunk to the chunkpool */ - c->next = chunkpool; - chunkpool = c; - chunkpool_chunks++; - } -} - - -void chunkqueue_free(chunkqueue *cq) { - chunk *c, *pc; - - if (!cq) return; - - for (c = cq->first; c; ) { - pc = c; - c = c->next; - chunk_free(pc); - } - - free(cq); -} - -static int chunkqueue_prepend_chunk(chunkqueue *cq, chunk *c) { - c->next = cq->first; - cq->first = c; - - if (cq->last == NULL) { - cq->last = c; - } - - return 0; -} - -static int chunkqueue_append_chunk(chunkqueue *cq, chunk *c) { - if (cq->last) { - cq->last->next = c; - } - cq->last = c; - - if (cq->first == NULL) { - cq->first = c; - } - - return 0; -} - -/** - * reset all chunks of the queue - */ -void chunkqueue_reset(chunkqueue *cq) { - chunk *c; - - /* mark all read done */ - for (c = cq->first; c; c = c->next) { - chunk_set_done(c); - } - - chunkqueue_remove_finished_chunks(cq); - - cq->bytes_in = 0; - cq->bytes_out = 0; - cq->is_closed = 0; -} - -int chunkqueue_append_file(chunkqueue *cq, buffer *fn, off_t offset, off_t len) { - chunk *c; - - if (len == 0) return 0; - - c = chunkpool_get_unused_chunk(); - - c->type = FILE_CHUNK; - - buffer_copy_string_buffer(c->file.name, fn); - c->file.start = offset; - c->file.length = len; - c->offset = 0; - - chunkqueue_append_chunk(cq, c); - - return 0; -} - -int chunkqueue_steal_tempfile(chunkqueue *cq, chunk *in) { - chunk *c; - - assert(in->type == FILE_CHUNK); - assert(in->file.is_temp == 1); - - c = chunkpool_get_unused_chunk(); - - c->type = FILE_CHUNK; - buffer_copy_string_buffer(c->file.name, in->file.name); - c->file.start = in->file.start + in->offset; - c->file.length = in->file.length - in->offset; - c->offset = 0; - c->file.is_temp = 1; - in->file.is_temp = 0; - - chunkqueue_append_chunk(cq, c); - - return 0; -} - -/** - * move the content of chunk to another chunkqueue. return total bytes copied/stolen. - */ -off_t chunkqueue_steal_chunk(chunkqueue *cq, chunk *c) { - /* we are copying the whole buffer, just steal it */ - off_t total = 0; - buffer *b, btmp; - - if (!cq) return 0; - if (chunk_is_done(c)) return 0; - - switch (c->type) { - case MEM_CHUNK: - total = c->mem->used - c->offset - 1; - if (c->offset == 0) { - b = chunkqueue_get_append_buffer(cq); - btmp = *b; *b = *(c->mem); *(c->mem) = btmp; - } else { - chunkqueue_append_mem(cq, c->mem->ptr + c->offset, total); - chunk_set_done(c); - } - break; - case FILE_CHUNK: - total = c->file.length - c->offset; - - if (c->file.is_temp) { - chunkqueue_steal_tempfile(cq, c); - } else { - chunkqueue_append_file(cq, c->file.name, c->file.start + c->offset, c->file.length - c->offset); - chunk_set_done(c); - } - - break; - case UNUSED_CHUNK: - return 0; - } - - return total; -} - -/* - * copy/steal all chunks from in chunkqueue. return total bytes copied/stolen. - * - */ -off_t chunkqueue_steal_all_chunks(chunkqueue *cq, chunkqueue *in) { - off_t total = 0; - chunk *c; - - if (!cq || !in) return 0; - - for (c = in->first; c; c = c->next) { - total += chunkqueue_steal_chunk(cq, c); - } - - return total; -} - -/* - * copy/steal max_len bytes from chunk chain. return total bytes copied/stolen. - * - */ -off_t chunkqueue_steal_chunks_len(chunkqueue *out, chunk *c, off_t max_len) { - off_t total = 0; - off_t we_have = 0, we_want = 0; - buffer *b; - - if (!out || !c) return 0; - - /* copy/steal chunks */ - for (; c && max_len > 0; c = c->next) { - switch (c->type) { - case FILE_CHUNK: - we_have = c->file.length - c->offset; - - if (we_have == 0) break; - - if (we_have > max_len) we_have = max_len; - - chunkqueue_append_file(out, c->file.name, c->offset, we_have); - - c->offset += we_have; - max_len -= we_have; - total += we_have; - - /* steal the tempfile - * - * This is tricky: - * - we reference the tempfile from the in-queue several times - * if the chunk is larger than max_len - * - we can't simply cleanup the in-queue as soon as possible - * as it would remove the tempfiles - * - the idea is to 'steal' the tempfiles and attach the is_temp flag to the last - * referencing chunk of the fastcgi-write-queue - * - */ - - if (c->offset == c->file.length) { - chunk *out_c; - - out_c = out->last; - - /* the last of the out-queue should be a FILE_CHUNK (we just created it) - * and the incoming side should have given use a temp-file-chunk */ - assert(out_c->type == FILE_CHUNK); - assert(c->file.is_temp == 1); - - out_c->file.is_temp = 1; - c->file.is_temp = 0; - } - - break; - case MEM_CHUNK: - /* skip empty chunks */ - if (c->mem->used == 0) break; - - we_have = c->mem->used - c->offset - 1; - if (we_have == 0) break; - - we_want = we_have < max_len ? we_have : max_len; - - if (we_have == we_want) { - /* steal whole chunk */ - chunkqueue_steal_chunk(out, c); - } else { - /* copy unused data from chunk */ - b = chunkqueue_get_append_buffer(out); - buffer_copy_string_len(b, c->mem->ptr + c->offset, we_want); - c->offset += we_want; - } - total += we_want; - max_len -= we_want; - - break; - default: - break; - } - } - return total; -} - -/** - * skip bytes in the chunkqueue - * - * @param cq chunkqueue - * @param skip bytes to skip - * @return bytes skipped - */ -off_t chunkqueue_skip(chunkqueue *cq, off_t skip) { - off_t total = 0; - off_t we_have = 0, we_want = 0; - chunk *c; - - if (!cq) return 0; - - /* consume chunks */ - for (c = cq->first; c && skip > 0; c = c->next) { - we_have = chunk_length(c); - - /* skip empty chunks */ - if (!we_have) continue; - - we_want = we_have < skip ? we_have : skip; - - c->offset += we_want; - total += we_want; - skip -= we_want; - } - - return total; -} - -int chunkqueue_append_buffer(chunkqueue *cq, buffer *mem) { - chunk *c; - - if (mem->used == 0) return 0; - - c = chunkpool_get_unused_chunk(); - c->type = MEM_CHUNK; - c->offset = 0; - buffer_copy_string_buffer(c->mem, mem); - - chunkqueue_append_chunk(cq, c); - - return 0; -} - -int chunkqueue_prepend_buffer(chunkqueue *cq, buffer *mem) { - chunk *c; - - if (mem->used == 0) return 0; - - c = chunkpool_get_unused_chunk(); - c->type = MEM_CHUNK; - c->offset = 0; - buffer_copy_string_buffer(c->mem, mem); - - chunkqueue_prepend_chunk(cq, c); - - return 0; -} - -int chunkqueue_append_mem(chunkqueue *cq, const char * mem, size_t len) { - chunk *c; - - if (len == 0) return 0; - - c = chunkpool_get_unused_chunk(); - c->type = MEM_CHUNK; - c->offset = 0; - buffer_copy_string_len(c->mem, mem, len); - - chunkqueue_append_chunk(cq, c); - - return 0; -} - -buffer * chunkqueue_get_prepend_buffer(chunkqueue *cq) { - chunk *c; - - c = chunkpool_get_unused_chunk(); - - c->type = MEM_CHUNK; - c->offset = 0; - buffer_reset(c->mem); - - chunkqueue_prepend_chunk(cq, c); - - return c->mem; -} - -buffer *chunkqueue_get_append_buffer(chunkqueue *cq) { - chunk *c; - - c = chunkpool_get_unused_chunk(); - - c->type = MEM_CHUNK; - c->offset = 0; - buffer_reset(c->mem); - - chunkqueue_append_chunk(cq, c); - - return c->mem; -} - -int chunkqueue_set_tempdirs(chunkqueue *cq, array *tempdirs) { - if (!cq) return -1; - - cq->tempdirs = tempdirs; - - return 0; -} - -chunk *chunkqueue_get_append_tempfile(chunkqueue *cq) { - chunk *c; - buffer *template = buffer_init_string("/var/tmp/lighttpd-upload-XXXXXX"); - - c = chunkpool_get_unused_chunk(); - - c->type = FILE_CHUNK; - c->offset = 0; - - if (cq->tempdirs && cq->tempdirs->used) { - size_t i; - - /* we have several tempdirs, only if all of them fail we jump out */ - - for (i = 0; i < cq->tempdirs->used; i++) { - data_string *ds = (data_string *)cq->tempdirs->data[i]; - - buffer_copy_string_buffer(template, ds->value); - PATHNAME_APPEND_SLASH(template); - buffer_append_string_len(template, CONST_STR_LEN("lighttpd-upload-XXXXXX")); - - if (-1 != (c->file.fd = mkstemp(template->ptr))) { - /* only trigger the unlink if we created the temp-file successfully */ - c->file.is_temp = 1; - break; - } - } - } else { - if (-1 != (c->file.fd = mkstemp(template->ptr))) { - /* only trigger the unlink if we created the temp-file successfully */ - c->file.is_temp = 1; - } - } - - buffer_copy_string_buffer(c->file.name, template); - c->file.length = 0; - - chunkqueue_append_chunk(cq, c); - - buffer_free(template); - - return c; -} - - -off_t chunkqueue_length(chunkqueue *cq) { - off_t len = 0; - chunk *c; - - for (c = cq->first; c; c = c->next) { - switch (c->type) { - case MEM_CHUNK: - len += c->mem->used ? c->mem->used - 1 : 0; - break; - case FILE_CHUNK: - len += c->file.length; - break; - default: - break; - } - } - - return len; -} - -off_t chunkqueue_written(chunkqueue *cq) { - off_t len = 0; - chunk *c; - - for (c = cq->first; c; c = c->next) { - switch (c->type) { - case MEM_CHUNK: - case FILE_CHUNK: - len += c->offset; - break; - default: - break; - } - } - - return len; -} - -int chunkqueue_is_empty(chunkqueue *cq) { - return cq->first ? 0 : 1; -} - -int chunkqueue_remove_finished_chunks(chunkqueue *cq) { - chunk *c; - - for (c = cq->first; c; c = cq->first) { - if (!chunk_is_done(c)) break; - - /* the chunk is finished, remove it from the queue */ - cq->first = c->next; - if (c == cq->last) cq->last = NULL; - - chunkpool_add_unused_chunk(c); - - } - - return 0; -} - -void chunkqueue_print(chunkqueue *cq) { - chunk *c; - - for (c = cq->first; c; c = c->next) { - fprintf(stderr, "(mem) %s", c->mem->ptr + c->offset); - } - fprintf(stderr, "\r\n"); -} - - -/** - * remove the last chunk if it is empty - */ - -void chunkqueue_remove_empty_last_chunk(chunkqueue *cq) { - chunk *c; - if (!cq->last) return; - if (!cq->first) return; - - if (cq->last->type != MEM_CHUNK || cq->last->mem->used != 0) return; - - if (cq->first == cq->last) { - c = cq->first; - - chunk_free(c); - cq->first = cq->last = NULL; - } else { - for (c = cq->first; c->next; c = c->next) { - if (c->next == cq->last) { - cq->last = c; - - chunk_free(c->next); - c->next = NULL; - - return; - } - } - } -} - - diff --git a/src/chunk.h b/src/chunk.h deleted file mode 100644 index 63793b2f..00000000 --- a/src/chunk.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef _CHUNK_H_ -#define _CHUNK_H_ - -#include "buffer.h" -#include "array.h" -#include "sys-mmap.h" - -typedef struct chunk { - enum { UNUSED_CHUNK, MEM_CHUNK, FILE_CHUNK } type; - - buffer *mem; /* either the storage of the mem-chunk or the read-ahead buffer */ - - struct { - /* filechunk */ - buffer *name; /* name of the file */ - off_t start; /* starting offset in the file */ - off_t length; /* octets to send from the starting offset */ - - int fd; - 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> octets away from the start of the file */ - } mmap; - - int is_temp; /* file is temporary and will be deleted on cleanup */ - - struct { - int fd; - off_t length; - off_t offset; - } copy; - } file; - - off_t offset; /* octets sent from this chunk - the size of the chunk is either - - mem-chunk: mem->used - 1 - - file-chunk: file.length - */ - - struct { - off_t written; - int ret_val; - } async; - - struct chunk *next; -} chunk; - -typedef struct { - chunk *first; - chunk *last; - - array *tempdirs; - - int is_closed; /* the input to this CQ is closed */ - - off_t bytes_in, bytes_out; -} chunkqueue; - -LI_API void chunkpool_free(void); - -LI_API chunkqueue* chunkqueue_init(void); -LI_API int chunkqueue_set_tempdirs(chunkqueue *c, array *tempdirs); -LI_API int chunkqueue_append_file(chunkqueue *c, buffer *fn, off_t offset, off_t len); -LI_API int chunkqueue_append_mem(chunkqueue *c, const char *mem, size_t len); -LI_API int chunkqueue_append_buffer(chunkqueue *c, buffer *mem); -LI_API int chunkqueue_prepend_buffer(chunkqueue *c, buffer *mem); - -LI_API buffer * chunkqueue_get_append_buffer(chunkqueue *c); -LI_API buffer * chunkqueue_get_prepend_buffer(chunkqueue *c); -LI_API chunk * chunkqueue_get_append_tempfile(chunkqueue *cq); -LI_API int chunkqueue_steal_tempfile(chunkqueue *cq, chunk *in); -LI_API off_t chunkqueue_steal_chunk(chunkqueue *cq, chunk *c); -LI_API off_t chunkqueue_steal_chunks_len(chunkqueue *cq, chunk *c, off_t max_len); -LI_API off_t chunkqueue_steal_all_chunks(chunkqueue *cq, chunkqueue *in); -LI_API off_t chunkqueue_skip(chunkqueue *cq, off_t skip); -LI_API void chunkqueue_remove_empty_last_chunk(chunkqueue *cq); - -LI_API int chunkqueue_remove_finished_chunks(chunkqueue *cq); - -LI_API off_t chunkqueue_length(chunkqueue *c); -LI_API off_t chunkqueue_written(chunkqueue *c); -LI_API void chunkqueue_free(chunkqueue *c); -LI_API void chunkqueue_reset(chunkqueue *c); - -LI_API int chunkqueue_is_empty(chunkqueue *c); - -LI_API void chunkqueue_print(chunkqueue *cq); - -LI_API int chunk_is_done(chunk *c); -LI_API void chunk_set_done(chunk *c); -LI_API off_t chunk_length(chunk *c); - -#endif diff --git a/src/config.h.cmake b/src/config.h.cmake deleted file mode 100644 index 0aaeaa4f..00000000 --- a/src/config.h.cmake +++ /dev/null @@ -1,153 +0,0 @@ -/* - CMake autogenerated config.h file. Do not edit! -*/ - -/* 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_SYSLIMITS_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_TIME_H -#cmakedefine HAVE_UNISTD_H -#cmakedefine HAVE_PTHREAD_H -#cmakedefine HAVE_INET_ATON -#cmakedefine HAVE_IPV6 -#cmakedefine HAVE_ISSETUGID - -/* XATTR */ -#cmakedefine HAVE_ATTR_ATTRIBUTES_H - -/* mySQL */ -#cmakedefine HAVE_MYSQL_H -#cmakedefine HAVE_LIBMYSQL - -/* postgresql */ -#cmakedefine HAVE_LIBPQ_FE_H -#cmakedefine HAVE_LIBPQ - -/* OpenSSL */ -#cmakedefine HAVE_OPENSSL_SSL_H -#cmakedefine HAVE_LIBCRYPTO -#cmakedefine OPENSSL_NO_KRB5 -#cmakedefine HAVE_LIBSSL - -#cmakedefine HAVE_AIO_H - -/* BZip */ -#cmakedefine HAVE_BZLIB_H -#cmakedefine HAVE_LIBBZ2 - -/* FAM */ -#cmakedefine HAVE_FAM_H - -/* getopt */ -#cmakedefine HAVE_GETOPT_H - -#cmakedefine HAVE_INTTYPES_H - -/* LDAP */ -#cmakedefine HAVE_LDAP_H -#cmakedefine HAVE_LIBLDAP - -/* libaio */ -#cmakedefine HAVE_LIBAIO_H -#cmakedefine HAVE_LIBAIO - -/* 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_H -#cmakedefine HAVE_LIBUUID - -/* ZLIB */ -#cmakedefine HAVE_ZLIB_H -#cmakedefine HAVE_LIBZ - -/* GLIB */ -#cmakedefine HAVE_GLIB_H -#cmakedefine HAVE_GLIB - -/* lua */ -#cmakedefine HAVE_LUA_H -#cmakedefine HAVE_LIBLUA - -/* 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_CRYPT -#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_SENDFILE64 -#cmakedefine HAVE_SENDFILEV -#cmakedefine HAVE_SIGACTION -#cmakedefine HAVE_SIGNAL -#cmakedefine HAVE_SIGTIMEDWAIT -#cmakedefine HAVE_STRPTIME -#cmakedefine HAVE_STRTOLL -#cmakedefine HAVE_SYSLOG -#cmakedefine HAVE_WRITEV - -/* libcrypt */ -#cmakedefine HAVE_LIBCRYPT - -/* fastcgi */ -#cmakedefine HAVE_FASTCGI_H -#cmakedefine HAVE_FASTCGI_FASTCGI_H - -#cmakedefine LIGHTTPD_STATIC diff --git a/src/configfile-glue.c b/src/configfile-glue.c deleted file mode 100644 index 8a31e333..00000000 --- a/src/configfile-glue.c +++ /dev/null @@ -1,675 +0,0 @@ -#include <string.h> -#include <ctype.h> - -#include "base.h" -#include "buffer.h" -#include "array.h" -#include "log.h" -#include "plugin.h" -#include "configfile.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[]) { - size_t i; - data_unset *du; - - for (i = 0; cv[i].key; i++) { - - if (NULL == (du = array_get_element(ca, cv[i].key, strlen(cv[i].key)))) { - /* no found */ - - continue; - } - - 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++) { - if (da->value->data[j]->type == TYPE_STRING) { - data_string *ds; - - if (NULL == (ds = (data_string *)array_get_unused_element(cv[i].destination, TYPE_STRING))) { - ds = data_string_init(); - } - - buffer_copy_string_buffer(ds->value, ((data_string *)(da->value->data[j]))->value); - if (!da->is_index_key) { - /* the id's were generated automaticly, as we copy now we might have to renumber them - * this is used to prepend server.modules by mod_indexfile as it has to be loaded - * before mod_fastcgi and friends */ - buffer_copy_string_buffer(ds->key, ((data_string *)(da->value->data[j]))->key); - } - - array_insert_unique(cv[i].destination, (data_unset *)ds); - } else { - log_error_write(srv, __FILE__, __LINE__, "sssd", - "the key of an array can only be a string or a integer, variable:", - cv[i].key, "type:", da->value->data[j]->type); - - return -1; - } - } - } else { - log_error_write(srv, __FILE__, __LINE__, "sss", "unexpected type for key: ", cv[i].key, "array of strings"); - - return -1; - } - break; - case T_CONFIG_STRING: - if (du->type == TYPE_STRING) { - data_string *ds = (data_string *)du; - - buffer_copy_string_buffer(cv[i].destination, ds->value); - } else if (du->type == TYPE_INTEGER) { - data_integer *di = (data_integer *)du; - - buffer_copy_long(cv[i].destination, di->value); - } else { - log_error_write(srv, __FILE__, __LINE__, "ssss", "unexpected type for key: ", cv[i].key, "(string)", "\"...\""); - - 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 (buffer_isdigit(ds->value)) { - *((unsigned short *)(cv[i].destination)) = strtol(ds->value->ptr, NULL, 10); - 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 (buffer_isdigit(ds->value)) { - *((unsigned int *)(cv[i].destination)) = strtol(ds->value->ptr, NULL, 10); - break; - } - - log_error_write(srv, __FILE__, __LINE__, "ssb", "got a string but expected a 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 a 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: - ERROR("found unsupported key in '%s' = '%s'", cv[i].key, (char *)(cv[i].destination)); - - srv->config_unsupported = 1; - - break; - case T_CONFIG_DEPRECATED: - ERROR("found deprecated key in '%s' = '%s'", 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[]) { - size_t i; - data_unset *du; - - for (i = 0; cv[i].key; i++) { - data_string *touched; - - if (NULL == (du = array_get_element(ca, cv[i].key, strlen(cv[i].key)))) { - /* no found */ - - continue; - } - - /* touched */ - touched = data_string_init(); - - buffer_copy_string_len(touched->value, CONST_STR_LEN("")); - buffer_copy_string_buffer(touched->key, du->key); - - array_insert_unique(srv->config_touched, (data_unset *)touched); - } - - return config_insert_values_internal(srv, ca, cv); -} - -unsigned short sock_addr_get_port(sock_addr *addr) { - switch (addr->plain.sa_family) { - case AF_INET: - return ntohs(addr->ipv4.sin_port); -#ifdef HAVE_IPV6 - case AF_INET6: - return ntohs(addr->ipv6.sin6_port); -#endif - default: - return 0; - } -} - -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; - /* check parent first */ - if (dc->parent && dc->parent->context_ndx) { - if (con->conf.log_condition_handling) { - TRACE("checking if the parent (%s) evaluates to 'true'", SAFE_BUF_STR(dc->parent->key)); - } - - switch (config_check_cond_cached(srv, con, dc->parent)) { - case COND_RESULT_FALSE: - return COND_RESULT_FALSE; - case COND_RESULT_UNSET: - return COND_RESULT_UNSET; - default: - break; - } - } - - if (dc->prev) { - if (con->conf.log_condition_handling) { - TRACE("triggering eval of successors of (%s) [in else]", SAFE_BUF_STR(dc->key)); - } - - /* make sure prev is checked first */ - config_check_cond_cached(srv, con, dc->prev); - - if (con->conf.log_condition_handling) { - TRACE("(%s) [in else] -> %s", SAFE_BUF_STR(dc->key), con->cond_cache[dc->context_ndx].result == COND_RESULT_FALSE ? "false" : "we will see"); - } - - switch (con->cond_cache[dc->context_ndx].result) { - case COND_RESULT_FALSE: /* one of prev set me to FALSE */ - return con->cond_cache[dc->context_ndx].result; - default: - break; - } - } - - if (!con->conditional_is_valid[dc->comp]) { - if (con->conf.log_condition_handling) { - TRACE("is condition [%d] (%s) already valid ? %s", - dc->comp, - SAFE_BUF_STR(dc->key), - con->conditional_is_valid[dc->comp] ? "yeah" : "nej"); - } - - return COND_RESULT_UNSET; - } - - /* pass the rules */ - - switch (dc->comp) { - case COMP_HTTP_HOST: { - char *ck_colon = NULL, *val_colon = NULL; - - if (!buffer_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: - 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_string_buffer(srv->cond_check_buf, l); - buffer_append_string_len(srv->cond_check_buf, CONST_STR_LEN(":")); - buffer_append_long(srv->cond_check_buf, sock_addr_get_port(&(srv_sock->addr))); - 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; - } -#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT - } else if (!buffer_is_empty(con->sock->tlsext_server_name)) { - l = con->sock->tlsext_server_name; -#endif - } 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) && - (con->dst_addr.plain.sa_family == AF_INET) && - (NULL != (nm_slash = strchr(dc->string->ptr, '/')))) { - int nm_bits; - long nm; - char *err; - struct in_addr val_inp; - - if (con->conf.log_condition_handling) { - l = srv->empty_string; - - log_error_write(srv, __FILE__, __LINE__, "bsbsb", dc->comp_key, - "(", l, ") compare to", dc->string); - } - - if (*(nm_slash+1) == '\0') { - log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", dc->string); - - return COND_RESULT_FALSE; - } - - nm_bits = strtol(nm_slash + 1, &err, 10); - - if (*err) { - log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", dc->string, *err); - - return COND_RESULT_FALSE; - } - - /* take IP convert to the native */ - buffer_copy_string_len(srv->cond_check_buf, dc->string->ptr, nm_slash - dc->string->ptr); -#ifdef _WIN32 - if (INADDR_NONE == (val_inp.s_addr = inet_addr(srv->cond_check_buf->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf); - - return COND_RESULT_FALSE; - } - -#else - if (0 == inet_aton(srv->cond_check_buf->ptr, &val_inp)) { - log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf); - - return COND_RESULT_FALSE; - } -#endif - - /* build netmask */ - nm = htonl(~((1 << (32 - nm_bits)) - 1)); - - if ((val_inp.s_addr & nm) == (con->dst_addr.ipv4.sin_addr.s_addr & nm)) { - 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; - } - } else { - 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_REFERER: { - data_string *ds; - - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Referer")))) { - l = ds->value; - } else { - l = srv->empty_string; - } - break; - } - case COMP_HTTP_COOKIE: { - data_string *ds; - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Cookie")))) { - l = ds->value; - } else { - l = srv->empty_string; - } - break; - } - case COMP_HTTP_USER_AGENT: { - data_string *ds; - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("User-Agent")))) { - l = ds->value; - } else { - l = srv->empty_string; - } - break; - } - case COMP_HTTP_REQUEST_METHOD: { - const char *method = get_http_method_name(con->request.http_method); - - /* we only have the request method as const char but we need a buffer for comparing */ - - buffer_copy_string(srv->tmp_buf, method); - - l = srv->tmp_buf; - - break; - } - case COMP_PHYSICAL_PATH_EXISTS: - case COMP_PHYSICAL_PATH: - l = con->physical.path; - 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) { - TRACE("'%s': '%s' is matched against '%s'", - SAFE_BUF_STR(dc->comp_key), - SAFE_BUF_STR(l), - SAFE_BUF_STR(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; - } - break; -#ifdef HAVE_PCRE_H - case CONFIG_COND_NOMATCH: - case CONFIG_COND_MATCH: { - cond_cache_t *cache = &con->cond_cache[dc->context_ndx]; - int n; - -#ifndef elementsof -#define elementsof(x) (sizeof(x) / sizeof(x[0])) -#endif - n = pcre_exec(dc->regex, dc->regex_study, l->ptr, l->used - 1, 0, 0, - cache->matches, elementsof(cache->matches)); - - cache->patterncount = n; - if (n > 0) { - cache->comp_value = l; - cache->comp_type = dc->comp; - - 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; - } - break; - } -#endif - 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) { - if (con->conf.log_condition_handling) { - TRACE("=== start of %d condition block ===", dc->context_ndx); - } - if (COND_RESULT_TRUE == (caches[dc->context_ndx].result = config_check_cond_nocache(srv, con, dc))) { - /* a node evaluted to true, all else nodes have to false */ - if (dc->next) { - data_config *c; - if (con->conf.log_condition_handling) { - TRACE("setting remains of chaining to %s", "false"); - } - for (c = dc->next; c; c = c->next) { - caches[c->context_ndx].result = COND_RESULT_FALSE; - } - } - } - caches[dc->context_ndx].comp_type = dc->comp; - - if (con->conf.log_condition_handling) { - TRACE("[%d] result: %s", - dc->context_ndx, - caches[dc->context_ndx].result == COND_RESULT_UNSET ? "unknown" : - (caches[dc->context_ndx].result == COND_RESULT_TRUE ? "true" : "false") - ); - } - } else { - if (con->conf.log_condition_cache_handling) { - TRACE("[%d] (cached) result: %s", - dc->context_ndx, - caches[dc->context_ndx].result == COND_RESULT_UNSET ? "unknown" : - (caches[dc->context_ndx].result == COND_RESULT_TRUE ? "true" : "false") - ); - } - } - - return caches[dc->context_ndx].result; -} - -/** - * 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++) { - if (item == COMP_LAST_ELEMENT || - con->cond_cache[i].comp_type == item) { - con->cond_cache[i].result = COND_RESULT_UNSET; - con->cond_cache[i].patterncount = 0; - con->cond_cache[i].comp_value = NULL; - } - } -} - -/** - * reset the config cache to its initial state at connection start - */ -void config_cond_cache_reset(server *srv, connection *con) { - size_t i; - - config_cond_cache_reset_all_items(srv, con); - - 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) { - return (config_check_cond_cached(srv, con, dc) == COND_RESULT_TRUE); -} - -int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n) -{ - cond_cache_t *cache = &con->cond_cache[dc->context_ndx]; - if (n >= cache->patterncount) { - return 0; - } - - n <<= 1; /* n *= 2 */ - buffer_append_string_len(buf, - cache->comp_value->ptr + cache->matches[n], - cache->matches[n + 1] - cache->matches[n]); - return 1; -} - -/* return <0 on error - * return 0-x if matched (and replaced) - */ -int config_exec_pcre_keyvalue_buffer(connection *con, pcre_keyvalue_buffer *kvb, data_config *context, buffer *match_buf, buffer *result) -{ -#ifdef HAVE_PCRE_H - pcre *match; - pcre_extra *extra; - const char *pattern; - size_t pattern_len; - int n; - size_t i; - pcre_keyvalue *kv; -# define N 10 - int ovec[N * 3]; - - for (i = 0; i < kvb->used; i++) { - kv = kvb->kv[i]; - - match = kv->key; - extra = kv->key_extra; - pattern = kv->value->ptr; - pattern_len = kv->value->used - 1; - - if ((n = pcre_exec(match, extra, match_buf->ptr, match_buf->used - 1, 0, 0, ovec, 3 * N)) < 0) { - if (n != PCRE_ERROR_NOMATCH) { - return n; - } - } else { - const char **list; - size_t start, end; - size_t k; - - /* it matched */ - pcre_get_substring_list(match_buf->ptr, ovec, n, &list); - - /* search for $[0-9] */ - - buffer_reset(result); - - start = 0; end = pattern_len; - for (k = 0; k < pattern_len; k++) { - if ((pattern[k] == '$' || pattern[k] == '%') && - isdigit((unsigned char)pattern[k + 1])) { - /* got one */ - - size_t num = pattern[k + 1] - '0'; - - end = k; - - buffer_append_string_len(result, pattern + start, end - start); - - if (pattern[k] == '$') { - /* n is always > 0 */ - if (num < (size_t)n) { - buffer_append_string(result, list[num]); - } - } else { - config_append_cond_match_buffer(con, context, result, num); - } - - k++; - start = k + 1; - } - } - - buffer_append_string_len(result, pattern + start, pattern_len - start); - - pcre_free(list); - - return i; - } - } - - return PCRE_ERROR_NOMATCH; -#undef N -#else - UNUSED(kvb); - return -2; -#endif -} - diff --git a/src/configfile.c b/src/configfile.c deleted file mode 100644 index 1254c270..00000000 --- a/src/configfile.c +++ /dev/null @@ -1,1376 +0,0 @@ -#include <sys/stat.h> - -#include <stdlib.h> -#include <fcntl.h> -#include <errno.h> -#include <string.h> -#include <stdio.h> -#include <ctype.h> -#include <assert.h> - -#include "server.h" -#include "log.h" -#include "stream.h" -#include "plugin.h" -#include "configparser.h" -#include "configfile.h" -#include "proc_open.h" -#include "fdevent.h" -#include "network_backends.h" - -#include "sys-files.h" -#include "sys-process.h" - -#ifndef PATH_MAX -/* win32 */ -#define PATH_MAX 64 -#endif - -static int config_insert(server *srv) { - size_t i; - int ret = 0; - buffer *stat_cache_string; - - 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_SERVER }, /* 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_SERVER }, /* 15 */ - { "debug.log-condition-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 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-404", 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 cant differ symlinks from files." - "Please remove server.follow-symlinks from your config.", - T_CONFIG_UNSUPPORTED, T_CONFIG_SCOPE_UNSET }, /* 24 */ -#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 */ - { "ssl.pemfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 29 */ - - { "ssl.engine", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 30 */ - - { "debug.log-file-not-found", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 31 */ - { "debug.log-request-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 32 */ - { "debug.log-response-header", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 33 */ - { "debug.log-request-header", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 34 */ - - { "server.protocol-http11", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 35 */ - { "debug.log-request-header-on-error", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 36 */ - { "debug.log-state-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 37 */ - { "ssl.ca-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 38 */ - - { "server.errorlog-use-syslog", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 39 */ - { "server.range-requests", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 40 */ - { "server.stat-cache-engine", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 41 */ - { "server.max-connections", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 42 */ - { "server.network-backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 43 */ - { "server.upload-dirs", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 44 */ - { "server.core-files", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 45 */ - { "debug.log-condition-cache-handling", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 46 */ - { "server.use-noatime", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 49 */ - { "server.max-stat-threads", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 48 */ - { "server.max-read-threads", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 49 */ - { "server.max-connection-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 50 */ - { "debug.log-timing", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 51 */ - { "ssl.cipher-list", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 52 */ - { "ssl.use-sslv2", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 53 */ - { "etag.use-inode", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 54 */ - { "etag.use-mtime", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 55 */ - { "etag.use-size", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 56 */ - { "server.breakagelog", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 57 */ - { "debug.log-timeouts", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 58 */ - { "debug.log-ssl-noise", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 59 */ - { "ssl.verifyclient.activate", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 60 */ - { "ssl.verifyclient.enforce", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 61 */ - { "ssl.verifyclient.depth", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 62 */ - { "ssl.verifyclient.username", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 63 */ - { "ssl.verifyclient.exportcert", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_SERVER }, /* 64 */ - - { "server.host", "use server.bind instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.docroot", "use server.document-root instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.virtual-root", "load mod_simple_vhost and use simple-vhost.server-root instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.virtual-default-host", "load mod_simple_vhost and use simple-vhost.default-host instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.virtual-docroot", "load mod_simple_vhost and use simple-vhost.document-root instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.userid", "use server.username instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.groupid", "use server.groupname instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.use-keep-alive", "use server.max-keep-alive-requests = 0 instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - { "server.force-lower-case-files", "use server.force-lowercase-filenames instead", T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_UNSET }, - - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - - /* 0 */ - 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[36].destination = &(srv->srvconf.log_request_header_on_error); - cv[37].destination = &(srv->srvconf.log_state_handling); - - cv[39].destination = &(srv->srvconf.errorlog_use_syslog); - - stat_cache_string = buffer_init(); - cv[41].destination = stat_cache_string; - cv[43].destination = srv->srvconf.network_backend; - cv[44].destination = srv->srvconf.upload_tempdirs; - cv[45].destination = &(srv->srvconf.enable_cores); - - cv[42].destination = &(srv->srvconf.max_conns); - cv[12].destination = &(srv->srvconf.max_request_size); - cv[47].destination = &(srv->srvconf.use_noatime); - cv[48].destination = &(srv->srvconf.max_stat_threads); - cv[49].destination = &(srv->srvconf.max_read_threads); - - cv[51].destination = &(srv->srvconf.log_timing); - cv[57].destination = srv->srvconf.breakagelog_file; - - srv->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - assert(srv->config_storage); - - for (i = 0; i < srv->config_context->used; i++) { - specific_config *s; - - s = calloc(1, sizeof(specific_config)); - assert(s); - s->document_root = buffer_init(); - s->mimetypes = array_init(); - s->server_name = buffer_init(); - s->ssl_pemfile = buffer_init(); - s->ssl_ca_file = buffer_init(); - s->error_handler = buffer_init(); - s->server_tag = buffer_init(); - s->errorfile_prefix = buffer_init(); - s->ssl_cipher_list = buffer_init(); - s->ssl_use_sslv2 = 1; - 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->max_keep_alive_requests = 16; - s->max_keep_alive_idle = 5; - s->max_read_idle = 60; - s->max_write_idle = 360; - s->max_connection_idle = 360; - s->use_xattr = 0; - s->is_ssl = 0; - s->use_ipv6 = 0; -#ifdef HAVE_LSTAT - s->follow_symlink = 1; -#endif - s->kbytes_per_second = 0; - s->allow_http11 = 1; - s->range_requests = 1; - s->etag_use_inode = 1; - s->etag_use_mtime = 1; - s->etag_use_size = 1; - s->force_lowercase_filenames = 0; - 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; - - cv[2].destination = s->errorfile_prefix; - - cv[7].destination = s->server_tag; - cv[8].destination = &(s->use_ipv6); - - - /* 13 max-worker */ - cv[14].destination = s->document_root; - cv[15].destination = &(s->force_lowercase_filenames); - cv[16].destination = &(s->log_condition_handling); - cv[46].destination = &(s->log_condition_cache_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 - /* 23 -> max-fds */ - 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->ssl_pemfile; - cv[30].destination = &(s->is_ssl); - - 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->allow_http11); - cv[38].destination = s->ssl_ca_file; - cv[40].destination = &(s->range_requests); - - cv[50].destination = &(s->max_connection_idle); - cv[52].destination = s->ssl_cipher_list; - cv[53].destination = &(s->ssl_use_sslv2); - - cv[54].destination = &(s->etag_use_inode); - cv[55].destination = &(s->etag_use_mtime); - cv[56].destination = &(s->etag_use_size); - cv[58].destination = &(s->log_timeouts); - cv[59].destination = &(s->log_ssl_noise); - - /* ssl.verify */ - cv[60].destination = &(s->ssl_verifyclient); - cv[61].destination = &(s->ssl_verifyclient_enforce); - cv[62].destination = &(s->ssl_verifyclient_depth); - cv[63].destination = s->ssl_verifyclient_username; - cv[64].destination = &(s->ssl_verifyclient_export_cert); - - srv->config_storage[i] = s; - - if (0 != (ret = config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv))) { - break; - } - } - - if (buffer_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 -#if defined(HAVE_SYS_INOTIFY_H) - } else if (buffer_is_equal_string(stat_cache_string, CONST_STR_LEN("inotify"))) { - srv->srvconf.stat_cache_engine = STAT_CACHE_ENGINE_INOTIFY; -#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 -#if defined(HAVE_SYS_INOTIFY_H) - ", \"inotify\"" -#endif - " but not:", stat_cache_string); - ret = HANDLER_ERROR; - } - - buffer_free(stat_cache_string); - - 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(allow_http11); - PATCH(mimetypes); - PATCH(document_root); - PATCH(max_keep_alive_requests); - PATCH(max_keep_alive_idle); - PATCH(max_read_idle); - PATCH(max_write_idle); - PATCH(max_connection_idle); - PATCH(use_xattr); - PATCH(error_handler); - 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_string_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_condition_cache_handling); - PATCH(log_file_not_found); - PATCH(log_ssl_noise); - PATCH(log_timeouts); - - PATCH(range_requests); - PATCH(force_lowercase_filenames); - PATCH(is_ssl); - - PATCH(ssl_pemfile); -#ifdef USE_OPENSSL - PATCH(ssl_ctx); -#endif - PATCH(ssl_ca_file); - PATCH(ssl_cipher_list); - PATCH(ssl_use_sslv2); - PATCH(etag_use_inode); - PATCH(etag_use_mtime); - PATCH(etag_use_size); - - PATCH(ssl_verifyclient); - PATCH(ssl_verifyclient_enforce); - PATCH(ssl_verifyclient_depth); - PATCH(ssl_verifyclient_username); - PATCH(ssl_verifyclient_export_cert); - - return 0; -} - -int config_patch_connection(server *srv, connection *con, comp_key_t comp) { - size_t i, j; - - con->conditional_is_valid[comp] = 1; - - /* 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-404"))) { - PATCH(error_handler); - } 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-connection-idle"))) { - PATCH(max_connection_idle); - } 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("ssl.pemfile"))) { - PATCH(ssl_pemfile); -#ifdef USE_OPENSSL - PATCH(ssl_ctx); -#endif - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.ca-file"))) { - PATCH(ssl_ca_file); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.engine"))) { - PATCH(is_ssl); - } 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.use-sslv2"))) { - PATCH(ssl_use_sslv2); - } 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_string_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("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-condition-cache-handling"))) { - PATCH(log_condition_cache_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-ssl-noise"))) { - PATCH(log_ssl_noise); - } 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); - } 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("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); - } - } - } - - - 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_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; - 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; - 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 '{': - 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]) - ); 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 (buffer_is_equal_string(token, CONST_STR_LEN("include"))) { - tid = TK_INCLUDE; - } else if (buffer_is_equal_string(token, CONST_STR_LEN("include_shell"))) { - tid = TK_INCLUDE_SHELL; - } else if (buffer_is_equal_string(token, CONST_STR_LEN("global"))) { - tid = TK_GLOBAL; - } else if (buffer_is_equal_string(token, CONST_STR_LEN("else"))) { - 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) { - fprintf(stderr, "%s.%d: %d, %s\n", - __FILE__, __LINE__, - tid, token->ptr); - } - 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 ); - lasttoken = buffer_init(); - token = buffer_init(); - - while((1 == (ret = config_tokenizer(srv, t, &token_id, token))) && context->ok) { - buffer_copy_string_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:", 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; -} - -int config_parse_file(server *srv, config_t *context, const char *fn) { - tokenizer_t t; - stream s; - int ret; - buffer *filename; - - if (buffer_is_empty(context->basedir) || - (fn[0] == '/' || fn[0] == '\\') || - (fn[0] == '.' && (fn[1] == '/' || fn[1] == '\\'))) { - filename = buffer_init_string(fn); - } else { - filename = buffer_init_buffer(context->basedir); - buffer_append_string(filename, fn); - } - - if (0 != stream_open(&s, filename)) { - log_error_write(srv, __FILE__, __LINE__, "sbss", - "opening configfile ", filename, "failed:", strerror(errno)); - ret = -1; - } else { - tokenizer_init(&t, filename, s.start, s.size); - ret = config_parse(srv, context, &t); - } - - stream_close(&s); - buffer_free(filename); - return ret; -} - -int config_parse_cmd(server *srv, config_t *context, const char *cmd) { - tokenizer_t t; - int ret; - buffer *source; - buffer *out; - char oldpwd[PATH_MAX]; - - if (NULL == getcwd(oldpwd, sizeof(oldpwd))) { - log_error_write(srv, __FILE__, __LINE__, "s", - "cannot get cwd", strerror(errno)); - return -1; - } - - source = buffer_init_string(cmd); - out = buffer_init(); - - if (!buffer_is_empty(context->basedir)) { - chdir(context->basedir->ptr); - } - - if (0 != proc_open_buffer(cmd, NULL, out, NULL)) { - log_error_write(srv, __FILE__, __LINE__, "sbss", - "opening", source, "failed:", strerror(errno)); - ret = -1; - } else { - tokenizer_init(&t, source, out->ptr, out->used); - ret = config_parse(srv, context, &t); - } - - buffer_free(source); - buffer_free(out); - chdir(oldpwd); - return ret; -} - -static void context_init(server *srv, config_t *context) { - context->srv = srv; - context->ok = 1; - context->configs_stack = buffer_ptr_init(NULL); - context->basedir = buffer_init(); -} - -static void context_free(config_t *context) { - buffer_ptr_free(context->configs_stack); - buffer_free(context->basedir); -} - -int config_read(server *srv, const char *fn) { - config_t context; - data_config *dc; - data_integer *dpid; - data_string *dcwd; - int ret; - char *pos; - data_array *modules; - - context_init(srv, &context); - context.all_configs = srv->config_context; - - /* use the current dir as basedir for all other includes - */ - pos = strrchr(fn, DIR_SEPERATOR); - - if (pos) { - buffer_copy_string_len(context.basedir, fn, pos - fn + 1); - fn = pos + 1; - } - - dc = data_config_init(); - buffer_copy_string_len(dc->key, CONST_STR_LEN("global")); - - 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 */ - srv->config = dc->value; - dpid = data_integer_init(); - dpid->value = getpid(); - buffer_copy_string_len(dpid->key, CONST_STR_LEN("var.PID")); - array_insert_unique(srv->config, (data_unset *)dpid); - - dcwd = data_string_init(); - buffer_prepare_copy(dcwd->value, 1024); - if (NULL != getcwd(dcwd->value->ptr, dcwd->value->size - 1)) { - dcwd->value->used = strlen(dcwd->value->ptr) + 1; - buffer_copy_string_len(dcwd->key, CONST_STR_LEN("var.CWD")); - array_insert_unique(srv->config, (data_unset *)dcwd); - } - - ret = config_parse_file(srv, &context, fn); - - /* remains nothing if parser is ok */ - assert(!(0 == ret && context.ok && 0 != context.configs_stack->used)); - context_free(&context); - - if (0 != ret) { - return ret; - } - - if (NULL != (dc = (data_config *)array_get_element(srv->config_context, CONST_STR_LEN("global")))) { - srv->config = dc->value; - } else { - return -1; - } - - if (NULL != (modules = (data_array *)array_get_element(srv->config, CONST_STR_LEN("server.modules")))) { - data_string *ds; - data_array *prepends; - int prepend_mod_indexfile = 1; - int append_mod_dirlisting = 1; - int append_mod_staticfile = 1; - int append_mod_chunked = 1; - size_t i; - - if (modules->type != TYPE_ARRAY) { - fprintf(stderr, "server.modules must be an array"); - return -1; - } - - prepends = data_array_init(); - - /* prepend default modules */ - for (i = 0; i < modules->value->used; i++) { - ds = (data_string *)modules->value->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_chunked"))) { - append_mod_chunked = 0; - } - - if (0 == prepend_mod_indexfile && - 0 == append_mod_dirlisting && - 0 == append_mod_staticfile && - 0 == append_mod_chunked) { - - break; - } - } - - if (prepend_mod_indexfile) { - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_indexfile")); - array_insert_unique(prepends->value, (data_unset *)ds); - } - - prepends = (data_array *)configparser_merge_data((data_unset *)prepends, (data_unset *)modules); - buffer_copy_string_buffer(prepends->key, modules->key); - array_replace(srv->config, (data_unset *)prepends); - modules->free((data_unset *)modules); - modules = prepends; - - /* append default modules */ - if (append_mod_dirlisting) { - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_dirlisting")); - array_insert_unique(modules->value, (data_unset *)ds); - } - - if (append_mod_staticfile) { - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_staticfile")); - array_insert_unique(modules->value, (data_unset *)ds); - } - - if (append_mod_chunked) { - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_chunked")); - array_insert_unique(modules->value, (data_unset *)ds); - } - } else { - data_string *ds; - - modules = data_array_init(); - - /* server.modules is not set */ - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_indexfile")); - array_insert_unique(modules->value, (data_unset *)ds); - - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_dirlisting")); - array_insert_unique(modules->value, (data_unset *)ds); - - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_staticfile")); - array_insert_unique(modules->value, (data_unset *)ds); - - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN("mod_chunked")); - array_insert_unique(modules->value, (data_unset *)ds); - - buffer_copy_string_len(modules->key, CONST_STR_LEN("server.modules")); - array_insert_unique(srv->config, (data_unset *)modules); - } - - - if (0 != config_insert(srv)) { - return -1; - } - - return 0; -} - - -int config_set_defaults(server *srv) { - specific_config *s = srv->config_storage[0]; - const fdevent_handler_info_t *handler; - const network_backend_info_t *backend; - struct stat st1, st2; - - if (buffer_is_empty(s->document_root)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "a default document-root has to be set"); - - return -1; - } - - if (buffer_is_empty(srv->srvconf.changeroot)) { - pathname_unix2local(s->document_root); - if (-1 == stat(s->document_root->ptr, &st1)) { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "base-docroot doesn't exist:", - s->document_root, strerror(errno)); - return -1; - } - - } else { - buffer_copy_string_buffer(srv->tmp_buf, srv->srvconf.changeroot); - buffer_append_string_buffer(srv->tmp_buf, s->document_root); - - if (-1 == stat(srv->tmp_buf->ptr, &st1)) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "base-docroot doesn't exist:", - srv->tmp_buf); - return -1; - } - - } - - buffer_copy_string_buffer(srv->tmp_buf, s->document_root); - - buffer_to_lower(srv->tmp_buf); - - 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_string_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->is_ssl ? 443 : 80; - } - - if (srv->srvconf.event_handler->used == 0) { - /* get a useful default */ - handler = fdevent_get_defaulthandler(); - if (!handler) { - log_error_write(srv, __FILE__, __LINE__, "s", - "sorry, there is no event handler for this system"); - - return -1; - } - } else { - /* - * User override - */ - - handler = fdevent_get_handler_info_by_name(srv->srvconf.event_handler->ptr); - if (!handler) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "the selected event-handler is unknown:", - srv->srvconf.event_handler ); - - return -1; - } - - if (!handler->init) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "the selected event-handler is known but not supported:", - srv->srvconf.event_handler ); - - return -1; - } - } - srv->event_handler = handler->type; - - if (buffer_is_empty(srv->srvconf.network_backend)) { - /* get a useful default */ - backend = network_get_defaultbackend(); - if (!backend) { - log_error_write(srv, __FILE__, __LINE__, "s", - "sorry, there is no network backend for this system"); - - return -1; - } - } else { - /* - * User override - */ - - backend = network_get_backend_info_by_name(srv->srvconf.network_backend->ptr); - if (!backend) { - /* we don't know it */ - - log_error_write(srv, __FILE__, __LINE__, "sb", - "server.network-backend has a unknown value:", - srv->srvconf.network_backend); - - return -1; - } - - if (backend->write_handler == NULL) { - /* we know it but not supported */ - - log_error_write(srv, __FILE__, __LINE__, "sb", - "server.network-backend not supported:", - srv->srvconf.network_backend); - - return -1; - } - } - srv->network_backend = backend->type; - - if (s->is_ssl) { - if (buffer_is_empty(s->ssl_pemfile)) { - /* PEM file is require */ - - log_error_write(srv, __FILE__, __LINE__, "s", - "ssl.pemfile has to be set"); - return -1; - } - -#ifndef USE_OPENSSL - log_error_write(srv, __FILE__, __LINE__, "s", - "ssl support is missing, recompile with --with-openssl"); - - return -1; -#endif - } - - return 0; -} diff --git a/src/configfile.h b/src/configfile.h deleted file mode 100644 index 5694517d..00000000 --- a/src/configfile.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _CONFIG_PARSER_H_ -#define _CONFIG_PARSER_H_ - -#include "settings.h" -#include "server.h" -#include "array.h" -#include "buffer.h" - -typedef struct { - server *srv; - int ok; - array *all_configs; - buffer_ptr *configs_stack; /* to parse nested block */ - data_config *current; /* current started with { */ - buffer *basedir; -} config_t; - -LI_API void * configparserAlloc(void *(*mallocProc)(size_t)); -LI_API void configparserFree(void *p, void (*freeProc)(void*)); -LI_API void configparser(void *yyp, int yymajor, buffer *yyminor, config_t *ctx); -LI_API int config_parse_file(server *srv, config_t *context, const char *fn); -LI_API int config_parse_cmd(server *srv, config_t *context, const char *cmd); -LI_API data_unset * configparser_merge_data(data_unset *op1, const data_unset *op2); -LI_API void config_cond_cache_reset(server *srv, connection *con); -LI_API void config_cond_cache_reset_item(server *srv, connection *con, comp_key_t item); - -#define config_cond_cache_reset_all_items(srv, con) \ - config_cond_cache_reset_item(srv, con, COMP_LAST_ELEMENT); - -#endif diff --git a/src/configparser.y b/src/configparser.y deleted file mode 100644 index 3d8caa57..00000000 --- a/src/configparser.y +++ /dev/null @@ -1,571 +0,0 @@ -%token_prefix TK_ -%extra_argument {config_t *ctx} -%name configparser - -%include { -#include <assert.h> -#include <stdio.h> -#include <string.h> -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include "configfile.h" -#include "buffer.h" -#include "array.h" - -static void configparser_push(config_t *ctx, data_config *dc, int isnew) { - if (isnew) { - dc->context_ndx = ctx->all_configs->used; - assert(dc->context_ndx > ctx->current->context_ndx); - array_insert_unique(ctx->all_configs, (data_unset *)dc); - dc->parent = ctx->current; - array_insert_unique(dc->parent->childs, (data_unset *)dc); - } - buffer_ptr_append(ctx->configs_stack, (void *)ctx->current); - ctx->current = dc; -} - -static data_config *configparser_pop(config_t *ctx) { - data_config *old = ctx->current; - ctx->current = (data_config *) buffer_ptr_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(dc->value, CONST_BUF_LEN(key)))) { - return du->copy(du); - } - } - return NULL; -} - -/* op1 is to be eat/return by this function, 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_long(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_long(ds->value, ((data_integer*)op1)->value); - buffer_append_string_buffer(ds->value, ((data_string*)op2)->value); - op1->free(op1); - return (data_unset *)ds; - } else { - fprintf(stderr, "data type mismatch, cannot be merge\n"); - op1->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) { - array_insert_unique(dst, du->copy(du)); - } - } - break; - default: - assert(0); - break; - } - } - return op1; -} - -} - -%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 condlines {data_config *} -%type global {data_config *} -%type aelements {array *} -%type array {array *} -%type key {buffer *} -%type stringop {buffer *} - -%type cond {config_cond_t } - -%destructor value { $$->free($$); } -%destructor expression { $$->free($$); } -%destructor aelement { $$->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). { - buffer_copy_string_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(ctx->current->value, CONST_BUF_LEN(B->key))) { - array_insert_unique(ctx->current->value, B); - B = NULL; - } else { - if (0 == strcmp(B->key->ptr, "var.PID") || - 0 == strcmp(B->key->ptr, "var.CWD")) { - fprintf(stderr, "var.PID and var.CWD are magic config vars and can't be set in the config file itself\n"); - } 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; - B->free(B); - B = NULL; - } - buffer_free(A); - A = NULL; -} - -varline ::= key(A) APPEND expression(B). { - 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_get_element(vars, CONST_BUF_LEN(A)))) { - /* exists in current block */ - du = configparser_merge_data(du, B); - if (NULL == du) { - ctx->ok = 0; - } - else { - buffer_copy_string_buffer(du->key, A); - array_replace(vars, du); - } - B->free(B); - } else if (NULL != (du = configparser_get_variable(ctx, A))) { - du = configparser_merge_data(du, B); - if (NULL == du) { - ctx->ok = 0; - } - else { - buffer_copy_string_buffer(du->key, A); - array_insert_unique(ctx->current->value, du); - } - B->free(B); - } else { - buffer_copy_string_buffer(B->key, A); - array_insert_unique(ctx->current->value, B); - } - buffer_free(A); - A = NULL; - B = NULL; -} - -key(A) ::= LKEY(B). { - if (strchr(B->ptr, '.') == NULL) { - /* prepend the user-vars with var. */ - A = buffer_init_string("var."); - buffer_append_string_buffer(A, B); - buffer_free(B); - B = NULL; - } else { - A = B; - B = NULL; - } -} - -expression(A) ::= expression(B) PLUS value(C). { - A = configparser_merge_data(B, C); - if (NULL == A) { - ctx->ok = 0; - } - B = NULL; - C->free(C); - C = NULL; -} - -expression(A) ::= value(B). { - A = B; - B = NULL; -} - -value(A) ::= key(B). { - 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 { - A = NULL; - 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; - } - if (!A) { - /* make a dummy so it won't crash */ - A = (data_unset *)data_string_init(); - } - buffer_free(B); - B = NULL; -} - -value(A) ::= STRING(B). { - A = (data_unset *)data_string_init(); - buffer_copy_string_buffer(((data_string *)(A))->value, B); - buffer_free(B); - B = NULL; -} - -value(A) ::= INTEGER(B). { - A = (data_unset *)data_integer_init(); - ((data_integer *)(A))->value = strtol(B->ptr, NULL, 10); - 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). { - if (buffer_is_empty(B->key) || - NULL == array_get_element(C, CONST_BUF_LEN(B->key))) { - array_insert_unique(C, B); - B = NULL; - } else { - fprintf(stderr, "Duplicate array-key: %s\n", - B->key->ptr); - ctx->ok = 0; - B->free(B); - B = NULL; - } - - A = C; - C = NULL; -} - -aelements(A) ::= aelements(C) COMMA. { - A = C; - C = NULL; -} - -aelements(A) ::= aelement(B). { - A = array_init(); - array_insert_unique(A, B); - B = NULL; -} - -aelement(A) ::= expression(B). { - A = B; - B = NULL; -} -aelement(A) ::= stringop(B) ARRAY_ASSIGN expression(C). { - buffer_copy_string_buffer(C->key, B); - buffer_free(B); - B = NULL; - - A = C; - C = NULL; -} - -eols ::= EOL. -eols ::= . - -globalstart ::= GLOBAL. { - data_config *dc; - dc = (data_config *)array_get_element(ctx->srv->config_context, CONST_STR_LEN("global")); - assert(dc); - configparser_push(ctx, dc, 0); -} - -global(A) ::= globalstart LCURLY metalines RCURLY. { - data_config *cur; - - cur = ctx->current; - configparser_pop(ctx); - - assert(cur && ctx->current); - - A = cur; -} - -condlines(A) ::= condlines(B) eols ELSE condline(C). { - assert(B->context_ndx < C->context_ndx); - C->prev = B; - B->next = C; - A = C; - B = NULL; - C = NULL; -} - -condlines(A) ::= condline(B). { - A = B; - B = NULL; -} - -condline(A) ::= context LCURLY metalines RCURLY. { - data_config *cur; - - cur = ctx->current; - configparser_pop(ctx); - - assert(cur && ctx->current); - - A = cur; -} - -context ::= DOLLAR SRVVARNAME(B) LBRACKET stringop(C) RBRACKET cond(E) expression(D). { - data_config *dc; - buffer *b, *rvalue, *op; - - if (ctx->ok && D->type != TYPE_STRING) { - fprintf(stderr, "rvalue must be string"); - ctx->ok = 0; - } - - 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: - assert(0); - return; - } - - b = buffer_init(); - buffer_copy_string_buffer(b, ctx->current->key); - buffer_append_string(b, "/"); - 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(ctx->all_configs, CONST_BUF_LEN(b)))) { - configparser_push(ctx, dc, 0); - } else { - struct { - comp_key_t comp; - char *comp_key; - size_t len; - } comps[] = { - { COMP_SERVER_SOCKET, CONST_STR_LEN("SERVER[\"socket\"]" ) }, - { COMP_HTTP_SCHEME, CONST_STR_LEN("HTTP[\"scheme\"]" ) }, - { COMP_HTTP_URL, CONST_STR_LEN("HTTP[\"url\"]" ) }, - { COMP_HTTP_HOST, CONST_STR_LEN("HTTP[\"host\"]" ) }, - { COMP_HTTP_REFERER, CONST_STR_LEN("HTTP[\"referer\"]" ) }, - { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"useragent\"]" ) }, - { COMP_HTTP_USER_AGENT, CONST_STR_LEN("HTTP[\"user-agent\"]" ) }, - { COMP_HTTP_COOKIE, 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_PHYSICAL_PATH, CONST_STR_LEN("PHYSICAL[\"path\"]") }, - { COMP_PHYSICAL_PATH_EXISTS,CONST_STR_LEN("PHYSICAL[\"existing-path\"]") }, - - { COMP_UNSET, NULL, 0 }, - }; - size_t i; - - dc = data_config_init(); - - buffer_copy_string_buffer(dc->key, b); - buffer_copy_string_buffer(dc->op, op); - buffer_copy_string_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) { - fprintf(stderr, "error comp_key %s", dc->comp_key->ptr); - ctx->ok = 0; - } - - switch(E) { - case CONFIG_COND_NE: - case CONFIG_COND_EQ: - dc->string = buffer_init_buffer(rvalue); - break; - case CONFIG_COND_NOMATCH: - case CONFIG_COND_MATCH: { -#ifdef HAVE_PCRE_H - const char *errptr; - int erroff; - - if (NULL == (dc->regex = - pcre_compile(rvalue->ptr, 0, &errptr, &erroff, NULL))) { - dc->string = buffer_init_string(errptr); - dc->cond = CONFIG_COND_UNSET; - - fprintf(stderr, "parsing regex failed: %s -> %s at offset %d\n", - rvalue->ptr, errptr, erroff); - - ctx->ok = 0; - } else if (NULL == (dc->regex_study = - pcre_study(dc->regex, 0, &errptr)) && - errptr != NULL) { - fprintf(stderr, "studying regex failed: %s -> %s\n", - rvalue->ptr, errptr); - ctx->ok = 0; - } else { - dc->string = buffer_init_buffer(rvalue); - } -#else - fprintf(stderr, "can't handle '$%s[%s] =~ ...' as you compiled without pcre support. \n" - "(perhaps just a missing pcre-devel package ?) \n", - B->ptr, C->ptr); - ctx->ok = 0; -#endif - break; - } - - default: - fprintf(stderr, "unknown condition for $%s[%s]\n", - B->ptr, C->ptr); - ctx->ok = 0; - break; - } - - configparser_push(ctx, dc, 1); - } - - buffer_free(b); - buffer_free(op); - buffer_free(B); - B = NULL; - buffer_free(C); - C = NULL; - D->free(D); - D = NULL; -} -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 = buffer_init_buffer(((data_string*)B)->value); - } else if (B->type == TYPE_INTEGER) { - A = buffer_init(); - buffer_copy_long(A, ((data_integer *)B)->value); - } else { - fprintf(stderr, "operand must be string"); - ctx->ok = 0; - } - } - B->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/src/connections-glue.c b/src/connections-glue.c deleted file mode 100644 index fa5cf36f..00000000 --- a/src/connections-glue.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "base.h" -#include "connections.h" - -const char *connection_get_state(connection_state_t state) { - switch (state) { - case CON_STATE_CONNECT: return "connect"; - - case CON_STATE_REQUEST_START: return "req-start"; - case CON_STATE_READ_REQUEST_HEADER: return "read-header"; - case CON_STATE_HANDLE_REQUEST_HEADER: return "handle-req"; - case CON_STATE_READ_REQUEST_CONTENT: return "read-content"; - - case CON_STATE_HANDLE_RESPONSE_HEADER: return "resp-start"; - case CON_STATE_WRITE_RESPONSE_HEADER: return "write-header"; - case CON_STATE_WRITE_RESPONSE_CONTENT: return "write-content"; - case CON_STATE_RESPONSE_END: return "resp-end"; - - case CON_STATE_CLOSE: return "close"; - case CON_STATE_ERROR: return "error"; - default: return "(unknown)"; - } -} - -const char *connection_get_short_state(connection_state_t state) { - switch (state) { - case CON_STATE_CONNECT: return "."; - case CON_STATE_REQUEST_START: return "q"; - - case CON_STATE_READ_REQUEST_HEADER: return "r"; - case CON_STATE_HANDLE_REQUEST_HEADER: return "h"; - case CON_STATE_READ_REQUEST_CONTENT: return "R"; - - case CON_STATE_HANDLE_RESPONSE_HEADER: return "s"; - case CON_STATE_WRITE_RESPONSE_HEADER: return "w"; - case CON_STATE_WRITE_RESPONSE_CONTENT: return "W"; - case CON_STATE_RESPONSE_END: return "S"; - - case CON_STATE_CLOSE: return "C"; - case CON_STATE_ERROR: return "E"; - default: return "x"; - } -} - -int connection_set_state(server *srv, connection *con, connection_state_t state) { - UNUSED(srv); - - con->state = state; - - return 0; -} - diff --git a/src/connections.c b/src/connections.c deleted file mode 100644 index 1069adab..00000000 --- a/src/connections.c +++ /dev/null @@ -1,1599 +0,0 @@ -#include <sys/stat.h> - -#include <stdlib.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <fcntl.h> -#include <assert.h> - -#include "settings.h" - -#include "server.h" -#include "connections.h" -#include "fdevent.h" -#include "log.h" - -#include "request.h" -#include "response.h" -#include "network.h" -#include "stat_cache.h" -#include "joblist.h" - -#include "plugin.h" - -#include "inet_ntop_cache.h" -#include "configfile.h" -#include "http_req.h" - -#ifdef USE_OPENSSL -# include <openssl/ssl.h> -# include <openssl/err.h> -#endif - -#ifdef HAVE_SYS_FILIO_H -# include <sys/filio.h> -#endif - -#include "sys-socket.h" -#include "sys-files.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 == 0) { - conns->size = 128; - conns->ptr = NULL; - conns->ptr = malloc(sizeof(*conns->ptr) * conns->size); - for (i = 0; i < conns->size; i++) { - conns->ptr[i] = connection_init(srv); - } - } else if (conns->size == conns->used) { - conns->size += 128; - conns->ptr = realloc(conns->ptr, sizeof(*conns->ptr) * conns->size); - - for (i = conns->used; i < conns->size; i++) { - conns->ptr[i] = connection_init(srv); - } - } - - connection_reset(srv, conns->ptr[conns->used]); - - 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; - - 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; - - return 0; -} - -int connection_close(server *srv, connection *con) { -#ifdef USE_OPENSSL - /* should be in iosocket_close() */ - - if (con->sock->ssl) { - int ret, ssl_r; - unsigned long err; - - ERR_clear_error(); - switch (ret = SSL_shutdown(con->sock->ssl)) { - case 1: - /* done */ - break; - case 0: - /* wait for fd-event - * - * FIXME: wait for fdevent and call SSL_shutdown again - * (But it is not that important as we close the underlying connection anyway) - */ - - break; - default: - switch ((ssl_r = SSL_get_error(con->sock->ssl, ret))) { - case SSL_ERROR_WANT_WRITE: - 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 { - ERROR("SSL_shutdown failed (%i, %i): %s", ssl_r, ret, ERR_error_string(err, NULL)); - } while((err = ERR_get_error())); - } else { - ERROR("SSL_shutdown failed (%i, %i, %i): %s", ssl_r, ret, errno, strerror(errno)); - } - - break; - default: - while((err = ERR_get_error())) { - ERROR("SSL_shutdown failed (%i, %i): %s", ssl_r, ret, ERR_error_string(err, NULL)); - } - } - } - - SSL_free(con->sock->ssl); - ERR_clear_error(); - con->sock->ssl = NULL; - } -#endif - - fdevent_event_del(srv->ev, con->sock); - fdevent_unregister(srv->ev, con->sock); - - if (closesocket(con->sock->fd)) { - ERROR("close failed (%i): %s", con->sock->fd, strerror(errno)); - } - - connection_del(srv, con); - connection_set_state(srv, con, CON_STATE_CONNECT); - - return 0; -} - -#if 0 -static void dump_packet(const unsigned char *data, size_t len) { - size_t i, j; - - if (len == 0) return; - - for (i = 0; i < len; i++) { - if (i % 16 == 0) fprintf(stderr, " "); - - fprintf(stderr, "%02x ", data[i]); - - if ((i + 1) % 16 == 0) { - fprintf(stderr, " "); - for (j = 0; j <= i % 16; j++) { - unsigned char c; - - if (i-15+j >= len) break; - - c = data[i-15+j]; - - fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); - } - - fprintf(stderr, "\n"); - } - } - - if (len % 16 != 0) { - for (j = i % 16; j < 16; j++) { - fprintf(stderr, " "); - } - - fprintf(stderr, " "); - for (j = i & ~0xf; j < len; j++) { - unsigned char c; - - c = data[j]; - fprintf(stderr, "%c", c > 32 && c < 128 ? c : '.'); - } - fprintf(stderr, "\n"); - } -} -#endif - -static int connection_handle_response_header(server *srv, connection *con) { - int no_response_body = 0; - - if (con->mode == DIRECT) { - /* static files */ - switch(con->request.http_method) { - case HTTP_METHOD_GET: - case HTTP_METHOD_POST: - case HTTP_METHOD_HEAD: - /* webdav */ - case HTTP_METHOD_PUT: - case HTTP_METHOD_MKCOL: - case HTTP_METHOD_DELETE: - case HTTP_METHOD_COPY: - case HTTP_METHOD_MOVE: - case HTTP_METHOD_PROPFIND: - case HTTP_METHOD_PROPPATCH: - case HTTP_METHOD_LOCK: - case HTTP_METHOD_UNLOCK: - 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 || con->http_status == 403) && - con->uri.path->used && con->uri.path->ptr[0] != '*') { - response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); - - /* trash the content */ - no_response_body = 1; - - con->http_status = 200; - } - break; - default: - switch(con->http_status) { - case 400: /* bad request */ - case 414: /* overload request header */ - case 505: /* unknown protocol */ - case 207: /* this was webdav */ - break; - default: - con->http_status = 501; - break; - } - break; - } - } - - if (con->http_status == 0) { - TRACE("%s", "no status, setting 403"); - con->http_status = 403; - } - - switch(con->http_status) { - case 400: /* class: header + custom body */ - case 401: - case 403: - case 404: - case 408: - case 409: - case 410: - case 411: - case 416: - case 423: - case 500: - case 501: - case 502: - case 503: - case 504: - case 505: - case 509: - if (con->mode != DIRECT) break; - - con->send->is_closed = 0; - con->response.content_length = -1; - - buffer_reset(con->physical.path); - - /* try to send static errorfile */ - if (!buffer_is_empty(con->conf.errorfile_prefix)) { - stat_cache_entry *sce = NULL; - - buffer_copy_string_buffer(con->physical.path, con->conf.errorfile_prefix); - buffer_append_string(con->physical.path, get_http_status_body_name(con->http_status)); - - if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { - chunkqueue_append_file(con->send, con->physical.path, 0, sce->st.st_size); - con->send->bytes_in += sce->st.st_size; - con->send->is_closed = 1; - if (buffer_is_empty(sce->content_type)) { - /* for error docs default to html instead of application-data */ - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); - } else { - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); - } - } - } - - if (!con->send->is_closed) { - buffer *b; - - buffer_reset(con->physical.path); - - con->send->is_closed = 1; - b = chunkqueue_get_append_buffer(con->send); - - /* 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>")); - buffer_append_long(b, con->http_status); - buffer_append_string_len(b, CONST_STR_LEN(" - ")); - buffer_append_string(b, get_http_status_name(con->http_status)); - - buffer_append_string(b, - "</title>\n" - " </head>\n" - " <body>\n" - " <h1>"); - buffer_append_long(b, con->http_status); - buffer_append_string_len(b, CONST_STR_LEN(" - ")); - buffer_append_string(b, get_http_status_name(con->http_status)); - - buffer_append_string(b,"</h1>\n" - " </body>\n" - "</html>\n" - ); - - con->send->bytes_in += b->used - 1; - - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); - } - /* fall through */ - case 207: - case 200: /* class: header + body */ - case 201: - case 301: - case 302: - case 303: - break; - - case 206: /* write_queue is already prepared */ - break; - case 205: /* class: header only */ - case 304: - default: - if (con->mode == DIRECT) { - /* only if we have handled the request internally - * we will see no response-content - * - * if it was a fastcgi request we will see a END_REQUEST packet - * after the header was parsed. - * - * */ - no_response_body = 1; - } - break; - } - - if (no_response_body) { - /* disable chunked encoding again as we have no body */ - con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; - chunkqueue_reset(con->send); - - con->send->is_closed = 1; - } - - return 0; -} - -connection *connection_init(server *srv) { - connection *con; - - UNUSED(srv); - - con = calloc(1, sizeof(*con)); - - con->sock = iosocket_init(); - con->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); - CLEAN(request.pathinfo); - CLEAN(request.http_host); - - 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(authed_user); - CLEAN(server_name); - CLEAN(error_handler); - CLEAN(dst_addr_buf); - -#undef CLEAN - con->send_filters = filter_chain_init(); - /* send is the chunkqueue of the first send filter */ - con->send = con->send_filters->first->cq; - con->recv = chunkqueue_init(); - chunkqueue_set_tempdirs(con->recv, srv->srvconf.upload_tempdirs); - - con->send_raw = chunkqueue_init(); - con->recv_raw = chunkqueue_init(); - - con->request.headers = array_init(); - con->response.headers = array_init(); - con->environment = array_init(); - - con->http_req = http_request_init(); - - /* init plugin specific connection structures */ - - con->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *)); - - con->cond_cache = calloc(srv->config_context->used, sizeof(cond_cache_t)); - config_setup_connection(srv, con); - - return con; -} - -void connections_free(server *srv) { - connections *conns = srv->conns; - size_t i; - - for (i = 0; i < conns->size; i++) { - connection *con = conns->ptr[i]; - - connection_reset(srv, con); - iosocket_free(con->sock); - - filter_chain_free(con->send_filters); - con->send = NULL; - chunkqueue_free(con->recv); - chunkqueue_free(con->send_raw); - chunkqueue_free(con->recv_raw); - 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); - CLEAN(request.pathinfo); - CLEAN(request.http_host); - - 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(authed_user); - CLEAN(server_name); - CLEAN(error_handler); - CLEAN(dst_addr_buf); -#undef CLEAN - free(con->plugin_ctx); - free(con->cond_cache); - - http_request_free(con->http_req); - - free(con); - } - - free(conns->ptr); -} - - -int connection_reset(server *srv, connection *con) { - size_t i; - - plugins_call_connection_reset(srv, con); - - con->is_readable = 1; - con->is_writable = 1; - con->http_status = 0; - con->file_started = 0; - con->got_response = 0; - - 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; - con->request.content_length = -1; - - con->response.keep_alive = 0; - con->response.content_length = -1; - con->response.transfer_encoding = 0; - - con->mode = DIRECT; - -#define CLEAN(x) \ - if (con->x) buffer_reset(con->x); - - CLEAN(request.uri); - CLEAN(request.pathinfo); - CLEAN(request.request); - CLEAN(request.http_host); - - 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(authed_user); - CLEAN(server_name); - CLEAN(error_handler); -#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT - CLEAN(sock->tlsext_server_name); -#endif -#undef CLEAN - -#define CLEAN(x) \ - if (con->x) con->x->used = 0; - -#undef CLEAN - - array_reset(con->request.headers); - array_reset(con->response.headers); - array_reset(con->environment); - - filter_chain_reset(con->send_filters); - con->send = con->send_filters->first->cq; - chunkqueue_reset(con->recv); - chunkqueue_reset(con->send_raw); - - http_request_reset(con->http_req); - - /* the plugins should cleanup themself */ - for (i = 0; i < srv->plugins.used; i++) { - plugin *p = ((plugin **)(srv->plugins.ptr))[i]; - plugin_data *pd = p->data; - - if (!pd) continue; - - if (con->plugin_ctx[pd->id] != NULL) { - ERROR("missing cleanup in %s", SAFE_BUF_STR(p->name)); - } - - con->plugin_ctx[pd->id] = NULL; - } - - config_cond_cache_reset(srv, con); - - con->header_len = 0; - con->in_error_handler = 0; - - config_setup_connection(srv, con); - - return 0; -} - -/** - * handle all header and content read - * - * we get called by the state-engine and by the fdevent-handler - */ -static handler_t connection_handle_read_request_header(server *srv, connection *con) { - /* let's see if we need more data later */ - fdevent_event_del(srv->ev, con->sock); - - con->read_idle_ts = srv->cur_ts; /* start a read-call() */ - - /* read from the network */ - switch (network_read(srv, con, con->sock, con->recv_raw)) { - case NETWORK_STATUS_SUCCESS: - /* we read everything from the socket, do we have a full header ? */ - break; - case NETWORK_STATUS_WAIT_FOR_EVENT: - fdevent_event_add(srv->ev, con->sock, FDEVENT_IN); - return HANDLER_WAIT_FOR_EVENT; - case NETWORK_STATUS_CONNECTION_CLOSE: - /* the connection went away before we got something back */ - con->close_timeout_ts = srv->cur_ts - 2; - connection_set_state(srv, con, CON_STATE_CLOSE); - - return HANDLER_GO_ON; - default: - ERROR("++ %s", "oops, something went wrong while reading"); - return HANDLER_ERROR; - } - - switch (http_request_parse_cq(con->recv_raw, con->http_req)) { - case PARSE_ERROR: - con->http_status = 400; /* the header is broken */ - con->send->is_closed = 1; /* we have nothing to send */ - - chunkqueue_remove_finished_chunks(con->recv_raw); - - return HANDLER_FINISHED; - case PARSE_NEED_MORE: - /* we need more */ - fdevent_event_add(srv->ev, con->sock, FDEVENT_IN); - - return HANDLER_WAIT_FOR_EVENT; - case PARSE_SUCCESS: - chunkqueue_remove_finished_chunks(con->recv_raw); - break; - default: - chunkqueue_remove_finished_chunks(con->recv_raw); - TRACE("%s", "(error)"); - return HANDLER_ERROR; - } - - return HANDLER_GO_ON; -} - -/* decode the HTTP/1.1 chunk encoding */ - -static handler_t connection_handle_read_request_content(server *srv, connection *con) { - /* read data from the socket and push it to the backend */ - - chunkqueue *in = con->recv_raw; - chunkqueue *out = con->recv; /* the pure content */ - chunk *c; - - /* let's see if we need more data later */ - fdevent_event_del(srv->ev, con->sock); - - con->read_idle_ts = srv->cur_ts; /* start a read-call() */ - - if (con->request.content_length == -1) return HANDLER_GO_ON; - - /* if the content was short enough, it might be read already */ - if (in->first && - chunkqueue_length(in) - in->first->offset > 0) { - - /* - * looks like the request-header also had some content for us - */ - - } else { - /* read from the network */ - switch (network_read(srv, con, con->sock, in)) { - case NETWORK_STATUS_SUCCESS: - /* we have data */ - break; - case NETWORK_STATUS_WAIT_FOR_EVENT: - fdevent_event_add(srv->ev, con->sock, FDEVENT_IN); - return HANDLER_WAIT_FOR_EVENT; - case NETWORK_STATUS_CONNECTION_CLOSE: - /* the connection went away before we got something back */ - con->close_timeout_ts = srv->cur_ts - 2; - connection_set_state(srv, con, CON_STATE_CLOSE); - - return HANDLER_GO_ON; - default: - ERROR("++ %s", "oops, something went wrong while reading"); - return HANDLER_ERROR; - } - } - - /* how much data do we want to extract ? */ - for (c = in->first; c && (out->bytes_in != con->request.content_length); c = c->next) { - off_t weWant, weHave, toRead; - - weWant = con->request.content_length - out->bytes_in; - - if (c->mem->used == 0) continue; - - weHave = c->mem->used - c->offset - 1; - - toRead = weHave > weWant ? weWant : weHave; - - /* the new way, copy everything into a chunkqueue whcih might use tempfiles */ - if (con->request.content_length > 64 * 1024) { - chunk *dst_c = NULL; - /* copy everything to max 1Mb sized tempfiles */ - - /* - * if the last chunk is - * - smaller than 1Mb (size < 1Mb) - * - not read yet (offset == 0) - * -> append to it - * otherwise - * -> create a new chunk - * - * */ - - if (out->last && - out->last->type == FILE_CHUNK && - out->last->file.is_temp && - out->last->offset == 0) { - /* ok, take the last chunk for our job */ - - if (out->last->file.length < 1 * 1024 * 1024) { - dst_c = out->last; - - if (dst_c->file.fd == -1) { - /* this should not happen as we cache the fd, but you never know */ - dst_c->file.fd = open(dst_c->file.name->ptr, O_WRONLY | O_APPEND); - } - } else { - /* the chunk is too large now, close it */ - dst_c = out->last; - - if (dst_c->file.fd != -1) { - close(dst_c->file.fd); - dst_c->file.fd = -1; - } - dst_c = chunkqueue_get_append_tempfile(out); - } - } else { - dst_c = chunkqueue_get_append_tempfile(out); - } - - /* we have a chunk, let's write to it */ - - if (dst_c->file.fd == -1) { - /* we don't have file to write to, - * EACCES might be one reason. - * - * Instead of sending 500 we send 413 and say the request is too large - * */ - - ERROR("denying upload as opening to temp-file for upload failed: '%s': %s", - SAFE_BUF_STR(dst_c->file.name), strerror(errno)); - - con->http_status = 413; /* Request-Entity too large */ - con->keep_alive = 0; - return HANDLER_FINISHED; - } - - if (toRead != write(dst_c->file.fd, c->mem->ptr + c->offset, toRead)) { - /* write failed for some reason ... disk full ? */ - ERROR("denying upload as writing to file failed: '%s': %s", - SAFE_BUF_STR(dst_c->file.name), strerror(errno)); - - con->http_status = 413; /* Request-Entity too large */ - con->keep_alive = 0; - - close(dst_c->file.fd); - dst_c->file.fd = -1; - - return HANDLER_FINISHED; - } - - dst_c->file.length += toRead; - - if (out->bytes_in + toRead == con->request.content_length) { - /* we read everything, close the chunk */ - close(dst_c->file.fd); - dst_c->file.fd = -1; - } - } else { - buffer *b; - - b = (out->last) ? out->last->mem : NULL; - - if (NULL == b) { - b = chunkqueue_get_append_buffer(out); - buffer_prepare_copy(b, con->request.content_length - out->bytes_in + 1); - } - - buffer_append_string_len(b, c->mem->ptr + c->offset, toRead); - } - - c->offset += toRead; - - out->bytes_in += toRead; - in->bytes_out += toRead; - } - - if (out->bytes_in < con->request.content_length) { - /* we have to read more content */ - fdevent_event_add(srv->ev, con->sock, FDEVENT_IN); - } - - return HANDLER_GO_ON; -} - -static handler_t connection_handle_fdevent(void *s, void *context, int revents) { - server *srv = (server *)s; - connection *con = context; - - if (revents & FDEVENT_IN) { - switch (con->state) { - case CON_STATE_READ_REQUEST_HEADER: - case CON_STATE_READ_REQUEST_CONTENT: - joblist_append(srv, con); - break; - case CON_STATE_CLOSE: /* ignore the even, we will clean-up soon */ - break; - case CON_STATE_ERROR: - ERROR("we are in (CON_STATE_ERROR), but still get a FDEVENT_IN, removing event from fd = %d, %04x for (%s)", - con->sock->fd, - revents, - SAFE_BUF_STR(con->uri.path)); - - fdevent_event_del(srv->ev, con->sock); - - joblist_append(srv, con); - break; - default: - ERROR("I thought only READ_REQUEST_* need fdevent-in: %d, fd = %d, %04x for (%s)", - con->state, - con->sock->fd, - revents, - SAFE_BUF_STR(con->uri.path)); - break; - } - } - - if (revents & FDEVENT_OUT) { - switch (con->state) { - case CON_STATE_WRITE_RESPONSE_HEADER: - case CON_STATE_WRITE_RESPONSE_CONTENT: - joblist_append(srv, con); - break; - case CON_STATE_ERROR: - ERROR("we are in (CON_STATE_ERROR), but still get a FDEVENT_OUT, removing event from fd = %d, %04x for (%s)", - con->sock->fd, - revents, - SAFE_BUF_STR(con->uri.path)); - - fdevent_event_del(srv->ev, con->sock); - - joblist_append(srv, con); - break; - default: - TRACE("got FDEVENT_OUT for state %d, calling the job-handler, let's see what happens", con->state); - joblist_append(srv, con); - break; - } - } - - if (revents & ~(FDEVENT_IN | FDEVENT_OUT)) { - /* looks like an error */ - - connection_set_state(srv, con, CON_STATE_ERROR); - joblist_append(srv, con); - } - - - return HANDLER_FINISHED; -} - - -connection *connection_accept(server *srv, server_socket *srv_socket) { - /* accept everything */ - - /* search an empty place */ - int cnt; - sock_addr cnt_addr; - socklen_t cnt_len; - /* accept it and register the fd */ - - cnt_len = sizeof(cnt_addr); - - if (-1 == (cnt = accept(srv_socket->sock->fd, (struct sockaddr *) &cnt_addr, &cnt_len))) { -#ifdef _WIN32 - errno = WSAGetLastError(); -#endif - 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: /* we are out of FDs */ - server_out_of_fds(srv, NULL); - break; - default: - ERROR("accept failed on fd=%d with error: (%d) %s", srv_socket->sock->fd, errno, strerror(errno)); - break; - } - return NULL; - } else { - connection *con; - - /* ok, we have the connection, register it */ -#if 0 - TRACE("appected() = %i", cnt); -#endif - srv->con_opened++; - - con = connections_get_new_connection(srv); - con->sock->fd = cnt; - con->sock->fde_ndx = -1; -#if 0 - gettimeofday(&(con->start_tv), NULL); -#endif - fdevent_register(srv->ev, con->sock, connection_handle_fdevent, con); - - 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; - - if (-1 == (fdevent_fcntl_set(srv->ev, con->sock))) { - ERROR("fcntl failed: %s", strerror(errno)); - connection_close(srv, con); - return NULL; - } - -#ifdef USE_OPENSSL - /* connect FD to SSL */ - if (srv_socket->is_ssl) { - if (NULL == (con->sock->ssl = SSL_new(srv_socket->ssl_ctx))) { - ERROR("SSL: %s", - ERR_error_string(ERR_get_error(), NULL)); - connection_close(srv, con); - return NULL; - } - -#ifndef OPENSSL_NO_TLSEXT - SSL_set_app_data(con->sock->ssl, con); -#endif - SSL_set_accept_state(con->sock->ssl); - con->conf.is_ssl=1; - - if (1 != (SSL_set_fd(con->sock->ssl, cnt))) { - ERROR("SSL: %s", - ERR_error_string(ERR_get_error(), NULL)); - connection_close(srv, con); - return NULL; - } - } -#endif - return con; - } -} - -void connection_state_machine(server *srv, connection *con) { - int done = 0, r; - off_t bytes_moved = 0; -#ifdef USE_OPENSSL - server_socket *srv_sock = con->srv_socket; -#endif - - if (srv->srvconf.log_state_handling) { - TRACE("state at start for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - while (done == 0) { - size_t ostate = con->state; - int b; - - switch (con->state) { - case CON_STATE_CONNECT: - if (srv->srvconf.log_state_handling) { - TRACE("state for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - chunkqueue_reset(con->recv_raw); /* this is a new connection, trash the pipeline */ - - con->request_count = 0; - - break; - case CON_STATE_REQUEST_START: - /* init the request handling */ - if (srv->srvconf.log_state_handling) { - TRACE("state for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - con->request_start = srv->cur_ts; /* start of the request */ - con->read_idle_ts = srv->cur_ts; /* start a read-call() */ - - con->request_count++; /* max-keepalive requests */ - con->loops_per_request = 0; /* infinite loops */ - - /* if the content was short enough, it might have a header already in the pipe */ - if (con->recv_raw->first && - chunkqueue_length(con->recv_raw) - con->recv_raw->first->offset > 0) { - /* pipelining */ - - switch (http_request_parse_cq(con->recv_raw, con->http_req)) { - case PARSE_ERROR: - con->http_status = 400; /* the header is broken */ - con->keep_alive = 0; - - chunkqueue_remove_finished_chunks(con->recv_raw); - - connection_set_state(srv, con, CON_STATE_HANDLE_RESPONSE_HEADER); - break; - case PARSE_NEED_MORE: - /* the read() call is on the way */ - connection_set_state(srv, con, CON_STATE_READ_REQUEST_HEADER); - break; - case PARSE_SUCCESS: - /* pipelining worked, validate the header */ - chunkqueue_remove_finished_chunks(con->recv_raw); - - connection_set_state(srv, con, CON_STATE_VALIDATE_REQUEST_HEADER); - break; - default: - chunkqueue_remove_finished_chunks(con->recv_raw); - TRACE("%s", "(error)"); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - } else { - connection_set_state(srv, con, CON_STATE_READ_REQUEST_HEADER); - } - - break; - case CON_STATE_READ_REQUEST_HEADER: - /* read us much data as needed into the recv_raw-cq for a valid header */ - if (srv->srvconf.log_state_handling) { - TRACE("state for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - switch (connection_handle_read_request_header(srv, con)) { - case HANDLER_GO_ON: /** we have a full header, or connection close */ - if (con->state == CON_STATE_READ_REQUEST_HEADER) { - connection_set_state(srv, con, CON_STATE_VALIDATE_REQUEST_HEADER); - } - break; - case HANDLER_FINISHED: /** parsing failed, ->http_status is set */ - connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE_HEADER); - break; - case HANDLER_WAIT_FOR_EVENT: - return; - case HANDLER_ERROR: - TRACE("%s", "(error)"); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - default: - TRACE("%s", "(error)"); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - break; - case CON_STATE_VALIDATE_REQUEST_HEADER: - /* we have the full header, parse it */ - - http_request_parse(srv, con, con->http_req); - - if (con->http_status != 0) { - con->keep_alive = 0; - con->send->is_closed = 1; /* there is no content */ - - connection_set_state(srv, con, CON_STATE_HANDLE_RESPONSE_HEADER); - } else if (array_get_element(con->request.headers, CONST_STR_LEN("Expect"))) { - /* write */ - con->http_status = 100; - con->send->is_closed = 1; - - connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE_HEADER); - } else { - /* parsing the request went fine - * let's find a handler for this request */ - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER); - } - - break; - case CON_STATE_HANDLE_REQUEST_HEADER: - /* the request header is parsed, - * find someone who wants to handle this request */ - - if (srv->srvconf.log_state_handling) { - TRACE("state for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - switch (r = handle_get_backend(srv, con)) { - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - case HANDLER_WAIT_FOR_FD: - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER); - - server_out_of_fds(srv, con); - - break; - case HANDLER_COMEBACK: - done = -1; - case HANDLER_WAIT_FOR_EVENT: - /* come back here */ - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER); - - break; - case HANDLER_ERROR: - /* something went wrong */ - TRACE("%s", "(error)"); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - default: - TRACE("handle_get_backend returned %d on fd %d", r, con->sock->fd); - break; - } - - if (r != HANDLER_FINISHED && r != HANDLER_GO_ON) break; - - if (con->http_status == 404 || - con->http_status == 403) { - /* 404 error-handler */ - - if (con->in_error_handler == 0 && - (!buffer_is_empty(con->conf.error_handler) || - !buffer_is_empty(con->error_handler))) { - /* call error-handler */ - - con->error_handler_saved_status = con->http_status; - con->http_status = 0; - - if (buffer_is_empty(con->error_handler)) { - buffer_copy_string_buffer(con->request.uri, con->conf.error_handler); - } else { - buffer_copy_string_buffer(con->request.uri, con->error_handler); - } - buffer_reset(con->physical.path); - - con->in_error_handler = 1; - - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER); - - /* need to reset condition cache since request uri changed. */ - config_cond_cache_reset(srv, con); - - done = -1; - break; - } else if (con->in_error_handler) { - /* error-handler is a 404 */ - - /* continue as normal, status is the same */ - ERROR("Warning: Either the error-handler returned status 404 or the error-handler itself was not found: %s", SAFE_BUF_STR(con->request.uri)); - ERROR("returning the original status: %i", con->error_handler_saved_status); - ERROR("%s", "If this is a rails app: check your production.log"); - con->http_status = con->error_handler_saved_status; - } - } else if (con->in_error_handler) { - /* error-handler is back and has generated content */ - /* if Status: was set, take it otherwise use 200 */ - con->http_status = con->error_handler_saved_status; - } - - if (con->http_status == 0) con->http_status = 200; - - /* the backend is prepared, forward the content */ - connection_set_state(srv, con, CON_STATE_READ_REQUEST_CONTENT); - - break; - case CON_STATE_READ_REQUEST_CONTENT: - /* the request-handle is setup, read all the data and push it into the request-handler */ - - if (con->request.content_length == -1 || - con->request.content_length == 0) { - con->recv->is_closed = 1; - } - - if (!con->recv->is_closed && - con->recv->bytes_in < con->request.content_length) { - switch (connection_handle_read_request_content(srv, con)) { - case HANDLER_GO_ON: - break; - case HANDLER_ERROR: - TRACE("%s", "(error)"); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - case HANDLER_WAIT_FOR_EVENT: - break; - default: - ERROR("%s", "oops, unknown return value: ..."); - } - - if (con->recv->bytes_in == con->request.content_length) { - /* we read everything */ - fdevent_event_del(srv->ev, con->sock); - con->recv->is_closed = 1; - } - chunkqueue_remove_finished_chunks(con->recv_raw); - } - - chunkqueue_remove_finished_chunks(con->recv); - - /* - * this should call the backend - * they might build the connection now or stream the content to the upstream server - * */ - - switch(r = plugins_call_handle_send_request_content(srv, con)) { - case HANDLER_GO_ON: - /* everything was forwarded */ - break; - case HANDLER_FINISHED: - /* oops, we don't want this here */ - break; - case HANDLER_COMEBACK: - break; - case HANDLER_WAIT_FOR_EVENT: - return; - default: - /* something strange happened */ - TRACE("(error) plugins_call_handle_send_request_content(): r = %d", r); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - - chunkqueue_remove_finished_chunks(con->recv); - - /* jump back, this should be proceeded by mod_staticfile */ - if (r == HANDLER_COMEBACK && con->mode == DIRECT) { - connection_set_state(srv,con,CON_STATE_HANDLE_REQUEST_HEADER); - break; - } - - if (con->state == CON_STATE_ERROR) break; - - if (con->recv->is_closed && - con->recv->bytes_in == con->recv->bytes_out) { - /* everything we read is sent */ - connection_set_state(srv, con, CON_STATE_HANDLE_RESPONSE_HEADER); - } - - break; - case CON_STATE_HANDLE_RESPONSE_HEADER: - /* handle the HTTP response headers, or generate error-page */ - connection_handle_response_header(srv, con); - - /* we got a response header from the backend - * call all plugins who want to modify the response header - * - mod_compress/deflate - * - HTTP/1.1 chunking - * - */ - switch (plugins_call_handle_response_header(srv, con)) { - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - case HANDLER_WAIT_FOR_EVENT: - /* need to wait for more data */ - return; - default: - /* something strange happened */ - if (con->conf.log_request_handling) { - TRACE("%s", "handle response header failed"); - } - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - - if (con->state == CON_STATE_ERROR) break; - - /* all the response-headers are set, check if we have a */ - - connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE_HEADER); - - break; - case CON_STATE_WRITE_RESPONSE_HEADER: - /* write response headers */ - http_response_write_header(srv, con, con->send_raw); - - connection_set_state(srv, con, CON_STATE_WRITE_RESPONSE_CONTENT); - - if (con->request.http_method == HTTP_METHOD_HEAD) { - /* remove the content now */ - chunkqueue_reset(con->send); - - con->send->is_closed = 1; - } - - break; - case CON_STATE_WRITE_RESPONSE_CONTENT: - fdevent_event_del(srv->ev, con->sock); - - con->write_request_ts = srv->cur_ts; - - /* looks like we shall read some content from the backend */ - switch (plugins_call_handle_read_response_content(srv, con)) { - case HANDLER_WAIT_FOR_EVENT: - if (!con->send->is_closed && con->send->bytes_in == con->send->bytes_out) { - /* need to wait for more data */ - return; - } - break; - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - default: - /* something strange happened */ - if (con->conf.log_request_handling) { - TRACE("%s", "read response content failed"); - } - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - - if (con->state == CON_STATE_ERROR) break; - - /* we might have new content in the con->send buffer - * encode it for the network - * - chunking - * - compression - */ - switch (plugins_call_handle_filter_response_content(srv, con)) { - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - case HANDLER_WAIT_FOR_EVENT: - /* need to wait for more data */ - return; - default: - /* something strange happened */ - if (con->conf.log_request_handling) { - TRACE("%s", "filter response content failed"); - } - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - - if (con->state == CON_STATE_ERROR) break; - - /* copy output from filters into send_raw. */ - bytes_moved = filter_chain_copy_output(con->send_filters, con->send_raw); - - /** - * check that all previous filters have cleaned there chunkqueue - */ - if (con->send_raw->is_closed) { - filter *f; - - for (f = con->send_filters->first; - f; - f = f->next) { - off_t cq_len; - - cq_len = chunkqueue_length(f->cq); - - if (cq_len > 0) { - TRACE("filter[%d] is not empty: %jd (report me)", f->id, (intmax_t) cq_len); - } - } - } - - - /* limit download speed. */ - if (con->traffic_limit_reached) { - return; - } - - /* no response data available to send right now. wait for more. */ - if (!con->send_raw->is_closed && - con->send_raw->bytes_in == con->send_raw->bytes_out) { - return; - } - - switch(network_write_chunkqueue(srv, con, con->send_raw)) { - case NETWORK_STATUS_SUCCESS: - /* we send everything from the chunkqueue and the chunkqueue-sender signaled it is finished */ - if (con->send_raw->is_closed) { - if (con->http_status == 100) { - /* send out the 100 Continue header and handle the request as normal afterwards */ - con->http_status = 0; - - /* cleanup send chunkqueue's. */ - chunkqueue_reset(con->send); - chunkqueue_reset(con->send_raw); - - connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST_HEADER); - } else { - connection_set_state(srv, con, CON_STATE_RESPONSE_END); - } - } else { - /* still have data to send in send_raw queue */ - fdevent_event_add(srv->ev, con->sock, FDEVENT_OUT); - return; - } - break; - case NETWORK_STATUS_FATAL_ERROR: /* error on our side */ - TRACE("%s", "(network-subsys sent us a fatal-error)"); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - case NETWORK_STATUS_CONNECTION_CLOSE: /* remote close */ - connection_set_state(srv, con, CON_STATE_ERROR); - break; - case NETWORK_STATUS_WAIT_FOR_AIO_EVENT: - return; - case NETWORK_STATUS_WAIT_FOR_EVENT: - fdevent_event_add(srv->ev, con->sock, FDEVENT_OUT); - - return; - case NETWORK_STATUS_WAIT_FOR_FD: - /* the backend received a EMFILE - * - e.g. for a mmap() of /dev/zero */ - - server_out_of_fds(srv, con); - - return; - case NETWORK_STATUS_INTERRUPTED: - case NETWORK_STATUS_UNSET: - break; - } - - - break; - case CON_STATE_RESPONSE_END: /* transient */ - /* log the request */ - - if (srv->srvconf.log_state_handling) { - TRACE("state for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - plugins_call_handle_response_done(srv, con); - - srv->con_written++; - - if (con->keep_alive) { - connection_set_state(srv, con, CON_STATE_REQUEST_START); - -#if 0 - con->request_start = srv->cur_ts; - con->read_idle_ts = srv->cur_ts; -#endif - } else { - switch(r = plugins_call_handle_connection_close(srv, con)) { - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - default: - ERROR("unhandled return value from plugins_call_handle_connection_close: %i", r); - break; - } - - connection_close(srv, con); - - srv->con_closed++; - } - - connection_reset(srv, con); - - break; - case CON_STATE_CLOSE: - if (srv->srvconf.log_state_handling) { - TRACE("state for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - if (con->keep_alive) { - if (ioctl(con->sock->fd, FIONREAD, &b)) { - ERROR("ioctl() failed: %s", strerror(errno)); - } - if (b > 0) { - char buf[1024]; - ERROR("CLOSE-read() for fd %i, %i bytes", con->sock->fd, b); - - /* */ - sockread(con->sock->fd, buf, sizeof(buf)); - } else { - /* nothing to read */ - - con->close_timeout_ts = srv->cur_ts - 2; - } - } else { - con->close_timeout_ts = srv->cur_ts - 2; - } - - if (srv->cur_ts - con->close_timeout_ts > 1) { - connection_close(srv, con); - - if (srv->srvconf.log_state_handling) { - TRACE("connection closed for fd %i", con->sock->fd); - } - } - - break; - case CON_STATE_ERROR: /* transient */ - /* even if the connection was drop we still have to write it to the access log */ - if (con->http_status) { - plugins_call_handle_response_done(srv, con); - } -#ifdef USE_OPENSSL - if (srv_sock->is_ssl) { - int ret; - switch ((ret = SSL_shutdown(con->sock->ssl))) { - case 1: - /* ok */ - break; - case 0: - SSL_shutdown(con->sock->ssl); - break; - default: - ERROR("SSL (%i): %s", - SSL_get_error(con->sock->ssl, ret), - ERR_error_string(ERR_get_error(), NULL)); - break; - } - } -#endif - - switch(con->mode) { - case DIRECT: -#if 0 - TRACE("emergency exit for fd %i: direct", con->sock->fd); -#endif - break; - default: - switch(r = plugins_call_handle_connection_close(srv, con)) { - case HANDLER_GO_ON: - case HANDLER_FINISHED: - break; - default: - ERROR("unhandled return value from plugins_call_handle_connection_close: %i", r); - break; - } - break; - } - - connection_reset(srv, con); - - /* close the connection */ - if ((con->keep_alive == 1) && - (0 == shutdown(con->sock->fd, SHUT_WR))) { - con->close_timeout_ts = srv->cur_ts; - connection_set_state(srv, con, CON_STATE_CLOSE); - - if (srv->srvconf.log_state_handling) { - TRACE("shutdown for fd %i", con->sock->fd); - } - } else { - connection_close(srv, con); - } - - con->keep_alive = 0; - - srv->con_closed++; - - break; - default: - ERROR("unknown state for fd %i: %i", con->sock->fd, con->state); - connection_set_state(srv, con, CON_STATE_ERROR); - break; - } - - if (done == -1) { - done = 0; - } else if (ostate == con->state) { - done = 1; - } - } - - if (srv->srvconf.log_state_handling) { - TRACE("state at exit for fd %i: %s", con->sock->fd, connection_get_state(con->state)); - } - - return; -} diff --git a/src/connections.h b/src/connections.h deleted file mode 100644 index 1d15b806..00000000 --- a/src/connections.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _CONNECTIONS_H_ -#define _CONNECTIONS_H_ - -#include "settings.h" -#include "server.h" -#include "fdevent.h" - -LI_API connection* connection_init(server *srv); -LI_API int connection_reset(server *srv, connection *con); -LI_API void connections_free(server *srv); - -LI_API connection* connection_accept(server *srv, server_socket *srv_sock); -LI_API int connection_close(server *srv, connection *con); - -LI_API int connection_set_state(server *srv, connection *con, connection_state_t state); -LI_API const char * connection_get_state(connection_state_t state); -LI_API const char * connection_get_short_state(connection_state_t state); -LI_API void connection_state_machine(server *srv, connection *con); - -#endif diff --git a/src/crc32.c b/src/crc32.c deleted file mode 100644 index cdad7bce..00000000 --- a/src/crc32.c +++ /dev/null @@ -1,82 +0,0 @@ -#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(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/src/crc32.h b/src/crc32.h deleted file mode 100644 index 22b112d9..00000000 --- a/src/crc32.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef __crc32cr_table_h__ -#define __crc32cr_table_h__ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <sys/types.h> -#include <stdlib.h> - -#if defined HAVE_STDINT_H -#include <stdint.h> -#elif defined HAVE_INTTYPES_H -#include <inttypes.h> -#endif - -#ifdef _WIN32 -#define uint32_t unsigned __int32 -#endif - -#include "settings.h" - -LI_API uint32_t generate_crc32c(char *string, size_t length); - -#endif diff --git a/src/data_array.c b/src/data_array.c deleted file mode 100644 index c07effb5..00000000 --- a/src/data_array.c +++ /dev/null @@ -1,65 +0,0 @@ -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -#include "array.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_string_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->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) { - data_array *ds; - - ds = calloc(1, sizeof(*ds)); - - ds->key = buffer_init(); - ds->value = array_init(); - - ds->copy = data_array_copy; - ds->free = data_array_free; - ds->reset = data_array_reset; - ds->insert_dup = data_array_insert_dup; - ds->print = data_array_print; - ds->type = TYPE_ARRAY; - - return ds; -} diff --git a/src/data_config.c b/src/data_config.c deleted file mode 100644 index 8e2e75d6..00000000 --- a/src/data_config.c +++ /dev/null @@ -1,136 +0,0 @@ -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -#include "array.h" - -static data_unset *data_config_copy(const data_unset *s) { - data_config *src = (data_config *)s; - data_config *ds = data_config_init(); - - buffer_copy_string_buffer(ds->key, src->key); - buffer_copy_string_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_key); - - array_free(ds->value); - array_free(ds->childs); - - 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_reset(ds->key); - buffer_reset(ds->comp_key); - array_reset(ds->value); -} - -static int data_config_insert_dup(data_unset *dst, data_unset *src) { - UNUSED(dst); - - src->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, "$%s %s \"%s\" {\n", - ds->comp_key->ptr, ds->op->ptr, ds->string->ptr); - 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 = strlen(du->key->ptr); - 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->print(du, depth); - fprintf(stdout, "\n"); - } - - if (ds->childs) { - fprintf(stdout, "\n"); - for (i = 0; i < ds->childs->used; i ++) { - data_unset *du = ds->childs->data[i]; - - /* only the 1st block of chaining */ - if (NULL == ((data_config *)du)->prev) { - fprintf(stdout, "\n"); - array_print_indent(depth); - du->print(du, depth); - fprintf(stdout, "\n"); - } - } - } - - if (0 != ds->context_ndx) { - depth --; - array_print_indent(depth); - fprintf(stdout, "}"); - - fprintf(stdout, " # end of $%s %s \"%s\"", - ds->comp_key->ptr, ds->op->ptr, ds->string->ptr); - } - - if (ds->next) { - fprintf(stdout, "\n"); - array_print_indent(depth); - fprintf(stdout, "else "); - ds->next->print((data_unset *)ds->next, depth); - } -} - -data_config *data_config_init(void) { - data_config *ds; - - ds = calloc(1, sizeof(*ds)); - - ds->key = buffer_init(); - ds->op = buffer_init(); - ds->comp_key = buffer_init(); - ds->value = array_init(); - ds->childs = array_init(); - ds->childs->is_weakref = 1; - - ds->copy = data_config_copy; - ds->free = data_config_free; - ds->reset = data_config_reset; - ds->insert_dup = data_config_insert_dup; - ds->print = data_config_print; - ds->type = TYPE_CONFIG; - - return ds; -} diff --git a/src/data_count.c b/src/data_count.c deleted file mode 100644 index 5c5a5f06..00000000 --- a/src/data_count.c +++ /dev/null @@ -1,68 +0,0 @@ -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -#include "array.h" - -static data_unset *data_count_copy(const data_unset *s) { - data_count *src = (data_count *)s; - data_count *ds = data_count_init(); - - buffer_copy_string_buffer(ds->key, src->key); - ds->count = src->count; - ds->is_index_key = src->is_index_key; - return (data_unset *)ds; -} - -static void data_count_free(data_unset *d) { - data_count *ds = (data_count *)d; - - buffer_free(ds->key); - - free(d); -} - -static void data_count_reset(data_unset *d) { - data_count *ds = (data_count *)d; - - buffer_reset(ds->key); - - ds->count = 0; -} - -static int data_count_insert_dup(data_unset *dst, data_unset *src) { - data_count *ds_dst = (data_count *)dst; - data_count *ds_src = (data_count *)src; - - ds_dst->count += ds_src->count; - - src->free(src); - - return 0; -} - -static void data_count_print(const data_unset *d, int depth) { - data_count *ds = (data_count *)d; - UNUSED(depth); - - fprintf(stdout, "count(%d)", ds->count); -} - - -data_count *data_count_init(void) { - data_count *ds; - - ds = calloc(1, sizeof(*ds)); - - ds->key = buffer_init(); - ds->count = 1; - - ds->copy = data_count_copy; - ds->free = data_count_free; - ds->reset = data_count_reset; - ds->insert_dup = data_count_insert_dup; - ds->print = data_count_print; - ds->type = TYPE_COUNT; - - return ds; -} diff --git a/src/data_integer.c b/src/data_integer.c deleted file mode 100644 index 54234077..00000000 --- a/src/data_integer.c +++ /dev/null @@ -1,65 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "array.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_string_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_reset(ds->key); - ds->value = 0; -} - -static int data_integer_insert_dup(data_unset *dst, data_unset *src) { - UNUSED(dst); - - src->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) { - data_integer *ds; - - ds = calloc(1, sizeof(*ds)); - - ds->key = buffer_init(); - ds->value = 0; - - ds->copy = data_integer_copy; - ds->free = data_integer_free; - ds->reset = data_integer_reset; - ds->insert_dup = data_integer_insert_dup; - ds->print = data_integer_print; - ds->type = TYPE_INTEGER; - - return ds; -} diff --git a/src/data_string.c b/src/data_string.c deleted file mode 100644 index 1b9da692..00000000 --- a/src/data_string.c +++ /dev/null @@ -1,121 +0,0 @@ -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> - -#include "array.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_string_buffer(ds->key, src->key); - buffer_copy_string_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 (ds_dst->value->used) { - buffer_append_string_len(ds_dst->value, CONST_STR_LEN(", ")); - buffer_append_string_buffer(ds_dst->value, ds_src->value); - } else { - buffer_copy_string_buffer(ds_dst->value, ds_src->value); - } - - src->free(src); - - return 0; -} - -static int data_response_insert_dup(data_unset *dst, data_unset *src) { - data_string *ds_dst = (data_string *)dst; - data_string *ds_src = (data_string *)src; - - if (ds_dst->value->used) { - buffer_append_string_len(ds_dst->value, CONST_STR_LEN("\r\n")); - buffer_append_string_buffer(ds_dst->value, ds_dst->key); - buffer_append_string_len(ds_dst->value, CONST_STR_LEN(": ")); - buffer_append_string_buffer(ds_dst->value, ds_src->value); - } else { - buffer_copy_string_buffer(ds_dst->value, ds_src->value); - } - - src->free(src); - - return 0; -} - - -static void data_string_print(const data_unset *d, int depth) { - data_string *ds = (data_string *)d; - unsigned int i = 0; - UNUSED(depth); - - // empty and uninitialized strings - if (ds->value->used < 1) { - fputs("\"\"", stdout); - return; - } - - // print out the string as is, except prepend " with backslash - putc('"', stdout); - for (i = 0; i < ds->value->used - 1; i++) { - unsigned char c = ds->value->ptr[i]; - if (c == '"') { - fputs("\\\"", stdout); - } else { - putc(c, stdout); - } - } - putc('"', stdout); -} - - -data_string *data_string_init(void) { - data_string *ds; - - ds = calloc(1, sizeof(*ds)); - assert(ds); - - ds->key = buffer_init(); - ds->value = buffer_init(); - - ds->copy = data_string_copy; - ds->free = data_string_free; - ds->reset = data_string_reset; - ds->insert_dup = data_string_insert_dup; - ds->print = data_string_print; - ds->type = TYPE_STRING; - - return ds; -} - -data_string *data_response_init(void) { - data_string *ds; - - ds = data_string_init(); - ds->insert_dup = data_response_insert_dup; - - return ds; -} diff --git a/src/etag.c b/src/etag.c deleted file mode 100644 index 0e16c598..00000000 --- a/src/etag.c +++ /dev/null @@ -1,53 +0,0 @@ -#include <string.h> - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#if defined HAVE_STDINT_H -#include <stdint.h> -#elif defined HAVE_INTTYPES_H -#include <inttypes.h> -#endif - -#include "buffer.h" -#include "etag.h" - -int etag_is_equal(buffer *etag, const char *matches) { - if (buffer_is_equal_string(etag, matches, strlen(matches))) return 1; - return 0; -} - -int etag_create(buffer *etag, struct stat *st, etag_flags_t flags) { - - if (0 == flags) return 0; - - buffer_reset(etag); - - if (flags & ETAG_USE_INODE) { - buffer_append_off_t(etag, st->st_ino); - buffer_append_string_len(etag, CONST_STR_LEN("-")); - } - if (flags & ETAG_USE_SIZE) { - buffer_append_off_t(etag, st->st_size); - buffer_append_string_len(etag, CONST_STR_LEN("-")); - } - if (flags & ETAG_USE_MTIME) { - buffer_append_long(etag, st->st_mtime); - } - return 0; -} - -int etag_mutate(buffer *mut, buffer *etag) { - size_t i; - uint32_t h; - - for (h=0, i=0; i < etag->used-1; ++i) h = (h<<5)^(h>>27)^(etag->ptr[i]); - - buffer_reset(mut); - buffer_copy_string_len(mut, CONST_STR_LEN("\"")); - buffer_append_long(mut, h); - buffer_append_string_len(mut, CONST_STR_LEN("\"")); - - return 0; -} diff --git a/src/etag.h b/src/etag.h deleted file mode 100644 index 80e9ab9c..00000000 --- a/src/etag.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef ETAG_H -#define ETAG_H - -#include <sys/types.h> -#include <sys/stat.h> - -#include "buffer.h" - -typedef enum { ETAG_USE_INODE = 1, ETAG_USE_MTIME = 2, ETAG_USE_SIZE = 4 } etag_flags_t; - -LI_API int etag_is_equal(buffer *etag, const char *matches); -LI_API int etag_create(buffer *etag, struct stat *st, etag_flags_t flags); -LI_API int etag_mutate(buffer *mut, buffer *etag); - - -#endif diff --git a/src/fastcgi.h b/src/fastcgi.h deleted file mode 100644 index 31c00ad6..00000000 --- a/src/fastcgi.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 $ - */ - -#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/src/fcgi-stat-accel.c b/src/fcgi-stat-accel.c deleted file mode 100644 index bae39e6b..00000000 --- a/src/fcgi-stat-accel.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - compile with: - - $ gcc -lfcgi -lpthread fcgi-stat-accel.c -o fcgi-stat-accel - - fcgi-stat-accel will use the PHP_FCGI_CHILDREN environment variable to set the thread count. - - The default value, if spawned from lighttpd, is 20. -*/ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <sys/types.h> -#include <sys/stat.h> - -#ifdef HAVE_PTHREAD_H -#include <pthread.h> -#endif -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#include <string.h> -#include <fcntl.h> - -#include <stdlib.h> -#ifdef HAVE_FASTCGI_FASTCGI_H -#include <fastcgi/fcgiapp.h> -#elif defined (HAVE_FASTCGI_H) -#include <fcgiapp.h> -#endif - -#define THREAD_COUNT 20 - -#define FORBIDDEN(stream) \ - FCGX_FPrintF(stream, "Status: 403 Forbidden\r\nContent-Type: text/html\r\n\r\n<h1>403 Forbidden</h1>\n"); -#define NOTFOUND(stream, filename) \ - FCGX_FPrintF(stream, "Status: 404 Not Found\r\nContent-Type: text/html\r\n\r\n<h1>404 Not Found</h1>\r\n%s", filename); -#define SENDFILE(stream, filename) \ - FCGX_FPrintF(stream, "X-LIGHTTPD-send-file: %s\r\n\r\n", filename); - -#define UNUSED(x) ( (void)(x) ) - -static void *doit(void *a){ - FCGX_Request request; - int rc; - char *filename; - UNUSED(a); - - FCGX_InitRequest(&request, 0, /* FCGI_FAIL_ACCEPT_ON_INTR */ 0); - - while(1){ - int fd; - //Some platforms require accept() serialization, some don't. The documentation claims it to be thread safe -// static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER; -// pthread_mutex_lock(&accept_mutex); - rc = FCGX_Accept_r(&request); -// pthread_mutex_unlock(&accept_mutex); - - if(rc < 0) - break; - - //get the filename - if((filename = FCGX_GetParam("SCRIPT_FILENAME", request.envp)) == NULL){ - FORBIDDEN(request.out); - //don't try to open directories - }else if(filename[strlen(filename)-1] == '/'){ - FORBIDDEN(request.out); - //open the file - }else if((fd = open(filename, O_RDONLY)) == -1){ - NOTFOUND(request.out, filename); - //no error, serve it - }else{ - SENDFILE(request.out, filename); - - close(fd); - } - - FCGX_Finish_r(&request); - } - return NULL; -} - -int main(void){ - int i,j,thread_count; - pthread_t* id; - char* env_val; - - FCGX_Init(); - - thread_count = THREAD_COUNT; - env_val = getenv("PHP_FCGI_CHILDREN"); - if (env_val != NULL) { - j = atoi(env_val); - if (j != 0) { - thread_count = j; - }; - }; - - id = malloc(sizeof(*id) * thread_count); - - for (i = 0; i < thread_count; i++) { - pthread_create(&id[i], NULL, doit, NULL); - } - - /* block the current thread by executing one */ - doit(NULL); - - for (i = 0; i < thread_count; i++) { - pthread_join(id[i], NULL); - } - - free(id); - - return 0; -} - diff --git a/src/fdevent.c b/src/fdevent.c deleted file mode 100644 index 9386b1d9..00000000 --- a/src/fdevent.c +++ /dev/null @@ -1,390 +0,0 @@ -#include <sys/types.h> - -#include "settings.h" - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <stdio.h> -#include <fcntl.h> -#include <assert.h> - -#include "fdevent.h" -#include "buffer.h" -#include "log.h" - -#include "sys-socket.h" - -static fdevent_handler_info_t fdevent_handlers[] = { - /* - poll is most reliable - * - select works everywhere - * - linux-* are experimental - */ - { - FDEVENT_HANDLER_LINUX_SYSEPOLL, - "linux-sysepoll", - "epoll (Linux 2.6)", -#ifdef USE_LINUX_EPOLL - fdevent_linux_sysepoll_init -#else - NULL -#endif - }, - { - FDEVENT_HANDLER_POLL, - "poll", - "poll (Unix)", -#ifdef USE_POLL - fdevent_poll_init -#else - NULL -#endif - }, - { - FDEVENT_HANDLER_SELECT, - "select", - "select (generic)", -#ifdef USE_SELECT - fdevent_select_init -#else - NULL -#endif - }, - { - FDEVENT_HANDLER_LINUX_RTSIG, - "linux-rtsig", - "rt-signals (Linux 2.4+)", -#ifdef USE_LINUX_SIGIO - fdevent_linux_rtsig_init -#else - NULL -#endif - }, - { - FDEVENT_HANDLER_SOLARIS_DEVPOLL,"solaris-devpoll", - "/dev/poll (Solaris)", -#ifdef USE_SOLARIS_DEVPOLL - fdevent_solaris_devpoll_init -#else - NULL -#endif - }, - { - FDEVENT_HANDLER_FREEBSD_KQUEUE, - "freebsd-kqueue", - "kqueue (FreeBSD)", -#ifdef USE_FREEBSD_KQUEUE - fdevent_freebsd_kqueue_init -#else - NULL -#endif - }, - { - FDEVENT_HANDLER_FREEBSD_KQUEUE, - "kqueue", - "kqueue (FreeBSD)", -#ifdef USE_FREEBSD_KQUEUE - fdevent_freebsd_kqueue_init -#else - NULL -#endif - }, - { - FDEVENT_HANDLER_UNSET, - NULL, - NULL, - NULL - } -}; - - - -const fdevent_handler_info_t *fdevent_get_handlers() { - return fdevent_handlers; -} - -const fdevent_handler_info_t *fdevent_get_defaulthandler() { - const fdevent_handler_info_t *handler = fdevent_get_handlers(); - - while (handler->name) { - if (handler->init) { - return handler; - } - handler ++; - } - - return NULL; -} - -const fdevent_handler_info_t *fdevent_get_handler_info_by_type(fdevent_handler_t type) { - const fdevent_handler_info_t *handler = fdevent_get_handlers(); - - while (handler->name) { - if (type == handler->type) { - return handler; - } - handler ++; - } - - return NULL; -} - -const fdevent_handler_info_t *fdevent_get_handler_info_by_name(const char *name) { - const fdevent_handler_info_t *handler = fdevent_get_handlers(); - - while (handler->name) { - if (strcmp(name, handler->name) == 0) { - return handler; - } - handler ++; - } - - return NULL; -} - -fdevent_revent *fdevent_revent_init(void) { - STRUCT_INIT(fdevent_revent, revent); - - return revent; -} - -void fdevent_revent_free(fdevent_revent *revent) { - if (!revent) return; - - free(revent); -} - -fdevent_revents *fdevent_revents_init(void) { - STRUCT_INIT(fdevent_revents, revents); - - return revents; -} - -void fdevent_revents_reset(fdevent_revents *revents) { - if (!revents) return; - - revents->used = 0; -} - -void fdevent_revents_add(fdevent_revents *revents, int fd, int events) { - fdevent_revent *revent; - - if (revents->used == revents->size) { - /* resize the events-array */ - revents->ptr = realloc(revents->ptr, (revents->size + 1) * sizeof(*(revents->ptr))); - revents->ptr[revents->size++] = fdevent_revent_init(); - } - - revent = revents->ptr[revents->used++]; - revent->fd = fd; - revent->revents = events; -} - -void fdevent_revents_free(fdevent_revents *revents) { - size_t i; - - if (!revents) return; - - if (revents->size) { - for (i = 0; i < revents->size; i++) { - fdevent_revent_free(revents->ptr[i]); - } - - free(revents->ptr); - } - free(revents); -} - -fdevents *fdevent_init(size_t maxfds, fdevent_handler_t type) { - fdevents *ev; - const fdevent_handler_info_t *handler = fdevent_get_handler_info_by_type(type); - - ev = calloc(1, sizeof(*ev)); - ev->fdarray = calloc(maxfds, sizeof(*ev->fdarray)); - ev->maxfds = maxfds; - - assert(handler && handler->init); - if (0 != handler->init(ev)) { - switch(type) { - case FDEVENT_HANDLER_POLL: - ERROR("event-handler poll failed %s", ""); - return NULL; - - case FDEVENT_HANDLER_SELECT: - ERROR("event-handler select failed %s", ""); - return NULL; - - default: - if (0 != fdevent_linux_rtsig_init(ev)) { - ERROR("event-handler '%s' failed, use 'poll' or 'select' instead", handler->name); - - return NULL; - } - } - } - - return ev; -} - -void fdevent_free(fdevents *ev) { - size_t i; - if (!ev) return; - - if (ev->free) ev->free(ev); - - for (i = 0; i < ev->maxfds; i++) { - if (ev->fdarray[i]) free(ev->fdarray[i]); - } - - free(ev->fdarray); - free(ev); -} - -int fdevent_reset(fdevents *ev) { - if (ev->reset) return ev->reset(ev); - - return 0; -} - -static fdnode *fdnode_init() { - fdnode *fdn; - - fdn = calloc(1, sizeof(*fdn)); - fdn->fd = -1; - - return fdn; -} - -static void fdnode_free(fdnode *fdn) { - free(fdn); -} - -/* Unix file descriptors begin at zero for each process, but not so win32. -The fdevent system uses the file descriptors as indexers into the fdarray, but we can't -just do that on windows. Lazily, this is O(n) on the number of file descriptors/sockets, but -with select's limit of 64 events, this is not really a bottleneck. For the sake of win32, -it would be nice if all file/socket descriptors were references to IO devices in our own -global table, instead of just using the system-provided fd directly. -*/ -#ifdef _WIN32 - -size_t fdevent_find_free_slot(fdevents *ev, int fd) { - size_t i; - for ( i = 0; i < ev->maxfds; i++ ) - { - if ( ev->fdarray[i] == NULL ) - return i; - } - SEGFAULT("no more free fdevent.fdarray slots: %s", ""); - return -1; -} - -size_t fdevent_find_slot(fdevents *ev, int fd) { - size_t i; - for ( i = 0; i < ev->maxfds; i++ ) - { - if ( ev->fdarray[i] && ev->fdarray[i]->fd == fd ) return i; - } - DebugBreak(); - return -1; -} - -#else - -#define fdevent_find_free_slot( ev, fd ) (fd) -#define fdevent_find_slot( ev, fd ) (fd) - -#endif - -int fdevent_register(fdevents *ev, iosocket *sock, fdevent_handler handler, void *ctx) { - fdnode *fdn; - size_t fda_ndx; - - fda_ndx = fdevent_find_free_slot(ev, sock->fd); - - fdn = fdnode_init(); - fdn->handler = handler; - fdn->fd = sock->fd; - fdn->ctx = ctx; - - ev->fdarray[fda_ndx] = fdn; - - return 0; -} - -int fdevent_unregister(fdevents *ev, iosocket *sock) { - fdnode *fdn; - size_t fda_ndx; - if (!ev) return 0; - fda_ndx = fdevent_find_slot(ev, sock->fd); - - fdn = ev->fdarray[fda_ndx]; - - fdnode_free(fdn); - - ev->fdarray[fda_ndx] = NULL; - - return 0; -} - -int fdevent_event_del(fdevents *ev, iosocket *sock) { - if (ev->event_del) ev->event_del(ev, sock); - - return 0; -} - -int fdevent_event_add(fdevents *ev, iosocket *sock, int events) { - if (ev->event_add) ev->event_add(ev, sock, events); - - return 0; -} - -int fdevent_poll(fdevents *ev, int timeout_ms) { - if (ev->poll == NULL) SEGFAULT("ev->poll is %p", (void*) (intptr_t) ev->poll); - return ev->poll(ev, timeout_ms); -} - -int fdevent_get_revents(fdevents *ev, size_t event_count, fdevent_revents *revents) { - size_t i; - - if (ev->get_revents == NULL) SEGFAULT("ev->get_revents is %p", (void*) (intptr_t) ev->get_revents); - - fdevent_revents_reset(revents); - - ev->get_revents(ev, event_count, revents); - /* select() reports more events, as it counts per event and per fd (e.g. READ+WRITE on one fd == two events) */ - /* assert(revents->used == event_count); */ - - /* patch the event handlers */ - for (i = 0; i < revents->used; i++) { - size_t fda_ndx; - fdevent_revent *r = revents->ptr[i]; - - fda_ndx = fdevent_find_slot(ev, r->fd); - - r->handler = ev->fdarray[fda_ndx]->handler; - r->context = ev->fdarray[fda_ndx]->ctx; - } - - return 0; -} - -int fdevent_fcntl_set(fdevents *ev, iosocket *sock) { -#ifdef _WIN32 - int i = 1; -#endif -#ifdef FD_CLOEXEC - /* close fd on exec (cgi) */ - fcntl(sock->fd, F_SETFD, FD_CLOEXEC); -#endif - if ((ev) && (ev->fcntl_set)) return ev->fcntl_set(ev, sock->fd); -#ifdef O_NONBLOCK - return fcntl(sock->fd, F_SETFL, O_NONBLOCK | O_RDWR); -#elif defined _WIN32 - return ioctlsocket(sock->fd, FIONBIO, &i); -#else - return 0; -#endif -} - - diff --git a/src/fdevent.h b/src/fdevent.h deleted file mode 100644 index 13ae07f7..00000000 --- a/src/fdevent.h +++ /dev/null @@ -1,280 +0,0 @@ -#ifndef _FDEVENT_H_ -#define _FDEVENT_H_ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include "settings.h" -#include "bitset.h" - -#include "iosocket.h" -#include "array-static.h" - -/* select event-system */ - -#if defined(HAVE_EPOLL_CTL) && defined(HAVE_SYS_EPOLL_H) -# if defined HAVE_STDINT_H -# include <stdint.h> -# endif -# define USE_LINUX_EPOLL -# include <sys/epoll.h> -#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 USE_POLL -# ifdef HAVE_POLL_H -# include <poll.h> -# else -# include <sys/poll.h> -# endif -# if defined HAVE_SIGTIMEDWAIT && defined(__linux__) -# define USE_LINUX_SIGIO -# include <signal.h> -# endif -#endif -#ifdef _WIN32 -# define HAVE_SELECT -#endif -#if defined HAVE_SELECT -# ifdef _WIN32 -# include <winsock2.h> -# endif -# define USE_SELECT -# ifdef HAVE_SYS_SELECT_H -# include <sys/select.h> -# endif -#endif - -#if defined HAVE_SYS_DEVPOLL_H && defined(__sun) -# define USE_SOLARIS_DEVPOLL -# include <sys/devpoll.h> -#endif - -#if defined HAVE_SYS_EVENT_H && defined HAVE_KQUEUE -# define USE_FREEBSD_KQUEUE -# include <sys/event.h> -#endif - -#if defined HAVE_SYS_PORT_H && defined HAVE_PORT_CREATE -# define USE_SOLARIS_PORT -# include <sys/port.h> -#endif - - -typedef handler_t (*fdevent_handler)(void *srv, void *ctx, int revents); - -#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) - -typedef enum { FD_EVENT_TYPE_UNSET = -1, - FD_EVENT_TYPE_CONNECTION, - FD_EVENT_TYPE_FCGI_CONNECTION, - FD_EVENT_TYPE_DIRWATCH, - FD_EVENT_TYPE_CGI_CONNECTION -} fd_event_t; - -typedef enum { FDEVENT_HANDLER_UNSET, - FDEVENT_HANDLER_SELECT, - FDEVENT_HANDLER_POLL, - FDEVENT_HANDLER_LINUX_RTSIG, - FDEVENT_HANDLER_LINUX_SYSEPOLL, - FDEVENT_HANDLER_SOLARIS_DEVPOLL, - FDEVENT_HANDLER_FREEBSD_KQUEUE, - FDEVENT_HANDLER_SOLARIS_PORT -} fdevent_handler_t; - -/** - * a mapping from fd to connection structure - * - */ -typedef struct { - int fd; /**< the fd */ - void *conn; /**< a reference the corresponding data-structure */ - fd_event_t fd_type; /**< type of the fd */ - int events; /**< registered events */ - int revents; -} fd_conn; - -ARRAY_STATIC_DEF(fd_conn_buffer, fd_conn, ); - -/** - * revents - */ -typedef struct { - int fd; - int revents; - - fdevent_handler handler; - void *context; -} fdevent_revent; - -ARRAY_STATIC_DEF(fdevent_revents, fdevent_revent, ); - -/** - * array of unused fd's - * - */ - -typedef struct _fdnode { - fdevent_handler handler; /* who handles the events for this fd */ - void *ctx; /* opaque pointer which is passed as 3rd parameter to the handler */ - int fd; /* fd */ - - struct _fdnode *prev, *next; -} fdnode; - -typedef struct { - int *ptr; - - size_t used; - size_t size; -} buffer_int; - -/** - * fd-event handler for select(), poll() and rt-signals on Linux 2.4 - * - */ -typedef struct fdevents { - fdevent_handler_t type; - - fdnode **fdarray; /* a list of fdnodes */ - size_t maxfds; - -#ifdef USE_LINUX_SIGIO - int in_sigio; - int signum; - sigset_t sigset; - siginfo_t siginfo; - bitset *sigbset; -#endif -#ifdef USE_LINUX_EPOLL - int epoll_fd; - struct epoll_event *epoll_events; -#endif -#ifdef USE_POLL - struct pollfd *pollfds; - - size_t size; - size_t used; - - buffer_int unused; -#endif -#ifdef USE_SELECT - /* Temporary sets, cloned from permanent sets, and passed to select() */ - fd_set select_read; - fd_set select_write; - fd_set select_error; - - /* Permanent sets */ - fd_set select_set_read; - fd_set select_set_write; - fd_set select_set_error; - - /* Since windows socket IDs are as good as unpredictable, we need to keep track of each one. - *nix socket IDs start at zero for each process and are reused throughout the life of the process. */ -#ifdef _WIN32 - /* We could simply use select_set_error, because that is currently always inclusive, - but better to keep it separate. */ - fd_set select_set_all; -#else - int select_max_fd; -#endif -#endif - - -#ifdef USE_SOLARIS_DEVPOLL - int devpoll_fd; - struct pollfd *devpollfds; -#endif -#ifdef USE_FREEBSD_KQUEUE - int kq_fd; - struct kevent *kq_results; - bitset *kq_read_bevents; - bitset *kq_write_bevents; -#endif -#ifdef USE_SOLARIS_PORT - int port_fd; -#endif - int (*reset)(struct fdevents *ev); - void (*free)(struct fdevents *ev); - - int (*event_add)(struct fdevents *ev, iosocket *sock, int events); - int (*event_del)(struct fdevents *ev, iosocket *sock); - int (*get_revents)(struct fdevents *ev, size_t event_count, fdevent_revents *revents); - - int (*poll)(struct fdevents *ev, int timeout_ms); - - int (*fcntl_set)(struct fdevents *ev, int fd); -} fdevents; - -typedef struct { - fdevent_handler_t type; - const char *name; - const char *description; - int (*init)(fdevents *ev); -} fdevent_handler_info_t; - -LI_API const fdevent_handler_info_t *fdevent_get_handlers(); -LI_API const fdevent_handler_info_t *fdevent_get_defaulthandler(); -LI_API const fdevent_handler_info_t *fdevent_get_handler_info_by_type(fdevent_handler_t type); -LI_API const fdevent_handler_info_t *fdevent_get_handler_info_by_name(const char *name); - -LI_API fdevents* fdevent_init(size_t maxfds, fdevent_handler_t type); -LI_API int fdevent_reset(fdevents *ev); -LI_API void fdevent_free(fdevents *ev); - -/** - * call the plugin for the number of available events - */ -LI_API int fdevent_poll(fdevents *ev, int timeout_ms); -/** - * get all available events - */ -LI_API int fdevent_get_revents(fdevents *ev, size_t event_count, fdevent_revents *revents); - -/** - * add or remove a fd to the handled-pool - */ -LI_API int fdevent_register(fdevents *ev, iosocket *sock, fdevent_handler handler, void *ctx); -LI_API int fdevent_unregister(fdevents *ev, iosocket *sock); - -/** - * add a event to a registered fd - */ -LI_API int fdevent_event_add(fdevents *ev, iosocket *sock, int events); -LI_API int fdevent_event_del(fdevents *ev, iosocket *sock); - -/** - * set non-blocking - */ -LI_API int fdevent_fcntl_set(fdevents *ev, iosocket *sock); - -LI_API fdevent_revents* fdevent_revents_init(void); -LI_API void fdevent_revents_reset(fdevent_revents *revents); -LI_API void fdevent_revents_add(fdevent_revents *revents, int fd, int events); -LI_API void fdevent_revents_free(fdevent_revents *revents); - -LI_API fdevent_revent* fdevent_revent_init(void); -LI_API void fdevent_revent_free(fdevent_revent *revent); - - -/** - * plugin init - */ -LI_API int fdevent_select_init(fdevents *ev); -LI_API int fdevent_poll_init(fdevents *ev); -LI_API int fdevent_linux_rtsig_init(fdevents *ev); -LI_API int fdevent_linux_sysepoll_init(fdevents *ev); -LI_API int fdevent_solaris_devpoll_init(fdevents *ev); -LI_API int fdevent_freebsd_kqueue_init(fdevents *ev); - -#endif - - - diff --git a/src/fdevent_freebsd_kqueue.c b/src/fdevent_freebsd_kqueue.c deleted file mode 100644 index 64af9a91..00000000 --- a/src/fdevent_freebsd_kqueue.c +++ /dev/null @@ -1,221 +0,0 @@ -#include <sys/types.h> - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <signal.h> -#include <fcntl.h> - -#include "server.h" -#include "fdevent.h" -#include "settings.h" -#include "buffer.h" - -#ifdef USE_FREEBSD_KQUEUE -#include <sys/event.h> -#include <sys/time.h> - -#include "sys-files.h" -#include "log.h" - -static int fdevent_freebsd_kqueue_event_add(fdevents *ev, iosocket *sock, int events); - -static void fdevent_freebsd_kqueue_free(fdevents *ev) { - close(ev->kq_fd); - free(ev->kq_results); - bitset_free(ev->kq_read_bevents); - bitset_free(ev->kq_write_bevents); -} - -static int fdevent_freebsd_kqueue_event_del(fdevents *ev, iosocket *sock) { - int ret; - - if (sock->fde_ndx < 0) return -1; - - /* delete events */ - ret = fdevent_freebsd_kqueue_event_add(ev, sock, 0); - sock->fde_ndx = -1; - return 0; -} - -static int fdevent_freebsd_kqueue_event_add(fdevents *ev, iosocket *sock, int events) { - int ret; - struct kevent kev[2]; - struct timespec ts; - int nchanges = 0; - int changed_events = 0; - - if (bitset_test_bit(ev->kq_read_bevents, sock->fd)) { - changed_events |= FDEVENT_IN; - } - if (bitset_test_bit(ev->kq_write_bevents, sock->fd)) { - changed_events |= FDEVENT_OUT; - } - /* use XOR on changed_events and events to see which events changed. */ - changed_events ^= events; - - if (changed_events & FDEVENT_IN) { - /* add/delete FDEVENT_IN */ - if (events & FDEVENT_IN) { - /* add */ - EV_SET(&(kev[nchanges]), sock->fd, EVFILT_READ, EV_ADD, 0, 0, NULL); - nchanges++; - bitset_set_bit(ev->kq_read_bevents, sock->fd); - } else { - /* delete */ - EV_SET(&(kev[nchanges]), sock->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); - nchanges++; - bitset_clear_bit(ev->kq_read_bevents, sock->fd); - } - } - - if (changed_events & FDEVENT_OUT) { - /* add/delete FDEVENT_OUT */ - if (events & FDEVENT_OUT) { - /* add */ - EV_SET(&(kev[nchanges]), sock->fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); - nchanges++; - bitset_set_bit(ev->kq_write_bevents, sock->fd); - } else { - /* delete */ - EV_SET(&(kev[nchanges]), sock->fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); - nchanges++; - bitset_clear_bit(ev->kq_write_bevents, sock->fd); - } - } - - if (nchanges == 0) return 0; - - ts.tv_sec = 0; - ts.tv_nsec = 0; - - ret = kevent(ev->kq_fd, - kev, nchanges, - NULL, 0, - &ts); - - if (ret == -1) { - ERROR("kqueue failed polling: %s\n", strerror(errno)); - - return -1; - } - - sock->fde_ndx = sock->fd; - - return 0; -} - -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: - fprintf(stderr, "%s.%d: kqueue failed polling: %s\n", - __FILE__, __LINE__, strerror(errno)); - break; - } - } - - return ret; -} - -static int fdevent_freebsd_kqueue_get_revents(fdevents *ev, size_t event_count, fdevent_revents *revents) { - size_t ndx; - - for (ndx = 0; ndx < event_count; ndx++) { - int events = 0, e; - - 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) { - events |= FDEVENT_HUP; - } - - if (e & EV_ERROR) { - events |= FDEVENT_ERR; - } - - fdevent_revents_add(revents, ev->kq_results[ndx].ident, events); - } - - return 0; -} - -static int fdevent_freebsd_kqueue_reset(fdevents *ev) { - if (-1 == (ev->kq_fd = kqueue())) { - fprintf(stderr, "%s.%d: kqueue failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); - - 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_add); - - SET(get_revents); - - ev->kq_fd = -1; - - ev->kq_results = calloc(ev->maxfds, sizeof(*ev->kq_results)); - ev->kq_read_bevents = bitset_init(ev->maxfds); - ev->kq_write_bevents = bitset_init(ev->maxfds); - - /* check that kqueue works */ - - if (-1 == (ev->kq_fd = kqueue())) { - fprintf(stderr, "%s.%d: kqueue failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); - - return -1; - } - - close(ev->kq_fd); - ev->kq_fd = -1; - - return 0; -} -#else -int fdevent_freebsd_kqueue_init(fdevents *ev) { - UNUSED(ev); - - fprintf(stderr, "%s.%d: kqueue not available, try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__); - - return -1; -} -#endif diff --git a/src/fdevent_linux_rtsig.c b/src/fdevent_linux_rtsig.c deleted file mode 100644 index 95bebf2a..00000000 --- a/src/fdevent_linux_rtsig.c +++ /dev/null @@ -1,235 +0,0 @@ -#include <sys/types.h> - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <signal.h> -#include <limits.h> - -#ifndef _GNU_SOURCE -/* seems we dont need this when using _GNU_SOURCE - * #define __USE_GNU - */ -#define __USE_GNU -#endif -#include <fcntl.h> - -#include "fdevent.h" -#include "settings.h" -#include "buffer.h" -#include "sys-process.h" -#include "log.h" - -#ifdef USE_LINUX_SIGIO -static void fdevent_linux_rtsig_free(fdevents *ev) { - free(ev->pollfds); - if (ev->unused.ptr) free(ev->unused.ptr); - - bitset_free(ev->sigbset); -} - - -static int fdevent_linux_rtsig_event_del(fdevents *ev, iosocket *sock) { - if (sock->fde_ndx < 0) return -1; - - if ((size_t)sock->fde_ndx >= ev->used) { - SEGFAULT("del! out of range %d %zu", sock->fde_ndx, ev->used); - } - - if (ev->pollfds[sock->fde_ndx].fd == sock->fd) { - size_t k = sock->fde_ndx; - - ev->pollfds[k].fd = -1; - - bitset_clear_bit(ev->sigbset, sock->fd); - - if (ev->unused.size == 0) { - ev->unused.size = 16; - ev->unused.ptr = malloc(sizeof(*(ev->unused.ptr)) * ev->unused.size); - } 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); - } - - ev->unused.ptr[ev->unused.used++] = k; - } else { - SEGFAULT("del! %d %d", ev->pollfds[sock->fde_ndx].fd, sock->fd); - } - sock->fde_ndx = -1; - - return 0; -} - -#if 0 -static int fdevent_linux_rtsig_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; j--) { - if (ev->pollfds[j].fd == -1) ev->used--; - } - - - return 0; -} -#endif - -static int fdevent_linux_rtsig_event_add(fdevents *ev, iosocket *sock, int events) { - /* known index */ - if (sock->fde_ndx != -1) { - if (ev->pollfds[sock->fde_ndx].fd == sock->fd) { - ev->pollfds[sock->fde_ndx].events = events; - - return sock->fde_ndx; - } - SEGFAULT("add: (%d, %d)", sock->fde_ndx, ev->pollfds[sock->fde_ndx].fd); - } - - if (ev->unused.used > 0) { - int k = ev->unused.ptr[--ev->unused.used]; - - ev->pollfds[k].fd = sock->fd; - ev->pollfds[k].events = events; - - bitset_set_bit(ev->sigbset, sock->fd); - - return k; - } else { - if (ev->size == 0) { - ev->size = 16; - ev->pollfds = malloc(sizeof(*ev->pollfds) * ev->size); - } else if (ev->size == ev->used) { - ev->size += 16; - ev->pollfds = realloc(ev->pollfds, sizeof(*ev->pollfds) * ev->size); - } - - ev->pollfds[ev->used].fd = sock->fd; - ev->pollfds[ev->used].events = events; - - bitset_set_bit(ev->sigbset, sock->fd); - - return ev->used++; - } -} - -static int fdevent_linux_rtsig_poll(fdevents *ev, int timeout_ms) { - struct timespec ts; - int r; - -#if 0 - fdevent_linux_rtsig_event_compress(ev); -#endif - - ev->in_sigio = 1; - - ts.tv_sec = timeout_ms / 1000; - ts.tv_nsec = (timeout_ms % 1000) * 1000000; - r = sigtimedwait(&(ev->sigset), &(ev->siginfo), &(ts)); - - if (r == -1) { - if (errno == EAGAIN) return 0; - return r; - } else if (r == SIGIO) { - struct sigaction act; - - /* flush the signal queue */ - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_IGN; - sigaction(ev->signum, &act, NULL); - - /* re-enable the signal queue */ - act.sa_handler = SIG_DFL; - sigaction(ev->signum, &act, NULL); - - ev->in_sigio = 0; - r = poll(ev->pollfds, ev->used, timeout_ms); - - return r; - } else if (r == ev->signum) { -# if 0 - fprintf(stderr, "event: %d %02lx\n", ev->siginfo.si_fd, ev->siginfo.si_band); -# endif - return bitset_test_bit(ev->sigbset, ev->siginfo.si_fd); - } else { - /* ? */ - return -1; - } -} - -static int fdevent_linux_rtsig_get_revents(fdevents *ev, size_t event_count, fdevent_revents *revents) { - UNUSED(event_count); - - if (ev->in_sigio == 1) { - /* only one event */ - - fdevent_revents_add(revents, ev->siginfo.si_fd, ev->siginfo.si_band & 0x3f); - } else { - size_t ndx; - - for (ndx = 0; ndx < ev->used; ndx++) { - if (ev->pollfds[ndx].revents) { - fdevent_revents_add(revents, ev->pollfds[ndx].fd, ev->pollfds[ndx].revents); - } - } - } - - return 0; -} - -static int fdevent_linux_rtsig_fcntl_set(fdevents *ev, int fd) { - static pid_t pid = 0; - - if (pid == 0) pid = getpid(); - - if (-1 == fcntl(fd, F_SETSIG, ev->signum)) return -1; - - if (-1 == fcntl(fd, F_SETOWN, (int) pid)) return -1; - - return fcntl(fd, F_SETFL, O_ASYNC | O_NONBLOCK | O_RDWR); -} - - -int fdevent_linux_rtsig_init(fdevents *ev) { - ev->type = FDEVENT_HANDLER_LINUX_RTSIG; -#define SET(x) \ - ev->x = fdevent_linux_rtsig_##x; - - SET(free); - SET(poll); - - SET(event_del); - SET(event_add); - - SET(fcntl_set); - SET(get_revents); - - ev->signum = SIGRTMIN + 1; - - sigemptyset(&(ev->sigset)); - sigaddset(&(ev->sigset), ev->signum); - sigaddset(&(ev->sigset), SIGIO); - if (-1 == sigprocmask(SIG_BLOCK, &(ev->sigset), NULL)) { - fprintf(stderr, "%s.%d: sigprocmask failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); - - return -1; - } - - ev->in_sigio = 1; - - ev->sigbset = bitset_init(ev->maxfds); - - return 0; -} -#else -int fdevent_linux_rtsig_init(fdevents *ev) { - UNUSED(ev); - - fprintf(stderr, "%s.%d: linux-rtsig not supported, try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__); - return -1; -} -#endif diff --git a/src/fdevent_linux_sysepoll.c b/src/fdevent_linux_sysepoll.c deleted file mode 100644 index 24a6cc9e..00000000 --- a/src/fdevent_linux_sysepoll.c +++ /dev/null @@ -1,147 +0,0 @@ -#include <sys/types.h> - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <signal.h> -#include <fcntl.h> - -#include "fdevent.h" -#include "settings.h" -#include "buffer.h" -#include "log.h" - -#include "sys-files.h" - -#ifdef USE_LINUX_EPOLL -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, iosocket *sock) { - struct epoll_event ep; - - if (sock->fde_ndx < 0) return -1; - - memset(&ep, 0, sizeof(ep)); - - ep.data.fd = sock->fd; - ep.data.ptr = NULL; - - if (0 != epoll_ctl(ev->epoll_fd, EPOLL_CTL_DEL, sock->fd, &ep)) { - SEGFAULT("epoll_ctl (del) failed on fd=%d: %s", sock->fd, strerror(errno)); - - return 0; - } - - sock->fde_ndx = -1; - - return 0; -} - -static int fdevent_linux_sysepoll_event_add(fdevents *ev, iosocket *sock, int events) { - struct epoll_event ep; - int add = 0; - - /* a new fd */ - if (sock->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; - - /** - * - * 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 = sock->fd; - - if (0 != epoll_ctl(ev->epoll_fd, add ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, sock->fd, &ep)) { - SEGFAULT("epoll_ctl (add/mod) failed on fd=%d: %s", sock->fd, strerror(errno)); - - return 0; - } - - sock->fde_ndx = sock->fd; - - return 0; -} - -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_get_revents(fdevents *ev, size_t event_count, fdevent_revents *revents) { - size_t ndx; - - for (ndx = 0; ndx < event_count; 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; - - fdevent_revents_add(revents, ev->epoll_events[ndx].data.fd, events); - } - - return 0; -} - -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_add); - - SET(get_revents); - - if (-1 == (ev->epoll_fd = epoll_create(ev->maxfds))) { - ERROR("epoll_create failed (%s), try to set server.event-handler = \"poll\" or \"select\"", - strerror(errno)); - - return -1; - } - - if (-1 == fcntl(ev->epoll_fd, F_SETFD, FD_CLOEXEC)) { - ERROR("fcntl after epoll_create failed (%s), try to set server.event-handler = \"poll\" or \"select\"", - strerror(errno)); - - close(ev->epoll_fd); - - return -1; - } - - ev->epoll_events = malloc(ev->maxfds * sizeof(*ev->epoll_events)); - - return 0; -} - -#else -int fdevent_linux_sysepoll_init(fdevents *ev) { - UNUSED(ev); - - ERROR("event-handler 'linux-sysepoll' is not supported, try to set server.event-handler = \"%s\" or \"%s\"", "select", "poll"); - - return -1; -} -#endif diff --git a/src/fdevent_poll.c b/src/fdevent_poll.c deleted file mode 100644 index ebf149c7..00000000 --- a/src/fdevent_poll.c +++ /dev/null @@ -1,153 +0,0 @@ -#include <sys/types.h> - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <signal.h> -#include <fcntl.h> - -#include "fdevent.h" -#include "settings.h" -#include "buffer.h" -#include "log.h" - -#ifdef USE_POLL -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, iosocket *sock) { - if (sock->fde_ndx < 0) return -1; - - if ((size_t)sock->fde_ndx >= ev->used) { - SEGFAULT("(fdevent-poll-del) out of range %d %zd", sock->fde_ndx, ev->used); - } - - if (ev->pollfds[sock->fde_ndx].fd == sock->fd) { - size_t k = sock->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); - } 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); - } - - ev->unused.ptr[ev->unused.used++] = k; - } else { - SEGFAULT("(fdevent-poll-del) sock->fde_ndx: %d, sock->fd: %d -> stored fd: %d", sock->fde_ndx, sock->fd, ev->pollfds[sock->fde_ndx].fd); - } - - sock->fde_ndx = -1; - - return 0; -} - -#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_add(fdevents *ev, iosocket *sock, int events) { - if (sock->fde_ndx != -1) { - /* this fd was already added, just change the requested events */ - - if (ev->pollfds[sock->fde_ndx].fd == sock->fd) { - ev->pollfds[sock->fde_ndx].events = events; - - return sock->fde_ndx; - } - SEGFAULT("(fdevent-poll-add) (%d, %d)", sock->fde_ndx, ev->pollfds[sock->fde_ndx].fd); - } - - if (ev->unused.used > 0) { - int k = ev->unused.ptr[--ev->unused.used]; - - ev->pollfds[k].fd = sock->fd; - ev->pollfds[k].events = events; - - sock->fde_ndx = k; - - } else { - if (ev->size == 0) { - ev->size = 16; - ev->pollfds = malloc(sizeof(*ev->pollfds) * ev->size); - } else if (ev->size == ev->used) { - ev->size += 16; - ev->pollfds = realloc(ev->pollfds, sizeof(*ev->pollfds) * ev->size); - } - - ev->pollfds[ev->used].fd = sock->fd; - ev->pollfds[ev->used].events = events; - - sock->fde_ndx = ev->used++; - } - return 0; -} - -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_get_revents(fdevents *ev, size_t event_count, fdevent_revents *revents) { - size_t ndx; - - UNUSED(event_count); - - for (ndx = 0; ndx < ev->used; ndx++) { - if (ev->pollfds[ndx].revents) { - if (ev->pollfds[ndx].revents & POLLNVAL) { - /* should never happen */ - SEGFAULT("ev->pollfds[%zu].revents has POLLNVAL", ndx); - } - - fdevent_revents_add(revents, ev->pollfds[ndx].fd, ev->pollfds[ndx].revents); - } - } - - return 0; -} - -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_add); - - SET(get_revents); - - return 0; -} - -#else -int fdevent_poll_init(fdevents *ev) { - UNUSED(ev); - - ERROR("event-handler 'poll' is not supported, try to set server.event-handler = \"%s\"", "select"); - - return -1; -} -#endif diff --git a/src/fdevent_select.c b/src/fdevent_select.c deleted file mode 100644 index 66997da6..00000000 --- a/src/fdevent_select.c +++ /dev/null @@ -1,166 +0,0 @@ -#include <sys/types.h> - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <signal.h> -#include <fcntl.h> -#include <assert.h> -#include <stdio.h> - -#include "fdevent.h" -#include "settings.h" -#include "buffer.h" - -#include "sys-socket.h" - -#ifdef HAVE_SYS_TIME_H -/* struct timeval tv needs it on IRIX + MIPS pro*/ -#include <sys/time.h> -#endif - -#ifdef 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)); -#ifdef _WIN32 - FD_ZERO(&(ev->select_set_all)); -#endif -#ifndef _WIN32 - ev->select_max_fd = -1; -#endif - - return 0; -} - -static int fdevent_select_event_del(fdevents *ev, iosocket *sock) { - if (sock->fde_ndx < 0) return -1; - - FD_CLR(sock->fd, &(ev->select_set_read)); - FD_CLR(sock->fd, &(ev->select_set_write)); - FD_CLR(sock->fd, &(ev->select_set_error)); -#ifdef _WIN32 - FD_CLR(sock->fd, &(ev->select_set_all)); -#endif - - /* mark the fdevent as deleted */ - sock->fde_ndx = -1; - - return 0; -} - -static int fdevent_select_event_add(fdevents *ev, iosocket *sock, int events) { - /* we should be protected by max-fds, but you never know */ -#ifndef _WIN32 - assert(sock->fd < ((int)FD_SETSIZE)); -#endif - - if (events & FDEVENT_IN) { - FD_SET(sock->fd, &(ev->select_set_read)); - FD_CLR(sock->fd, &(ev->select_set_write)); - } - if (events & FDEVENT_OUT) { - FD_CLR(sock->fd, &(ev->select_set_read)); - FD_SET(sock->fd, &(ev->select_set_write)); - } - FD_SET(sock->fd, &(ev->select_set_error)); - - /* we need this for the poll */ -#ifdef _WIN32 - FD_SET(sock->fd, &(ev->select_set_all)); -#else - if (sock->fd > ev->select_max_fd) ev->select_max_fd = sock->fd; -#endif - - /* mark fd as added */ - sock->fde_ndx = sock->fd; - - return 0; -} - -static int fdevent_select_poll(fdevents *ev, int timeout_ms) { - struct timeval tv; -#ifndef _WIN32 - int max_ev = ev->select_max_fd + 1; -#else - // windows ignores this - int max_ev = 0; -#endif - - 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(max_ev, &(ev->select_read), &(ev->select_write), &(ev->select_error), &tv); -} - -/** - * scan the fdset for events - */ -static int fdevent_select_get_revents(fdevents *ev, size_t event_count, fdevent_revents *revents) { - - int ndx = 0; -#ifndef _WIN32 - int top = ev->select_max_fd + 1; -#else - int top = ev->select_set_all.fd_count; // FD_SETSIZE; -#endif - - UNUSED(event_count); - - for (ndx = 0; ndx < top; ndx++) { - int events = 0; - -#ifdef _WIN32 - int fdx = ev->select_set_all.fd_array[ndx]; - if ( fdx <= 0 ) continue; -#else - int fdx = ndx; -#endif - - if (FD_ISSET(fdx, &(ev->select_read))) { - events |= FDEVENT_IN; - } - if (FD_ISSET(fdx, &(ev->select_write))) { - events |= FDEVENT_OUT; - } - if (FD_ISSET(fdx, &(ev->select_error))) { - events |= FDEVENT_ERR; - } - - if (events) { - fdevent_revents_add(revents, fdx, events); - } - } - - return 0; -} - -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_add); - - SET(get_revents); - - return 0; -} - -#else -int fdevent_select_init(fdevents *ev) { - UNUSED(ev); - - return -1; -} -#endif diff --git a/src/fdevent_solaris_devpoll.c b/src/fdevent_solaris_devpoll.c deleted file mode 100644 index 60364230..00000000 --- a/src/fdevent_solaris_devpoll.c +++ /dev/null @@ -1,181 +0,0 @@ -#include <sys/types.h> - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <signal.h> -#include <fcntl.h> - -#include "fdevent.h" -#include "settings.h" -#include "buffer.h" -#include "log.h" - -#ifdef USE_SOLARIS_DEVPOLL -#include <unistd.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))) { - fprintf(stderr, "%s.%d: (del) write failed: (%d, %s)\n", - __FILE__, __LINE__, - fd, strerror(errno)); - - return -1; - } - - return -1; -} - -static int fdevent_solaris_devpoll_event_add(fdevents *ev, int fde_ndx, int fd, int events) { - struct pollfd pfd; - int add = 0; - - if (fde_ndx == -1) add = 1; - - pfd.fd = fd; - pfd.events = events; - pfd.revents = 0; - - if (-1 == write(ev->devpoll_fd, &pfd, sizeof(pfd))) { - fprintf(stderr, "%s.%d: (del) write failed: (%d, %s)\n", - __FILE__, __LINE__, - 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_get_revents(fdevents *ev, size_t event_count, fdevent_revents *revents) { - size_t ndx; - - for (ndx = 0; ndx < event_count; ndx++) { - if (ev->devpollfds[ndx].revents) { - if (ev->devpollfds[ndx].revents & POLLNVAL) { - /* should never happen */ - SEGFAULT("ev->devpollfds[%d].revents = %d (POLLNVAL)", ndx, ev->devpollfds[ndx].revents); - } - - fdevent_revents_add(revents, ev->devpollfds[ndx].fd, ev->devpollfds[ndx].revents); - } - } - - return 0; -} - -#if 0 -static int fdevent_solaris_devpoll_event_get_revent(fdevents *ev, size_t ndx) { - return ev->devpollfds[ndx].revents; -} - -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; -} -#endif - -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) { - fprintf(stderr, "%s.%d: opening /dev/poll failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); - - return -1; - } - - if (fcntl(ev->devpoll_fd, F_SETFD, FD_CLOEXEC) < 0) { - fprintf(stderr, "%s.%d: opening /dev/poll failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); - - close(ev->devpoll_fd); - - return -1; - } - 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_add); - -#if 0 - SET(event_next_fdndx); - SET(event_get_fd); - SET(event_get_revent); -#endif - SET(get_revents); - - ev->devpollfds = malloc(sizeof(*ev->devpollfds) * ev->maxfds); - - if ((ev->devpoll_fd = open("/dev/poll", O_RDWR)) < 0) { - fprintf(stderr, "%s.%d: opening /dev/poll failed (%s), try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__, strerror(errno)); - - 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); - - fprintf(stderr, "%s.%d: solaris-devpoll not supported, try to set server.event-handler = \"poll\" or \"select\"\n", - __FILE__, __LINE__); - - return -1; -} -#endif diff --git a/src/filter.c b/src/filter.c deleted file mode 100644 index efb5eb65..00000000 --- a/src/filter.c +++ /dev/null @@ -1,182 +0,0 @@ -/** - * the network filter-API - * - * - */ - -#include <stdlib.h> - -#include <string.h> -#include <assert.h> - -#include "filter.h" - -/** - * create the filter - * - * each filter has a chunkqueue with content and - * a prev and a next pointer - */ -static filter *filter_init(void) { - filter *fl; - - fl = calloc(1, sizeof(*fl)); - - fl->next = NULL; - fl->prev = NULL; - - fl->cq = chunkqueue_init(); - - return fl; -} - -/** - * free the filter - */ -static void filter_free(filter *fl) { - filter *next, *prev; - - /* free chunkqueue */ - if(fl->cq) { - chunkqueue_free(fl->cq); - } - - /* remove this filter from chain. */ - next = fl->next; - prev = fl->prev; - - if(next) { - next->prev = prev; - } - if(prev) { - prev->next = next; - } - - free(fl); -} - -/** - * reset the filter - */ -static void filter_reset(filter *fl) { - /* reset chunkqueue */ - if(fl->cq) { - chunkqueue_reset(fl->cq); - } else { - fl->cq = chunkqueue_init(); - } -} - -/** - * create a filter chain - */ -filter_chain *filter_chain_init(void) { - filter_chain *chain; - - chain = calloc(1, sizeof(*chain)); - - chain->first = NULL; - chain->last = NULL; - filter_chain_create_filter(chain, FILTER_ID_INPUT); - - return chain; -} - -void filter_chain_free(filter_chain *chain) { - filter *first; - if (!chain) return; - - /* free all filters, after the first filter */ - first = chain->first; - while(first->next) { - filter_free(first->next); - } - /* free first filter */ - filter_free(first); - - free(chain); -} - -void filter_chain_reset(filter_chain *chain) { - filter *first; - if (!chain) return; - - /* free all filters, except first filter */ - first = chain->first; - while(first->next) { - filter_free(first->next); - } - /* reset first filter */ - filter_reset(first); - chain->last = first; -} - -filter *filter_chain_create_filter(filter_chain *chain, int id) { - filter *fl; - if (!chain) return NULL; - - fl = filter_init(); - fl->id = id; - /* add filter to end of chain. */ - if (chain->last != NULL) { - chain->last->next = fl; - } - fl->prev = chain->last; - chain->last = fl; - if (chain->first == NULL) { - chain->first = fl; - } - - return fl; -} - -filter *filter_chain_get_filter(filter_chain *chain, int id) { - filter *fl; - if (!chain) return NULL; - - /* find filter with id */ - fl = chain->first; - while(fl) { - if (fl->id == id) break; - fl = fl->next; - } - - return fl; -} - -void filter_chain_remove_filter(filter_chain *chain, filter *fl) { - if (!chain || !fl) return; - - if (chain->first == fl) { - chain->first = fl->next; - } - if (chain->last == fl) { - chain->last = fl->prev; - } - filter_free(fl); -} - -/** - * move all the content of the last filter to the chunk queue - */ -off_t filter_chain_copy_output(filter_chain *chain, chunkqueue *out) { - off_t total; - chunkqueue *in; - - if (!chain || !out) return 0; - if (out->is_closed) return 0; - - in = chain->last->cq; - total = chunkqueue_steal_all_chunks(out, in); - in->bytes_out += total; - out->bytes_in += total; - - chunkqueue_remove_finished_chunks(in); - - if (in->is_closed) { - out->is_closed = 1; - } - - return total; -} - diff --git a/src/filter.h b/src/filter.h deleted file mode 100644 index 91749d42..00000000 --- a/src/filter.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _FILTER_H_ -#define _FILTER_H_ - -#include "chunk.h" - -#define FILTER_ID_INPUT -1 - -/* - * The filter chain will always have an input filter to hold - * the pre-filtered content. All other filters are added after - * the last filter. - * - * Each filter uses the chunkqueue from the previous filter as - * input and outputs to it's own chunkqueue. - * - */ - -typedef struct filter { - struct filter *prev; - struct filter *next; - - int id; /* use plugin id */ - chunkqueue *cq; /* this filters output */ -} filter; - -typedef struct { - filter *first; - filter *last; - -} filter_chain; - -LI_API filter_chain * filter_chain_init(void); -LI_API void filter_chain_free(filter_chain *chain); -LI_API void filter_chain_reset(filter_chain *chain); - -LI_API filter * filter_chain_create_filter(filter_chain *chain, int id); -LI_API filter * filter_chain_get_filter(filter_chain *chain, int id); -LI_API void filter_chain_remove_filter(filter_chain *chain, filter *fl); - -LI_API off_t filter_chain_copy_output(filter_chain *chain, chunkqueue *out); - -#endif diff --git a/src/http-header-glue.c b/src/http-header-glue.c deleted file mode 100644 index a16eec49..00000000 --- a/src/http-header-glue.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - * make sure _GNU_SOURCE is defined - */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include <string.h> -#include <errno.h> -#include <time.h> - -#include "base.h" -#include "array.h" -#include "buffer.h" -#include "log.h" -#include "etag.h" -#include "response.h" - -/* - * This was 'borrowed' from tcpdump. - * - * - * This is fun. - * - * In older BSD systems, socket addresses were fixed-length, and - * "sizeof (struct sockaddr)" gave the size of the structure. - * All addresses fit within a "struct sockaddr". - * - * In newer BSD systems, the socket address is variable-length, and - * there's an "sa_len" field giving the length of the structure; - * this allows socket addresses to be longer than 2 bytes of family - * and 14 bytes of data. - * - * Some commercial UNIXes use the old BSD scheme, some use the RFC 2553 - * variant of the old BSD scheme (with "struct sockaddr_storage" rather - * than "struct sockaddr"), and some use the new BSD scheme. - * - * Some versions of GNU libc use neither scheme, but has an "SA_LEN()" - * macro that determines the size based on the address family. Other - * versions don't have "SA_LEN()" (as it was in drafts of RFC 2553 - * but not in the final version). On the latter systems, we explicitly - * check the AF_ type to determine the length; we assume that on - * all those systems we have "struct sockaddr_storage". - */ - -#ifdef HAVE_IPV6 -# ifndef SA_LEN -# ifdef HAVE_SOCKADDR_SA_LEN -# define SA_LEN(addr) ((addr)->sa_len) -# else /* HAVE_SOCKADDR_SA_LEN */ -# ifdef HAVE_STRUCT_SOCKADDR_STORAGE -static size_t get_sa_len(const struct sockaddr *addr) { - switch (addr->sa_family) { - -# ifdef AF_INET - case AF_INET: - return (sizeof (struct sockaddr_in)); -# endif - -# ifdef AF_INET6 - case AF_INET6: - return (sizeof (struct sockaddr_in6)); -# endif - - default: - return (sizeof (struct sockaddr)); - - } -} -# define SA_LEN(addr) (get_sa_len(addr)) -# else /* HAVE_SOCKADDR_STORAGE */ -# define SA_LEN(addr) (sizeof (struct sockaddr)) -# endif /* HAVE_SOCKADDR_STORAGE */ -# endif /* HAVE_SOCKADDR_SA_LEN */ -# endif /* SA_LEN */ -#endif - - - - -int response_header_insert(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) { - data_string *ds; - - UNUSED(srv); - - if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { - ds = data_response_init(); - } - buffer_copy_string_len(ds->key, key, keylen); - buffer_copy_string_len(ds->value, value, vallen); - - array_insert_unique(con->response.headers, (data_unset *)ds); - - return 0; -} - -int response_header_overwrite(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) { - data_string *ds; - - UNUSED(srv); - - /* if there already is a key by this name overwrite the value */ - if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key, keylen))) { - buffer_copy_string(ds->value, value); - - return 0; - } - - return response_header_insert(srv, con, key, keylen, value, vallen); -} - - -int response_header_append(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) { - data_string *ds; - - UNUSED(srv); - - /* if there already is a key by this name append the value */ - if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key, keylen))) { - buffer_append_string_len(ds->value, CONST_STR_LEN(", ")); - buffer_append_string_len(ds->value, value, vallen); - return 0; - } - - return response_header_insert(srv, con, key, keylen, value, vallen); -} - -int http_response_redirect_to_directory(server *srv, connection *con) { - buffer *o; - - o = buffer_init(); - - if (con->conf.is_ssl) { - buffer_copy_string_len(o, CONST_STR_LEN("https://")); - } else { - buffer_copy_string_len(o, CONST_STR_LEN("http://")); - } - if (con->uri.authority->used) { - buffer_append_string_buffer(o, con->uri.authority); - } else { - /* get the name of the currently connected socket */ - struct hostent *he; -#ifdef HAVE_IPV6 - char hbuf[256]; -#endif - sock_addr our_addr; - socklen_t our_addr_len; - - our_addr_len = sizeof(our_addr); - - if (-1 == getsockname(con->sock->fd, &(our_addr.plain), &our_addr_len)) { - con->http_status = 500; - - log_error_write(srv, __FILE__, __LINE__, "ss", - "can't get sockname", strerror(errno)); - - buffer_free(o); - return 0; - } - - - /* Lookup name: secondly try to get hostname for bind address */ - switch(our_addr.plain.sa_family) { -#ifdef HAVE_IPV6 - case AF_INET6: - if (0 != getnameinfo((const struct sockaddr *)(&our_addr.ipv6), - SA_LEN((const struct sockaddr *)&our_addr.ipv6), - hbuf, sizeof(hbuf), NULL, 0, 0)) { - - char dst[INET6_ADDRSTRLEN]; - - ERROR("NOTICE: getnameinfo() failed: %s, using ip-address instead", - strerror(errno)); - - buffer_append_string(o, - inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr, - dst, sizeof(dst))); - } else { - buffer_append_string(o, hbuf); - } - break; -#endif - case AF_INET: - if (NULL == (he = gethostbyaddr((char *)&our_addr.ipv4.sin_addr, sizeof(struct in_addr), AF_INET))) { - ERROR("NOTICE: gethostbyaddr() failed: %d, using ip-address instead", - h_errno); - - buffer_append_string(o, inet_ntoa(our_addr.ipv4.sin_addr)); - } else { - buffer_append_string(o, he->h_name); - } - break; - default: - ERROR("ERROR: unsupported address-type, %d", our_addr.plain.sa_family); - - buffer_free(o); - return -1; - } - - if (!((con->conf.is_ssl == 0 && srv->srvconf.port == 80) || - (con->conf.is_ssl == 1 && srv->srvconf.port == 443))) { - buffer_append_string_len(o, CONST_STR_LEN(":")); - buffer_append_long(o, srv->srvconf.port); - } - } - buffer_append_string_buffer(o, con->uri.path); - buffer_append_string_len(o, CONST_STR_LEN("/")); - if (!buffer_is_empty(con->uri.query)) { - buffer_append_string_len(o, CONST_STR_LEN("?")); - buffer_append_string_buffer(o, con->uri.query); - } - - response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(o)); - - con->http_status = 301; - con->send->is_closed = 1; /* no content */ - - buffer_free(o); - - return 0; -} - -buffer * strftime_cache_get(server *srv, time_t last_mod) { - struct tm *tm; - size_t i; - - for (i = 0; i < FILE_CACHE_MAX; i++) { - /* found cache-entry */ - if (srv->mtime_cache[i].mtime == last_mod) return srv->mtime_cache[i].str; - - /* found empty slot */ - if (srv->mtime_cache[i].mtime == 0) break; - } - - if (i == FILE_CACHE_MAX) { - i = 0; - } - - srv->mtime_cache[i].mtime = last_mod; - buffer_prepare_copy(srv->mtime_cache[i].str, 1024); - tm = gmtime(&(srv->mtime_cache[i].mtime)); - srv->mtime_cache[i].str->used = strftime(srv->mtime_cache[i].str->ptr, - srv->mtime_cache[i].str->size - 1, - "%a, %d %b %Y %H:%M:%S GMT", tm); - srv->mtime_cache[i].str->used++; - - return srv->mtime_cache[i].str; -} - - -int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) { - data_string *http_if_none_match; - data_string *http_if_modified_since; - - 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. - */ - - http_if_none_match = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("if-none-match")); - http_if_modified_since = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("if-modified-since")); - - /* last-modified handling */ - if (http_if_none_match) { - if (etag_is_equal(con->physical.etag, BUF_STR(http_if_none_match->value))) { - if (con->request.http_method == HTTP_METHOD_GET || - con->request.http_method == HTTP_METHOD_HEAD) { - - /* check if etag + last-modified */ - if (http_if_modified_since) { - size_t used_len; - char *semicolon; - - if (NULL == (semicolon = strchr(BUF_STR(http_if_modified_since->value), ';'))) { - used_len = http_if_modified_since->value->used - 1; - } else { - used_len = semicolon - BUF_STR(http_if_modified_since->value); - } - - if (0 == strncmp(BUF_STR(http_if_modified_since->value), mtime->ptr, used_len)) { - if ('\0' == mtime->ptr[used_len]) con->http_status = 304; - return HANDLER_FINISHED; - } else { -#ifdef HAVE_STRPTIME - char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")]; - time_t t_header, t_file; - struct tm tm; - - /* check if we can safely copy the string */ - if (used_len >= sizeof(buf)) { - TRACE("last-mod check failed as timestamp was too long: %s: %zu, %zu", - SAFE_BUF_STR(http_if_modified_since->value), - used_len, sizeof(buf) - 1); - - con->http_status = 412; - return HANDLER_FINISHED; - } - - - strncpy(buf, BUF_STR(http_if_modified_since->value), used_len); - buf[used_len] = '\0'; - - if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) { - con->http_status = 412; - return HANDLER_FINISHED; - } - 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; -#else - return HANDLER_GO_ON; -#endif - } - } else { - con->http_status = 304; - return HANDLER_FINISHED; - } - } else { - con->http_status = 412; - return HANDLER_FINISHED; - } - } - } else if (http_if_modified_since) { - size_t used_len; - char *semicolon; - - if (NULL == (semicolon = strchr(BUF_STR(http_if_modified_since->value), ';'))) { - used_len = http_if_modified_since->value->used - 1; - } else { - used_len = semicolon - BUF_STR(http_if_modified_since->value); - } - - if (0 == strncmp(BUF_STR(http_if_modified_since->value), mtime->ptr, used_len)) { - if ('\0' == mtime->ptr[used_len]) con->http_status = 304; - return HANDLER_FINISHED; - } else { -#ifdef HAVE_STRPTIME - 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; - - strncpy(buf, BUF_STR(http_if_modified_since->value), used_len); - buf[used_len] = '\0'; - - if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) { - 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; -#else - return HANDLER_GO_ON; -#endif - } - } - - return HANDLER_GO_ON; -} diff --git a/src/http_auth.c b/src/http_auth.c deleted file mode 100644 index 357f4a1b..00000000 --- a/src/http_auth.c +++ /dev/null @@ -1,1252 +0,0 @@ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_CRYPT_H -# include <crypt.h> -#elif defined(__linux__) -/* linux needs _XOPEN_SOURCE */ -#endif - -#ifdef HAVE_LIBCRYPT -# define HAVE_CRYPT -#endif - -#include <sys/types.h> -#include <sys/stat.h> - -#include <fcntl.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <time.h> -#include <errno.h> -#include <ctype.h> - -#include "server.h" -#include "log.h" -#include "http_auth.h" -#include "http_auth_digest.h" -#include "stream.h" - -#include "inet_ntop_cache.h" - -#include "sys-strings.h" -#include "sys-files.h" - -#ifdef USE_OPENSSL -# include <openssl/md5.h> -#else -# include "md5.h" - -typedef li_MD5_CTX MD5_CTX; -#define MD5_Init li_MD5_Init -#define MD5_Update li_MD5_Update -#define MD5_Final li_MD5_Final - -#endif - -/** - * 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 - * ---------------------------------------------------------------------------- - */ - -handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s); -#ifdef USE_LDAP -void auth_ldap_cleanup(ldap_plugin_config *p); -#endif - -static const char base64_pad = '='; - -/* "A-Z a-z 0-9 + /" maps to 0-63 */ -static const short base64_reverse_table[256] = { -/* 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, /* 0x00 - 0x0F */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 - 0x1F */ - -1, -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, -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, -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 */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 - 0x8F */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 - 0x9F */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 - 0xAF */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 - 0xBF */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 - 0xCF */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 - 0xDF */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 - 0xEF */ - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xF0 - 0xFF */ -}; - - -static unsigned char * base64_decode(buffer *out, const char *in) { - unsigned char *result; - int ch, j = 0, k; - size_t i; - - size_t in_len = strlen(in); - - buffer_prepare_copy(out, in_len); - - result = (unsigned char *)out->ptr; - - ch = in[0]; - /* run through the whole string, converting as we go */ - for (i = 0; i < in_len; i++) { - ch = (unsigned char) in[i]; - - if (ch == '\0') break; - - if (ch == base64_pad) break; - - ch = base64_reverse_table[ch]; - if (ch < 0) continue; - - switch(i % 4) { - case 0: - result[j] = ch << 2; - break; - case 1: - result[j++] |= ch >> 4; - result[j] = (ch & 0x0f) << 4; - break; - case 2: - result[j++] |= ch >>2; - result[j] = (ch & 0x03) << 6; - break; - case 3: - result[j++] |= ch; - break; - } - } - k = j; - /* mop things up if we ended on a boundary */ - if (ch == base64_pad) { - switch(i % 4) { - case 0: - case 1: - return NULL; - case 2: - k++; - case 3: - result[k++] = 0; - } - } - result[k] = '\0'; - - out->used = k; - - return result; -} - -static int http_auth_get_password(server *srv, mod_auth_plugin_data *p, buffer *username, buffer *realm, buffer *password) { - int ret = -1; - - if (!username->used|| !realm->used) return -1; - - if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) { - stream f; - char * f_line; - - if (buffer_is_empty(p->conf.auth_htdigest_userfile)) return -1; - - if (0 != stream_open(&f, p->conf.auth_htdigest_userfile)) { - log_error_write(srv, __FILE__, __LINE__, "sbss", "opening digest-userfile", p->conf.auth_htdigest_userfile, "failed:", strerror(errno)); - - return -1; - } - - f_line = f.start; - - while (f_line - f.start != f.size) { - char *f_user, *f_pwd, *e, *f_realm; - size_t u_len, pwd_len, r_len; - - f_user = f_line; - - /* - * htdigest format - * - * user:realm:md5(user:realm:password) - */ - - if (NULL == (f_realm = memchr(f_user, ':', f.size - (f_user - f.start) ))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "parsed error in", p->conf.auth_htdigest_userfile, - "expected 'username:realm:hashed password'"); - - stream_close(&f); - - return -1; - } - - if (NULL == (f_pwd = memchr(f_realm + 1, ':', f.size - (f_realm + 1 - f.start)))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "parsed error in", p->conf.auth_plain_userfile, - "expected 'username:realm:hashed password'"); - - stream_close(&f); - - return -1; - } - - /* get pointers to the fields */ - u_len = f_realm - f_user; - f_realm++; - r_len = f_pwd - f_realm; - f_pwd++; - - if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) { - pwd_len = e - f_pwd; - } else { - pwd_len = f.size - (f_pwd - f.start); - } - - if (username->used - 1 == u_len && - (realm->used - 1 == r_len) && - (0 == strncmp(username->ptr, f_user, u_len)) && - (0 == strncmp(realm->ptr, f_realm, r_len))) { - /* found */ - - buffer_copy_string_len(password, f_pwd, pwd_len); - - ret = 0; - break; - } - - /* EOL */ - if (!e) break; - - f_line = e + 1; - } - - stream_close(&f); - } else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD || - p->conf.auth_backend == AUTH_BACKEND_PLAIN) { - stream f; - char * f_line; - buffer *auth_fn; - - auth_fn = (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) ? p->conf.auth_htpasswd_userfile : p->conf.auth_plain_userfile; - - if (buffer_is_empty(auth_fn)) return -1; - - if (0 != stream_open(&f, auth_fn)) { - log_error_write(srv, __FILE__, __LINE__, "sbss", - "opening plain-userfile", auth_fn, "failed:", strerror(errno)); - - return -1; - } - - f_line = f.start; - - while (f_line - f.start != f.size) { - char *f_user, *f_pwd, *e; - size_t u_len, pwd_len; - - f_user = f_line; - - /* - * htpasswd format - * - * user:crypted passwd - */ - - if (NULL == (f_pwd = memchr(f_user, ':', f.size - (f_user - f.start) ))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "parsed error in", auth_fn, - "expected 'username:hashed password'"); - - stream_close(&f); - - return -1; - } - - /* get pointers to the fields */ - u_len = f_pwd - f_user; - f_pwd++; - - if (NULL != (e = memchr(f_pwd, '\n', f.size - (f_pwd - f.start)))) { - pwd_len = e - f_pwd; - } else { - pwd_len = f.size - (f_pwd - f.start); - } - - if (username->used - 1 == u_len && - (0 == strncmp(username->ptr, f_user, u_len))) { - /* found */ - - buffer_copy_string_len(password, f_pwd, pwd_len); - - ret = 0; - break; - } - - /* EOL */ - if (!e) break; - - f_line = e + 1; - } - - stream_close(&f); - } else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) { - ret = 0; - } else { - return -1; - } - - return ret; -} - -static int http_auth_match_rules(server *srv, mod_auth_plugin_data *p, const char *url, const char *username, const char *group, const char *host) { - const char *r = NULL, *rules = NULL; - size_t i; - int username_len; - data_string *require; - array *req; - - UNUSED(group); - UNUSED(host); - - /* check what has to be match to fullfil the request */ - /* search auth-directives for path */ - for (i = 0; i < p->conf.auth_require->used; i++) { - if (p->conf.auth_require->data[i]->key->used == 0) continue; - - if (0 == strncmp(url, p->conf.auth_require->data[i]->key->ptr, p->conf.auth_require->data[i]->key->used - 1)) { - break; - } - } - - if (i == p->conf.auth_require->used) { - return -1; - } - - req = ((data_array *)(p->conf.auth_require->data[i]))->value; - - require = (data_string *)array_get_element(req, CONST_STR_LEN("require")); - - /* if we get here, the user we got a authed user */ - if (buffer_is_equal_string(require->value, CONST_STR_LEN("valid-user"))) { - return 0; - } - - /* user=name1|group=name3|host=name4 */ - - /* seperate the string by | */ -#if 0 - log_error_write(srv, __FILE__, __LINE__, "sb", "rules", require->value); -#endif - - username_len = username ? strlen(username) : 0; - - r = rules = require->value->ptr; - - while (1) { - const char *eq; - const char *k, *v, *e; - int k_len, v_len, r_len; - - e = strchr(r, '|'); - - if (e) { - r_len = e - r; - } else { - r_len = strlen(rules) - (r - rules); - } - - /* from r to r + r_len is a rule */ - - if (0 == strncmp(r, "valid-user", r_len)) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "parsing the 'require' section in 'auth.require' failed: valid-user cannot be combined with other require rules", - require->value); - return -1; - } - - /* search for = in the rules */ - if (NULL == (eq = strchr(r, '='))) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "parsing the 'require' section in 'auth.require' failed: a = is missing", - require->value); - return -1; - } - - /* = out of range */ - if (eq > r + r_len) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "parsing the 'require' section in 'auth.require' failed: = out of range", - require->value); - - return -1; - } - - /* the part before the = is user|group|host */ - - k = r; - k_len = eq - r; - v = eq + 1; - v_len = r_len - k_len - 1; - - if (k_len == 4) { - if (0 == strncmp(k, "user", k_len)) { - if (username && - username_len == v_len && - 0 == strncmp(username, v, v_len)) { - return 0; - } - } else if (0 == strncmp(k, "host", k_len)) { - log_error_write(srv, __FILE__, __LINE__, "s", "host ... (not implemented)"); - } else { - log_error_write(srv, __FILE__, __LINE__, "s", "unknown key"); - return -1; - } - } else if (k_len == 5) { - if (0 == strncmp(k, "group", k_len)) { - log_error_write(srv, __FILE__, __LINE__, "s", "group ... (not implemented)"); - } else { - log_error_write(srv, __FILE__, __LINE__, "ss", "unknown key", k); - return -1; - } - } else { - log_error_write(srv, __FILE__, __LINE__, "s", "unknown key"); - return -1; - } - - if (!e) break; - r = e + 1; - } - - log_error_write(srv, __FILE__, __LINE__, "s", "nothing matched"); - - return -1; -} - -#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 at the top of this file. - */ - -static void to64(char *s, unsigned long v, int n) -{ - static 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; - 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..' - */ - MD5_Init(&ctx); - - /* - * The password first, since that is what is most unknown - */ - MD5_Update(&ctx, (const unsigned char*) pw, strlen(pw)); - - /* - * Then our magic string - */ - MD5_Update(&ctx, (const unsigned char*) APR1_ID, strlen(APR1_ID)); - - /* - * Then the raw salt - */ - MD5_Update(&ctx, (const unsigned char*) sp, sl); - - /* - * Then just as many characters of the MD5(pw, salt, pw) - */ - MD5_Init(&ctx1); - MD5_Update(&ctx1, (const unsigned char*) pw, strlen(pw)); - MD5_Update(&ctx1, (const unsigned char*) sp, sl); - MD5_Update(&ctx1, (const unsigned char*) pw, strlen(pw)); - MD5_Final(final, &ctx1); - for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) { - 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) { - MD5_Update(&ctx, final, 1); - } - else { - MD5_Update(&ctx, (const unsigned char*) 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, "$"); - - 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++) { - MD5_Init(&ctx1); - if (i & 1) { - MD5_Update(&ctx1, (const unsigned char*) pw, strlen(pw)); - } - else { - MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); - } - if (i % 3) { - MD5_Update(&ctx1, (const unsigned char*) sp, sl); - } - - if (i % 7) { - MD5_Update(&ctx1, (const unsigned char*) pw, strlen(pw)); - } - - if (i & 1) { - MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); - } - else { - MD5_Update(&ctx1, (const unsigned char*) pw, strlen(pw)); - } - 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. - */ - memset(final, 0, sizeof(final)); - - /* FIXME - */ -#define apr_cpystrn strncpy - apr_cpystrn(result, passwd, nbytes - 1); -} - - -/** - * - * - * @param password password-string from the auth-backend - * @param pw password-string from the client - */ - -static int http_auth_basic_password_compare(server *srv, mod_auth_plugin_data *p, array *req, buffer *username, buffer *realm, buffer *password, const char *pw) { - UNUSED(srv); - UNUSED(req); - - if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) { - /* - * htdigest format - * - * user:realm:md5(user:realm:password) - */ - - MD5_CTX Md5Ctx; - HASH HA1; - char a1[256]; - - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)username->ptr, username->used - 1); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)realm->ptr, realm->used - 1); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw)); - MD5_Final(HA1, &Md5Ctx); - - CvtHex(HA1, a1); - - if (buffer_is_equal_string(password, a1, strlen(a1))) { - return 0; - } - } else if (p->conf.auth_backend == AUTH_BACKEND_HTPASSWD) { - char sample[120]; - 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)); - return (strcmp(sample, password->ptr) == 0) ? 0 : 1; - } else { -#ifdef HAVE_CRYPT - char salt[32]; - char *crypted; - size_t salt_len = 0; - /* - * htpasswd format - * - * user:crypted password - */ - - /* - * Algorithm Salt - * CRYPT_STD_DES 2-character (Default) - * CRYPT_EXT_DES 9-character - * CRYPT_MD5 12-character beginning with $1$ - * CRYPT_BLOWFISH 16-character beginning with $2$ - */ - - if (password->used < 13 + 1) { - fprintf(stderr, "%s.%d\n", __FILE__, __LINE__); - return -1; - } - - if (password->used == 13 + 1) { - /* a simple DES password is 2 + 11 characters */ - salt_len = 2; - } else if (password->ptr[0] == '$' && password->ptr[2] == '$') { - char *dollar = NULL; - - if (NULL == (dollar = strchr(password->ptr + 3, '$'))) { - fprintf(stderr, "%s.%d\n", __FILE__, __LINE__); - return -1; - } - - salt_len = dollar - password->ptr; - } - - if (salt_len > sizeof(salt) - 1) { - fprintf(stderr, "%s.%d\n", __FILE__, __LINE__); - return -1; - } - - strncpy(salt, password->ptr, salt_len); - - salt[salt_len] = '\0'; - - crypted = crypt(pw, salt); - - if (buffer_is_equal_string(password, crypted, strlen(crypted))) { - return 0; - } else { - fprintf(stderr, "%s.%d\n", __FILE__, __LINE__); - } - -#endif - } - } else if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) { - if (buffer_is_equal_string(password, pw, strlen(pw))) { - return 0; - } - } else if (p->conf.auth_backend == AUTH_BACKEND_LDAP) { -#ifdef USE_LDAP - LDAP *ldap = NULL; - LDAPMessage *lm = NULL, *first = NULL; - struct berval credentials; - char *dn = NULL; - int ret = 0; - char *attrs[] = { LDAP_NO_ATTRS, NULL }; - size_t i = 0; - - /* for now we stay synchronous */ - - /* - * 1. connect anonymously (done in plugin init) - * 2. get DN for uid = username - * 3. auth against ldap server - * 4. (optional) check a field - * 5. disconnect - * - */ - - /* check username - * - * we have to protect us againt username which modifies out filter in - * a unpleasant way - */ - - for (i = 0; i < username->used - 1; i++) { - char c = username->ptr[i]; - - if (!isalpha(c) && - !isdigit(c) && - (c != ' ') && - (c != '@') && - (c != '-') && - (c != '_') && - (c != '.') ) { - - log_error_write(srv, __FILE__, __LINE__, "sbd", - "ldap: invalid character (- _.@a-zA-Z0-9 allowed) in username:", username, i); - - return -1; - } - } - - if (p->conf.auth_ldap_allow_empty_pw != 1 && pw[0] == '\0') - return -1; - - /* 2. */ - - if (p->conf.ldap->ldap == NULL) { - if(auth_ldap_init(srv, &p->conf) != HANDLER_GO_ON) - return -1; - } - - /* build filter */ - buffer_copy_string_buffer(p->ldap_filter, p->conf.ldap->ldap_filter_pre); - buffer_append_string_buffer(p->ldap_filter, username); - buffer_append_string_buffer(p->ldap_filter, p->conf.ldap->ldap_filter_post); - - ret = ldap_search_ext_s(p->conf.ldap->ldap, p->conf.auth_ldap_basedn->ptr, LDAP_SCOPE_SUBTREE, p->ldap_filter->ptr, attrs, 0, NULL, NULL, NULL, 0, &lm); - - if (ret == LDAP_SERVER_DOWN) { - log_error_write(srv, __FILE__, __LINE__, "s", - "ldap: server down, try to reconnect"); - - auth_ldap_cleanup(p->conf.ldap); - - if(auth_ldap_init(srv, &p->conf) != HANDLER_GO_ON) - return -1; - - log_error_write(srv, __FILE__, __LINE__, "s", - "ldap: successfully reconnected"); - } - - if (ret != LDAP_SUCCESS) { - log_error_write(srv, __FILE__, __LINE__, "sssb", - "ldap:", ldap_err2string(ret), ", filter:", p->ldap_filter); - - return -1; - } - - if (ldap_count_entries(p->conf.ldap->ldap, lm) > 1) { - log_error_write(srv, __FILE__, __LINE__, "ssb", - "ldap:", "more than one record returned, you might have to refine the filter:", p->ldap_filter); - } - - first = ldap_first_entry(p->conf.ldap->ldap, lm); - if (first == NULL) { - ldap_get_option(p->conf.ldap->ldap, LDAP_OPT_ERROR_NUMBER, &ret); - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - - ldap_msgfree(lm); - - return -1; - } - - dn = ldap_get_dn(p->conf.ldap->ldap, first); - if (dn == NULL) { - ldap_get_option(p->conf.ldap->ldap, LDAP_OPT_ERROR_NUMBER, &ret); - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - - ldap_msgfree(lm); - - return -1; - } - - ldap_msgfree(lm); - - /* 3. */ - ret = ldap_initialize(&ldap, p->conf.auth_ldap_url->ptr); - if (ret) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - - ldap_memfree(dn); - - return -1; - } - - { - const int ldap_version = LDAP_VERSION3; - ret = ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version); - } - if (ret != LDAP_OPT_SUCCESS) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - - ldap_memfree(ldap); - ldap_memfree(dn); - - return -1; - } - - if (p->conf.auth_ldap_starttls == 1) { - ret = ldap_start_tls_s(ldap, NULL, NULL); - if (ret != LDAP_OPT_SUCCESS) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret)); - - ldap_memfree(ldap); - ldap_memfree(dn); - - return -1; - } - } - - /* Don't initialize it using " = {...}" since upstream doesn't - * guarantee an order and the compiler only issues a warning when - * passing an int as the pointer and vice-versa. - * We have to cast away the const since berval is a general struct, - * but ldap_sasl_bind_s doesn't write to the *pw location - */ - credentials.bv_val = (char*)pw; - credentials.bv_len = strlen(pw); - - /* TODO: add funtionality to specify LDAP_SASL_EXTERNAL (or GSS-SPNEGO, etc.) */ - ret = ldap_sasl_bind_s(ldap, dn, LDAP_SASL_SIMPLE, &credentials, NULL, NULL, NULL); - if (ret != LDAP_SUCCESS) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - - ldap_memfree(ldap); - ldap_memfree(dn); - - return -1; - } - - /* 5. */ - ldap_unbind_ext_s(ldap, NULL, NULL); - ldap_memfree(dn); - - /* everything worked, good, access granted */ - - return 0; -#endif - } - return -1; -} - -int http_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, buffer *url, const char *realm_str) { - buffer *username, *password; - char *pw; - - data_string *realm; - - realm = (data_string *)array_get_element(req, CONST_STR_LEN("realm")); - - username = buffer_init(); - - if (!base64_decode(username, realm_str)) { - log_error_write(srv, __FILE__, __LINE__, "sb", "decodeing base64-string failed", username); - - buffer_free(username); - return 0; - } - - /* r2 == user:password */ - if (NULL == (pw = strchr(username->ptr, ':'))) { - log_error_write(srv, __FILE__, __LINE__, "sb", ": is missing in", username); - - buffer_free(username); - return 0; - } - - *pw++ = '\0'; - - username->used = pw - username->ptr; - - password = buffer_init(); - - /* copy password to r1 */ - if (http_auth_get_password(srv, p, username, realm->value, password)) { - buffer_free(username); - buffer_free(password); - - log_error_write(srv, __FILE__, __LINE__, "ss", "get_password failed, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); - - return 0; - } - - /* password doesn't match */ - if (http_auth_basic_password_compare(srv, p, req, username, realm->value, password, pw)) { - log_error_write(srv, __FILE__, __LINE__, "sbsBss", "password doesn't match for", con->uri.path, "username:", username, ", IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); - - buffer_free(username); - buffer_free(password); - - return 0; - } - - /* value is our allow-rules */ - if (http_auth_match_rules(srv, p, url->ptr, username->ptr, NULL, NULL)) { - buffer_free(username); - buffer_free(password); - - log_error_write(srv, __FILE__, __LINE__, "s", "rules didn't match"); - - return 0; - } - - /* remember the username */ - buffer_copy_string_buffer(p->auth_user, username); - - buffer_free(username); - buffer_free(password); - - return 1; -} - -typedef struct { - const char *key; - int key_len; - char **ptr; -} digest_kv; - -int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, buffer *url, const char *realm_str) { - char a1[256]; - char a2[256]; - - char *username; - char *realm; - char *nonce; - char *uri; - char *algorithm; - char *qop; - char *cnonce; - char *nc; - char *respons; - - char *e, *c; - const char *m = NULL; - int i; - buffer *password, *b, *username_buf, *realm_buf; - - 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; - dkv[9].ptr = NULL; - - UNUSED(req); - - for (i = 0; dkv[i].key; i++) { - *(dkv[i].ptr) = NULL; - } - - - if (p->conf.auth_backend != AUTH_BACKEND_HTDIGEST && - p->conf.auth_backend != AUTH_BACKEND_PLAIN) { - log_error_write(srv, __FILE__, __LINE__, "s", - "digest: unsupported backend (only htdigest or plain)"); - - return -1; - } - - b = buffer_init_string(realm_str); - - /* 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; - } - } - } - } - - if (p->conf.auth_debug > 1) { - log_error_write(srv, __FILE__, __LINE__, "ss", "username", username); - log_error_write(srv, __FILE__, __LINE__, "ss", "realm", realm); - log_error_write(srv, __FILE__, __LINE__, "ss", "nonce", nonce); - log_error_write(srv, __FILE__, __LINE__, "ss", "uri", uri); - log_error_write(srv, __FILE__, __LINE__, "ss", "algorigthm", algorithm); - log_error_write(srv, __FILE__, __LINE__, "ss", "qop", qop); - log_error_write(srv, __FILE__, __LINE__, "ss", "cnonce", cnonce); - log_error_write(srv, __FILE__, __LINE__, "ss", "nc", nc); - log_error_write(srv, __FILE__, __LINE__, "ss", "response", respons); - } - - /* 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 -1; - } - - /** - * 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 -1; - } - - m = get_http_method_name(con->request.http_method); - - /* password-string == HA1 */ - password = buffer_init(); - username_buf = buffer_init_string(username); - realm_buf = buffer_init_string(realm); - if (http_auth_get_password(srv, p, username_buf, realm_buf, password)) { - buffer_free(password); - buffer_free(b); - buffer_free(username_buf); - buffer_free(realm_buf); - return 0; - } - - buffer_free(username_buf); - buffer_free(realm_buf); - - if (p->conf.auth_backend == AUTH_BACKEND_PLAIN) { - /* generate password from plain-text */ - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)username, strlen(username)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)password->ptr, password->used - 1); - MD5_Final(HA1, &Md5Ctx); - } else if (p->conf.auth_backend == AUTH_BACKEND_HTDIGEST) { - /* HA1 */ - /* transform the 32-byte-hex-md5 to a 16-byte-md5 */ - for (i = 0; i < HASHLEN; i++) { - HA1[i] = hex2int(password->ptr[i*2]) << 4; - HA1[i] |= hex2int(password->ptr[i*2+1]); - } - } else { - /* we already check that above */ - SEGFAULT("p->conf.auth_backend is %d", p->conf.auth_backend); - } - - buffer_free(password); - - if (algorithm && - strcasecmp(algorithm, "md5-sess") == 0) { - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)HA1, 16); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); - MD5_Final(HA1, &Md5Ctx); - } - - CvtHex(HA1, a1); - - /* calculate H(A2) */ - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)m, strlen(m)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)uri, strlen(uri)); - if (qop && strcasecmp(qop, "auth-int") == 0) { - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)"", HASHHEXLEN); - } - MD5_Final(HA2, &Md5Ctx); - CvtHex(HA2, HA2Hex); - - /* calculate response */ - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)a1, HASHHEXLEN); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)nonce, strlen(nonce)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - if (qop && *qop) { - MD5_Update(&Md5Ctx, (unsigned char *)nc, strlen(nc)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)cnonce, strlen(cnonce)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - MD5_Update(&Md5Ctx, (unsigned char *)qop, strlen(qop)); - MD5_Update(&Md5Ctx, (unsigned char *)":", 1); - }; - MD5_Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN); - MD5_Final(RespHash, &Md5Ctx); - CvtHex(RespHash, a2); - - if (0 != strcmp(a2, respons)) { - /* digest not ok */ - - if (p->conf.auth_debug) { - log_error_write(srv, __FILE__, __LINE__, "sss", - "digest: digest mismatch", a2, respons); - } - - log_error_write(srv, __FILE__, __LINE__, "ssss", - "digest: auth failed for ", username, ": wrong password, IP:", inet_ntop_cache_get_ip(srv, &(con->dst_addr))); - - buffer_free(b); - return 0; - } - - /* value is our allow-rules */ - if (http_auth_match_rules(srv, p, url->ptr, username, NULL, NULL)) { - buffer_free(b); - - log_error_write(srv, __FILE__, __LINE__, "s", - "digest: rules did match"); - - return 0; - } - - /* remember the username */ - buffer_copy_string(p->auth_user, username); - - buffer_free(b); - - if (p->conf.auth_debug) { - log_error_write(srv, __FILE__, __LINE__, "s", - "digest: auth ok"); - } - return 1; -} - - -int http_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char out[33]) { - HASH h; - MD5_CTX Md5Ctx; - char hh[32]; - - UNUSED(p); - - /* generate shared-secret */ - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)fn->ptr, fn->used - 1); - MD5_Update(&Md5Ctx, (unsigned char *)"+", 1); - - /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */ - LI_ltostr(hh, srv->cur_ts); - MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); - MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy)); - LI_ltostr(hh, rand()); - MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); - - MD5_Final(h, &Md5Ctx); - - CvtHex(h, out); - - return 0; -} diff --git a/src/http_auth.h b/src/http_auth.h deleted file mode 100644 index 84674fdb..00000000 --- a/src/http_auth.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef _HTTP_AUTH_H_ -#define _HTTP_AUTH_H_ - -#include "server.h" -#include "plugin.h" - -#if defined(HAVE_LDAP_H) && defined(HAVE_LBER_H) && defined(HAVE_LIBLDAP) && defined(HAVE_LIBLBER) -# define USE_LDAP -# include <ldap.h> -#endif - -typedef enum { - AUTH_BACKEND_UNSET, - AUTH_BACKEND_PLAIN, - AUTH_BACKEND_LDAP, - AUTH_BACKEND_HTPASSWD, - AUTH_BACKEND_HTDIGEST -} auth_backend_t; - -#ifdef USE_LDAP -typedef struct { - LDAP *ldap; - - buffer *ldap_filter_pre; - buffer *ldap_filter_post; -} ldap_plugin_config; -#endif - -typedef struct { - /* auth */ - array *auth_require; - - buffer *auth_plain_groupfile; - buffer *auth_plain_userfile; - - buffer *auth_htdigest_userfile; - buffer *auth_htpasswd_userfile; - - buffer *auth_backend_conf; - - buffer *auth_ldap_url; - buffer *auth_ldap_basedn; - buffer *auth_ldap_binddn; - buffer *auth_ldap_bindpw; - buffer *auth_ldap_filter; - buffer *auth_ldap_cafile; - buffer *auth_ldap_cert; - buffer *auth_ldap_key; - unsigned short auth_ldap_starttls; - unsigned short auth_ldap_allow_empty_pw; - - unsigned short auth_debug; - - /* generated */ - auth_backend_t auth_backend; - -#ifdef USE_LDAP - ldap_plugin_config *ldap; -#endif -} mod_auth_plugin_config; - -typedef struct { - PLUGIN_DATA; - buffer *tmp_buf; - - buffer *auth_user; - -#ifdef USE_LDAP - buffer *ldap_filter; -#endif - - mod_auth_plugin_config **config_storage; - - mod_auth_plugin_config conf; /* this is only used as long as no handler_ctx is setup */ -} mod_auth_plugin_data; - -int http_auth_basic_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, buffer *url, const char *realm_str); -int http_auth_digest_check(server *srv, connection *con, mod_auth_plugin_data *p, array *req, buffer *url, const char *realm_str); -int http_auth_digest_generate_nonce(server *srv, mod_auth_plugin_data *p, buffer *fn, char hh[33]); - -#endif diff --git a/src/http_auth_digest.c b/src/http_auth_digest.c deleted file mode 100644 index 27b933ae..00000000 --- a/src/http_auth_digest.c +++ /dev/null @@ -1,25 +0,0 @@ -#include <string.h> -#include "http_auth_digest.h" - -#include "buffer.h" - -#ifndef USE_OPENSSL -# include "md5.h" - -typedef li_MD5_CTX MD5_CTX; -#define MD5_Init li_MD5_Init -#define MD5_Update li_MD5_Update -#define MD5_Final li_MD5_Final - -#endif - -void CvtHex(IN HASH Bin, OUT HASHHEX Hex) { - unsigned short i; - - for (i = 0; i < HASHLEN; i++) { - Hex[i*2] = int2hex((Bin[i] >> 4) & 0xf); - Hex[i*2+1] = int2hex(Bin[i] & 0xf); - } - Hex[HASHHEXLEN] = '\0'; -} - diff --git a/src/http_auth_digest.h b/src/http_auth_digest.h deleted file mode 100644 index 25d95ee4..00000000 --- a/src/http_auth_digest.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _DIGCALC_H_ -#define _DIGCALC_H_ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define HASHLEN 16 -typedef unsigned char HASH[HASHLEN]; -#define HASHHEXLEN 32 -typedef char HASHHEX[HASHHEXLEN+1]; -#ifdef USE_OPENSSL -#define IN const -#else -#define IN -#endif -#define OUT - -void CvtHex( - IN HASH Bin, - OUT HASHHEX Hex - ); - -#endif diff --git a/src/http_parser.h b/src/http_parser.h deleted file mode 100644 index ba86f16a..00000000 --- a/src/http_parser.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _HTTP_PARSER_H_ -#define _HTTP_PARSER_H_ - -typedef enum { - PARSE_UNSET, - PARSE_SUCCESS, - PARSE_ERROR, - PARSE_NEED_MORE -} parse_status_t; - -#endif diff --git a/src/http_req.c b/src/http_req.c deleted file mode 100644 index 004ca303..00000000 --- a/src/http_req.c +++ /dev/null @@ -1,323 +0,0 @@ -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <assert.h> - -#include "log.h" -#include "http_req.h" -#include "http_req_parser.h" - -typedef struct { - chunkqueue *cq; - - chunk *c; /* current chunk in the chunkqueue */ - size_t offset; /* current offset in current chunk */ - - chunk *lookup_c; - size_t lookup_offset; - - int last_token_id; - - int is_key; - int is_statusline; -} http_req_tokenizer_t; - -http_req *http_request_init(void) { - http_req *req = calloc(1, sizeof(*req)); - - req->uri_raw = buffer_init(); - req->headers = array_init(); - - return req; -} - -void http_request_reset(http_req *req) { - if (!req) return; - - buffer_reset(req->uri_raw); - array_reset(req->headers); - -} - -void http_request_free(http_req *req) { - if (!req) return; - - buffer_free(req->uri_raw); - array_free(req->headers); - - free(req); -} - -static int http_req_get_next_char(http_req_tokenizer_t *t, unsigned char *c) { - if (t->c->mem->used == 0) { - TRACE("chunk-len: %zd", t->c->mem->used); - } - - if (t->offset == t->c->mem->used - 1) { - /* end of chunk, open next chunk */ - - if (!t->c->next) return -1; - - t->c = t->c->next; - /* skip empty chunks */ - while (t->c && t->c->mem->used == 0) t->c = t->c->next; - if (!t->c) return -1; - - t->offset = 0; - } - - *c = t->c->mem->ptr[t->offset++]; - - t->lookup_offset = t->offset; - t->lookup_c = t->c; - -#if 0 - fprintf(stderr, "%s.%d: get: %c (%d) at offset: %d\r\n", __FILE__, __LINE__, *c > 31 ? *c : ' ', *c, t->offset - 1); -#endif - - return 0; -} - -static int http_req_lookup_next_char(http_req_tokenizer_t *t, unsigned char *c) { - if (t->lookup_c->mem->used == 0) { - TRACE("chunk-len: %zd", t->lookup_c->mem->used); - } - if (t->lookup_offset == t->lookup_c->mem->used - 1) { - /* end of chunk, open next chunk */ - - if (!t->lookup_c->next) return -1; - - t->lookup_c = t->lookup_c->next; - - /* skip empty chunks */ - while (t->lookup_c && t->lookup_c->mem->used == 0) t->lookup_c = t->lookup_c->next; - if (!t->lookup_c) return -1; - - t->lookup_offset = 0; - } - - *c = t->lookup_c->mem->ptr[t->lookup_offset++]; -#if 0 - fprintf(stderr, "%s.%d: lookup: %c (%d) at offset: %d\r\n", __FILE__, __LINE__, *c > 31 ? *c : ' ', *c, t->lookup_offset - 1); -#endif - - return 0; -} - -typedef enum { - PARSER_UNSET, - PARSER_OK, - PARSER_ERROR, - PARSER_EOF -} http_req_parser_t; - -static http_req_parser_t http_req_tokenizer( - http_req_tokenizer_t *t, - int *token_id, - buffer *token -) { - unsigned char c; - int tid = 0; - - /* push the token to the parser */ - - while (tid == 0 && 0 == http_req_get_next_char(t, &c)) { - switch (c) { - case ':': - tid = TK_COLON; - - t->is_key = 0; - - break; - case ' ': - case '\t': - if (t->last_token_id == TK_CRLF) { - /* WS as the start of a line */ - - tid = TK_TAB; - t->is_key = 0; - } - /* ignore the rest of the WS-chars */ - break; - case '\r': - if (0 != http_req_lookup_next_char(t, &c)) return PARSER_EOF; - - if (c == '\n') { - tid = TK_CRLF; - - t->c = t->lookup_c; - t->offset = t->lookup_offset; - - t->is_statusline = 0; - t->is_key = 1; - } else { - ERROR("CR with out LF at pos: %zu", t->offset); - return PARSER_ERROR; - } - break; - case '\n': - tid = TK_CRLF; - - t->is_statusline = 0; - t->is_key = 1; - - break; - default: - while (c >= 32 && c != 127 && c != 255) { - if (t->is_statusline) { - if (c == 32) break; /* the space is a splitter in the statusline */ - } else { - if (t->is_key) { - if (c == ':') break; /* the : is the splitter between key and value */ - if (c == ' ') break; /* no spaces in keys */ - } - } - if (0 != http_req_lookup_next_char(t, &c)) return PARSER_EOF; - } - - if (t->c == t->lookup_c && - t->offset == t->lookup_offset + 1) { - - ERROR("invalid char (%d) at pos: %zu", c, t->offset); - return PARSER_ERROR; - } - - tid = TK_STRING; - - /* the lookup points to the first invalid char */ - t->lookup_offset--; - - /* no overlapping string */ - if (t->c == t->lookup_c) { - buffer_copy_string_len(token, t->c->mem->ptr + t->offset - 1, t->lookup_offset - t->offset + 1); - } else { - /* first chunk */ - buffer_copy_string_len(token, t->c->mem->ptr + t->offset - 1, t->c->mem->used - t->offset); - - /* chunks in the middle */ - for (t->c = t->c->next; t->c != t->lookup_c; t->c = t->c->next) { - buffer_append_string_buffer(token, t->c->mem); - t->offset = t->c->mem->used - 1; - } - - /* last chunk */ - buffer_append_string_len(token, t->c->mem->ptr, t->lookup_offset); - } - - t->offset = t->lookup_offset; - - break; - } - } - - if (tid) { - *token_id = tid; - - return PARSER_OK; - } - - return PARSER_EOF; -} - -parse_status_t http_request_parse_cq(chunkqueue *cq, http_req *req) { - http_req_tokenizer_t t; - void *pParser = NULL; - int token_id = 0; - buffer *token = NULL; - http_req_ctx_t context; - parse_status_t ret = PARSE_UNSET; - http_req_parser_t parser_ret; - - t.cq = cq; - t.c = cq->first; - t.offset = t.c->offset; - t.is_key = 0; - t.is_statusline = 1; - t.last_token_id = 0; - - context.ok = 1; - context.errmsg = buffer_init(); - context.req = req; - context.unused_buffers = buffer_pool_init(); - - pParser = http_req_parserAlloc( malloc ); - token = buffer_init(); - - array_reset(req->headers); - - while((PARSER_OK == (parser_ret = http_req_tokenizer(&t, &token_id, token))) && context.ok) { - http_req_parser(pParser, token_id, token, &context); - - token = buffer_pool_get(context.unused_buffers); - - /* CRLF CRLF ... the header end sequence */ - if (t.last_token_id == TK_CRLF && - token_id == TK_CRLF) break; - - t.last_token_id = token_id; - } - - // Tokenizer failed - if (parser_ret == PARSER_ERROR) { - ret = PARSE_ERROR; - } - - /* oops, the parser failed */ - if (context.ok == 0) { - ret = PARSE_ERROR; - - if (!buffer_is_empty(context.errmsg)) { - TRACE("parsing failed: %s", SAFE_BUF_STR(context.errmsg)); - } else { - chunk *c; - buffer *hdr = buffer_init(); - - for (c = cq->first; c; c = c->next) { - if (c == cq->first) { - buffer_append_string_len(hdr, c->mem->ptr + c->offset, c->mem->used - 1 - c->offset); - } else { - buffer_append_string_buffer(hdr, c->mem); - } - } - - TRACE("parsing failed at token (%s [%d]), header: %s", SAFE_BUF_STR(token), token_id, SAFE_BUF_STR(hdr)); - - buffer_free(hdr); - } - } - - http_req_parser(pParser, 0, token, &context); - http_req_parserFree(pParser, free); - - if (context.ok == 0) { - /* we are missing the some tokens */ - - if (!buffer_is_empty(context.errmsg)) { - TRACE("parsing failed: %s", SAFE_BUF_STR(context.errmsg)); - } - - if (ret == PARSE_UNSET) { - ret = buffer_is_empty(context.errmsg) ? PARSE_NEED_MORE : PARSE_ERROR; - } - } else if (parser_ret == PARSER_EOF) { // didn't see CRLF CRLF, no other error till now - ret = PARSE_NEED_MORE; - } else { - chunk *c; - - for (c = cq->first; c != t.c; c = c->next) { - c->offset = c->mem->used - 1; - } - - c->offset = t.offset; - - ret = PARSE_SUCCESS; - } - - buffer_pool_append(context.unused_buffers, token); - buffer_pool_free(context.unused_buffers); - buffer_free(context.errmsg); - - return ret; -} - - diff --git a/src/http_req.h b/src/http_req.h deleted file mode 100644 index 54b4a087..00000000 --- a/src/http_req.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef _HTTP_REQ_H_ -#define _HTTP_REQ_H_ - -#include <stdio.h> - -#include "array.h" -#include "chunk.h" -#include "http_parser.h" - -typedef struct { - int protocol; /* http/1.0, http/1.1 */ - int method; /* e.g. GET */ - buffer *uri_raw; /* e.g. /foobar/ */ - array *headers; -} http_req; - -typedef struct { - int ok; - buffer *errmsg; - - http_req *req; - buffer_pool *unused_buffers; -} http_req_ctx_t; - -LI_API http_req * http_request_init(void); -LI_API void http_request_free(http_req *req); -LI_API void http_request_reset(http_req *req); - -LI_API parse_status_t http_request_parse_cq(chunkqueue *cq, http_req *http_request); - -/* declare prototypes for the parser */ -void *http_req_parserAlloc(void *(*mallocProc)(size_t)); -void http_req_parserFree(void *p, void (*freeProc)(void*)); -void http_req_parserTrace(FILE *TraceFILE, char *zTracePrompt); -void http_req_parser(void *, int, buffer *, http_req_ctx_t *); - -#endif diff --git a/src/http_req_parser.y b/src/http_req_parser.y deleted file mode 100644 index 54c2673a..00000000 --- a/src/http_req_parser.y +++ /dev/null @@ -1,160 +0,0 @@ -%token_prefix TK_ -%token_type {buffer *} -%extra_argument {http_req_ctx_t *ctx} -%name http_req_parser - -%include { -#include <assert.h> -#include <string.h> -#include "http_req.h" -#include "keyvalue.h" -#include "array.h" -#include "log.h" -} - -%parse_failure { - ctx->ok = 0; -} - -%type protocol { http_version_t } -%type method { http_method_t } -%type request_hdr { http_req * } -%type headers { array * } -%type header { data_string * } -%type multiline { buffer * } -%token_destructor { buffer_free($$); } - -/** -* GET ... HTTP/1.0 -* Host: ... -*/ -request_hdr ::= method(B) STRING(C) protocol(D) CRLF headers CRLF . { - http_req *req = ctx->req; - - req->method = B; - req->protocol = D; - buffer_copy_string_buffer(req->uri_raw, C); - buffer_pool_append(ctx->unused_buffers, C); -} - -/** -* \r\n -* GET ... HTTP/1.0\r\n -* Host: ...\r\n -* \r\n -*/ -request_hdr ::= CRLF method(B) STRING(C) protocol(D) CRLF headers CRLF . { - http_req *req = ctx->req; - - req->method = B; - req->protocol = D; - buffer_copy_string_buffer(req->uri_raw, C); - buffer_pool_append(ctx->unused_buffers, C); -} - - -/** -* GET ... HTTP/1.0\r\n -* \r\n -* -*/ -request_hdr ::= method(B) STRING(C) protocol(D) CRLF CRLF . { - http_req *req = ctx->req; - - req->method = B; - req->protocol = D; - buffer_copy_string_buffer(req->uri_raw, C); - buffer_pool_append(ctx->unused_buffers, C); -} - -/** -* \r\n -* GET ... HTTP/1.0\r\n -* \r\n -* -*/ -request_hdr ::= CRLF method(B) STRING(C) protocol(D) CRLF CRLF . { - http_req *req = ctx->req; - - req->method = B; - req->protocol = D; - buffer_copy_string_buffer(req->uri_raw, C); - buffer_pool_append(ctx->unused_buffers, C); -} - - -method(A) ::= STRING(B) . { - A = get_http_method_key(BUF_STR(B)); - - buffer_pool_append(ctx->unused_buffers, B); -} - -protocol(A) ::= STRING(B). { - /* the protocol might be HTTP/1.0 or HTTP/1.1 - * the version string is allowed to have leading zeros - */ - A = HTTP_VERSION_UNSET; - - if (0 == strncmp(BUF_STR(B), "HTTP/", 5)) { - char *err = NULL; - /* is there a dot */ - char *major, *minor; - - major = BUF_STR(B) + 5; - minor = strchr(major, '.'); - if (minor) { - int hi, lo; - hi = strtol(major, &err, 10); - minor++; - if (*err == '.' && *minor != '\0') { - lo = strtol(minor, &err, 10); - if (*err == '\0') { - if (hi == 1 && lo == 1) { - A = HTTP_VERSION_1_1; - } else if (hi == 1 && lo == 0) { - A = HTTP_VERSION_1_0; - } - } - } - } - } - - buffer_pool_append(ctx->unused_buffers, B); -} - -headers ::= headers header. -headers ::= header. - -header(HDR) ::= STRING(A) COLON multiline(B). { - http_req *req = ctx->req; - - if (NULL == (HDR = (data_string *)array_get_unused_element(req->headers, TYPE_STRING))) { - HDR = data_string_init(); - } - - buffer_copy_string_buffer(HDR->key, A); - buffer_copy_string_buffer(HDR->value, B); - buffer_pool_append(ctx->unused_buffers, A); - buffer_pool_append(ctx->unused_buffers, B); - - array_insert_unique(req->headers, (data_unset *)HDR); -} - -header ::= STRING COLON CRLF . - -multiline(A) ::= STRING(B) CRLF TAB multiline(C). { - buffer_append_string_buffer(B, C); - A = B; - - B = NULL; - buffer_pool_append(ctx->unused_buffers, C); -} - -/* the simple form */ -multiline(A) ::= STRING(B) CRLF. { - A = B; - - B = NULL; -} - - diff --git a/src/http_req_range.c b/src/http_req_range.c deleted file mode 100644 index 434959bb..00000000 --- a/src/http_req_range.c +++ /dev/null @@ -1,188 +0,0 @@ -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <assert.h> - -#include "log.h" -#include "http_req_range.h" -#include "http_req_range_parser.h" - -typedef struct { - buffer *hdr; - size_t ndx; - - chunk *lookup_c; - size_t lookup_offset; -} http_req_range_tokenizer_t; - -http_req_range *http_request_range_init(void) { - http_req_range *range = calloc(1, sizeof(*range)); - - range->start = -1; - range->end = -1; - - return range; -} - -void http_request_range_reset(http_req_range *range) { - if (!range) return; - - http_request_range_free(range->next); - - range->next = NULL; - - range->start = -1; - range->end = -1; -} - -void http_request_range_free(http_req_range *range) { - if (!range) return; - - http_request_range_free(range->next); - - free(range); -} - -static int http_req_range_tokenizer( - http_req_range_tokenizer_t *t, - int *token_id, - buffer *token -) { - int tid = 0; - - /* push the token to the parser */ - - while (tid == 0) { - char c = t->hdr->ptr[t->ndx]; - - switch (c) { - case '-': - tid = TK_MINUS; - t->ndx++; - - break; - case '=': - tid = TK_EQUAL; - t->ndx++; - - break; - case ',': - tid = TK_COMMA; - t->ndx++; - - break; - - case ' ': - case '\t': - /* ignore WS */ - t->ndx++; - break; - case '\0': - return 0; - default: - /* 'bytes' or a number */ - if (0 == strncmp(t->hdr->ptr + t->ndx, "bytes", 5)) { - tid = TK_BYTES; - - t->ndx += 5; - } else { - size_t d; - /* */ - for (d = t->ndx; d < t->hdr->used; d++) { - char dc = t->hdr->ptr[d]; - if (dc < '0' || dc > '9') { - break; - } - } - - if (d == t->ndx) { - /* no digit found */ - - TRACE("%s", "no digit found"); - - return -1; - } - - tid = TK_NUMBER; - - buffer_copy_string_len(token, t->hdr->ptr + t->ndx, d - t->ndx); - - t->ndx = d; - } - - break; - } - } - - if (tid) { - *token_id = tid; - - return 1; - } - - return -1; -} - -parse_status_t http_request_range_parse(buffer *hdr, http_req_range *ranges) { - http_req_range_tokenizer_t t; - void *pParser = NULL; - int token_id = 0; - buffer *token = NULL; - http_req_range_ctx_t context; - parse_status_t ret = PARSE_UNSET; - - t.hdr = hdr; - t.ndx = 0; - - context.ok = 1; - context.errmsg = buffer_init(); - context.ranges = ranges; - context.unused_buffers = buffer_pool_init(); - - pParser = http_req_range_parserAlloc( malloc ); - token = buffer_pool_get(context.unused_buffers); -#if 0 - http_req_range_parserTrace(stderr, "range: "); -#endif - - while((1 == http_req_range_tokenizer(&t, &token_id, token)) && context.ok) { - http_req_range_parser(pParser, token_id, token, &context); - - token = buffer_pool_get(context.unused_buffers); - } - - /* oops, the parser failed */ - if (context.ok == 0) { - ret = PARSE_ERROR; - - if (!buffer_is_empty(context.errmsg)) { - TRACE("parsing failed: %s", SAFE_BUF_STR(context.errmsg)); - } else { - TRACE("%s", "parsing failed ..."); - } - } - - http_req_range_parser(pParser, 0, token, &context); - http_req_range_parserFree(pParser, free); - - if (context.ok == 0) { - /* we are missing the some tokens */ - - if (!buffer_is_empty(context.errmsg)) { - TRACE("parsing failed: %s", SAFE_BUF_STR(context.errmsg)); - } - - if (ret == PARSE_UNSET) { - ret = buffer_is_empty(context.errmsg) ? PARSE_NEED_MORE : PARSE_ERROR; - } - } else { - ret = PARSE_SUCCESS; - } - - buffer_pool_append(context.unused_buffers, token); - buffer_pool_free(context.unused_buffers); - buffer_free(context.errmsg); - - return ret; -} - diff --git a/src/http_req_range.h b/src/http_req_range.h deleted file mode 100644 index 32a862aa..00000000 --- a/src/http_req_range.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef _HTTP_REQ_RANGE_H_ -#define _HTTP_REQ_RANGE_H_ - -#include <stdio.h> - -#include "array.h" -#include "chunk.h" -#include "http_parser.h" - -typedef struct _http_req_range { - off_t start; - off_t end; - struct _http_req_range *next; -} http_req_range; - -typedef struct { - int ok; - buffer *errmsg; - - http_req_range *ranges; - - buffer_pool *unused_buffers; -} http_req_range_ctx_t; - -LI_API http_req_range * http_request_range_init(void); -LI_API void http_request_range_free(http_req_range *range); -LI_API void http_request_range_reset(http_req_range *range); - -LI_API parse_status_t http_request_range_parse(buffer *range_hdr, http_req_range *ranges); - -/* declare prototypes for the parser */ -void *http_req_range_parserAlloc(void *(*mallocProc)(size_t)); -void http_req_range_parserFree(void *p, void (*freeProc)(void*)); -void http_req_range_parserTrace(FILE *TraceFILE, char *zTracePrompt); -void http_req_range_parser(void *, int, buffer *, http_req_range_ctx_t *); - -#endif diff --git a/src/http_req_range_parser.y b/src/http_req_range_parser.y deleted file mode 100644 index 62241fb3..00000000 --- a/src/http_req_range_parser.y +++ /dev/null @@ -1,76 +0,0 @@ -%token_prefix TK_ -%token_type {buffer *} -%extra_argument {http_req_range_ctx_t *ctx} -%name http_req_range_parser - -%include { -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include <sys/types.h> -#include <string.h> -#include "http_req_range.h" -#include "log.h" -#include "sys-strings.h" -} - -%parse_failure { - ctx->ok = 0; -} - -%type num { off_t } -%type range { http_req_range * } -%type ranges { http_req_range * } -%token_destructor { buffer_free($$); } - - -range_hdr ::= BYTES EQUAL ranges(A) . { - ctx->ranges->start = A->start; - ctx->ranges->end = A->end; - ctx->ranges->next = A->next; - - A->next = NULL; - http_request_range_free(A); -} - -ranges(A) ::= ranges(B) COMMA range(C) . { - for (A = B; A->next; A = A->next); - - A->next = C; - - A = B; -} -ranges(A) ::= range(B) . { - A = B; -} -range(A) ::= num(B) MINUS . { - http_req_range *r = http_request_range_init(); - - r->start = B; - r->end = -1; - - A = r; -} - -range(A) ::= num(B) MINUS num(C) . { - http_req_range *r = http_request_range_init(); - - r->start = B; - r->end = C; - - A = r; -} - -range(A) ::= MINUS num(B) . { - http_req_range *r = http_request_range_init(); - - r->start = -1; - r->end = B; - - A = r; -} - -num(A) ::= NUMBER(B) . { - A = strtoull(BUF_STR(B), NULL, 10); - buffer_pool_append(ctx->unused_buffers, B); -} diff --git a/src/http_req_range_test.c b/src/http_req_range_test.c deleted file mode 100644 index 3532582c..00000000 --- a/src/http_req_range_test.c +++ /dev/null @@ -1,72 +0,0 @@ -#include <stdio.h> -#include <assert.h> -#include <string.h> -#include <stdint.h> - -#include <tap.h> - -#include "http_req_range.h" -#include "log.h" - -int main(void) { - http_req_range *r, *ranges = http_request_range_init(); - buffer *b = buffer_init(); - - log_init(); - plan_tests(7); - - buffer_copy_string_len(b, CONST_STR_LEN("bytes=0-0")); - ok(PARSE_SUCCESS == http_request_range_parse(b, ranges), "0-0"); - for (r = ranges; r; r = r->next) { - diag(".. %jd - %jd", (intmax_t) r->start, (intmax_t) r->end); - } - http_request_range_reset(ranges); - - buffer_copy_string_len(b, CONST_STR_LEN("bytes=1-2,3-4")); - ok(PARSE_SUCCESS == http_request_range_parse(b, ranges), "1-2,3-4"); - for (r = ranges; r; r = r->next) { - diag(".. %jd - %jd", (intmax_t) r->start, (intmax_t) r->end); - } - http_request_range_reset(ranges); - - buffer_copy_string_len(b, CONST_STR_LEN("bytes=-0")); - ok(PARSE_SUCCESS == http_request_range_parse(b, ranges), "-0"); - for (r = ranges; r; r = r->next) { - diag(".. %jd - %jd", (intmax_t) r->start, (intmax_t) r->end); - } - http_request_range_reset(ranges); - - buffer_copy_string_len(b, CONST_STR_LEN("bytes=0-")); - ok(PARSE_SUCCESS == http_request_range_parse(b, ranges), "0-"); - for (r = ranges; r; r = r->next) { - diag(".. %jd - %jd", (intmax_t) r->start, (intmax_t) r->end); - } - http_request_range_reset(ranges); - - buffer_copy_string_len(b, CONST_STR_LEN("bytes=0-0,0-")); - ok(PARSE_SUCCESS == http_request_range_parse(b, ranges), "0-0,0-"); - for (r = ranges; r; r = r->next) { - diag(".. %jd - %jd", (intmax_t) r->start, (intmax_t) r->end); - } - http_request_range_reset(ranges); - - buffer_copy_string_len(b, CONST_STR_LEN("bytes=0-0,-0")); - ok(PARSE_SUCCESS == http_request_range_parse(b, ranges), "0-0,-0"); - for (r = ranges; r; r = r->next) { - diag(".. %jd - %jd", (intmax_t) r->start, (intmax_t) r->end); - } - http_request_range_reset(ranges); - - buffer_copy_string_len(b, CONST_STR_LEN("bytes=1-2,3-4,5-")); - ok(PARSE_SUCCESS == http_request_range_parse(b, ranges), "1-2,3-4,5-"); - for (r = ranges; r; r = r->next) { - diag(".. %jd - %jd", (intmax_t) r->start, (intmax_t) r->end); - } - - http_request_range_free(ranges); - - buffer_free(b); - log_free(); - - return exit_status(); -} diff --git a/src/http_req_test.c b/src/http_req_test.c deleted file mode 100644 index d53c0b85..00000000 --- a/src/http_req_test.c +++ /dev/null @@ -1,141 +0,0 @@ -#include <stdio.h> -#include <assert.h> -#include <string.h> - -#include <tap.h> - -#include "http_req.h" -#include "log.h" - -const char* chunkqueue_to_buffer(chunkqueue *cq, buffer *b) { - chunk *c; - - buffer_reset(b); - - for (c = cq->first; c; c = c->next) { - buffer_append_string(b, c->mem->ptr + c->offset); - } - - return b->ptr; -} - -int main(void) { - http_req *req = http_request_init(); - chunkqueue *cq = chunkqueue_init(); - buffer *b, *content = buffer_init(); - const char *body; - - log_init(); - plan_tests(8); - - /* basic request header + CRLF */ - b = chunkqueue_get_append_buffer(cq); - - buffer_copy_string(b, - "GET / HTTP/1.0\r\n" - "Location: foobar\r\n" - "Content-Lenght: 24\r\n" - "\r\nABC" - ); - - ok(PARSE_SUCCESS == http_request_parse_cq(cq, req), "basic GET header"); - - chunkqueue_remove_finished_chunks(cq); - body = chunkqueue_to_buffer(cq, content); - ok(0 == strcmp("ABC", body), "content is ABC, got %s", body); - - http_request_free(req); - - /* line-wrapping */ - - chunkqueue_reset(cq); - req = http_request_init(); - - b = chunkqueue_get_append_buffer(cq); - buffer_copy_string(b, - "GET /server-status HTTP/1.0\r\n" - "User-Agent: Wget/1.9.1\r\n" - "Authorization: Digest username=\"jan\", realm=\"jan\", nonce=\"9a5428ccc05b086a08d918e73b01fc6f\",\r\n" - " uri=\"/server-status\", response=\"ea5f7d9a30b8b762f9610ccb87dea74f\"\r\n" - "\r\n" - ); - - ok(PARSE_SUCCESS == http_request_parse_cq(cq, req), "POST request with line-wrapping"); - - chunkqueue_remove_finished_chunks(cq); - - http_request_free(req); - - /* no request line */ - - chunkqueue_reset(cq); - req = http_request_init(); - - b = chunkqueue_get_append_buffer(cq); - - buffer_copy_string(b, - "Location: foobar\r\n" - "Content-Lenght: 24\r\n" - "\r\nABC" - ); - - ok(PARSE_ERROR == http_request_parse_cq(cq, req), "missing request-line"); - - http_request_free(req); - - /* LF as line-ending */ - - chunkqueue_reset(cq); - req = http_request_init(); - - b = chunkqueue_get_append_buffer(cq); - - buffer_copy_string(b, - "GET / HTTP/1.0\n" - "\nABC" - ); - ok(PARSE_SUCCESS == http_request_parse_cq(cq, req), "no request key-value pairs"); - - chunkqueue_remove_finished_chunks(cq); - body = chunkqueue_to_buffer(cq, content); - ok(0 == strcmp("ABC", body), "content is ABC, got %s", body); - - /* LF as line-ending */ - - chunkqueue_reset(cq); - req = http_request_init(); - - b = chunkqueue_get_append_buffer(cq); - - buffer_copy_string(b, - "GE"); - - b = chunkqueue_get_append_buffer(cq); - buffer_copy_string_len(b, CONST_STR_LEN("T ")); - b = chunkqueue_get_append_buffer(cq); - buffer_copy_string_len(b, CONST_STR_LEN("/foo")); - b = chunkqueue_get_append_buffer(cq); - buffer_copy_string_len(b, CONST_STR_LEN("bar HTTP/1.0\r")); - - b = chunkqueue_get_append_buffer(cq); - buffer_copy_string(b, "\n" - "Locati"); - - b = chunkqueue_get_append_buffer(cq); - buffer_copy_string(b, "on: foobar\r\n" - "Content-Lenght: 24\r\n" - "\r\nABC" - ); - ok(PARSE_SUCCESS == http_request_parse_cq(cq, req), "POST request with line-wrapping"); - - chunkqueue_remove_finished_chunks(cq); - body = chunkqueue_to_buffer(cq, content); - ok(0 == strcmp("ABC", body), "content is ABC, got %s", body); - - http_request_free(req); - chunkqueue_free(cq); - buffer_free(content); - log_free(); - - return exit_status(); -} diff --git a/src/http_resp.c b/src/http_resp.c deleted file mode 100644 index 23b2e58c..00000000 --- a/src/http_resp.c +++ /dev/null @@ -1,303 +0,0 @@ -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <assert.h> - -#include "log.h" -#include "http_resp.h" -#include "http_resp_parser.h" - -typedef struct { - chunkqueue *cq; - - chunk *c; /* current chunk in the chunkqueue */ - size_t offset; /* current offset in current chunk */ - - chunk *lookup_c; - size_t lookup_offset; - - int is_key; - int is_statusline; -} http_resp_tokenizer_t; - -http_resp *http_response_init(void) { - http_resp *resp = calloc(1, sizeof(*resp)); - - resp->reason = buffer_init(); - resp->headers = array_init(); - resp->status = -1; - - return resp; -} - -void http_response_reset(http_resp *resp) { - if (!resp) return; - - buffer_reset(resp->reason); - array_reset(resp->headers); - resp->status = -1; - -} - -void http_response_free(http_resp *resp) { - if (!resp) return; - - buffer_free(resp->reason); - array_free(resp->headers); - - free(resp); -} - -static int http_resp_get_next_char(http_resp_tokenizer_t *t, unsigned char *c) { - if (t->offset == t->c->mem->used - 1) { - /* end of chunk, open next chunk */ - - if (!t->c->next) return -1; - - t->c = t->c->next; - t->offset = 0; - } - - *c = t->c->mem->ptr[t->offset++]; - - t->lookup_offset = t->offset; - t->lookup_c = t->c; - -#if 0 - fprintf(stderr, "%s.%d: get: %c (%d) at offset: %d\r\n", __FILE__, __LINE__, *c > 31 ? *c : ' ', *c, t->offset - 1); -#endif - - return 0; -} - -static int http_resp_lookup_next_char(http_resp_tokenizer_t *t, unsigned char *c) { - if (t->lookup_offset == t->lookup_c->mem->used - 1) { - /* end of chunk, open next chunk */ - - if (!t->lookup_c->next) return -1; - - t->lookup_c = t->lookup_c->next; - t->lookup_offset = 0; - } - - *c = t->lookup_c->mem->ptr[t->lookup_offset++]; -#if 0 - fprintf(stderr, "%s.%d: lookup: %c (%d) at offset: %d\r\n", __FILE__, __LINE__, *c > 31 ? *c : ' ', *c, t->lookup_offset - 1); -#endif - - return 0; -} - -typedef enum { - PARSER_UNSET, - PARSER_OK, - PARSER_ERROR, - PARSER_EOF -} http_resp_parser_t; - -static http_resp_parser_t http_resp_tokenizer( - http_resp_tokenizer_t *t, - int *token_id, - buffer *token -) { - unsigned char c; - int tid = 0; - - /* push the token to the parser */ - - while (tid == 0 && 0 == http_resp_get_next_char(t, &c)) { - switch (c) { - case ':': - tid = TK_COLON; - - t->is_key = 0; - - break; - case ' ': - case '\t': - /* ignore WS */ - - break; - case '\r': - if (0 != http_resp_lookup_next_char(t, &c)) return -1; - - if (c == '\n') { - tid = TK_CRLF; - - t->c = t->lookup_c; - t->offset = t->lookup_offset; - - t->is_statusline = 0; - t->is_key = 1; - } else { - ERROR("CR with out LF at pos: %zu", t->offset); - return PARSER_ERROR; - } - break; - case '\n': - tid = TK_CRLF; - - t->is_statusline = 0; - t->is_key = 1; - - break; - default: - while (c >= 32 && c != 127 && c != 255) { - if (t->is_statusline) { - if (t->is_key && c == ':') { t->is_statusline = 0; break; } /* this is not a status line by a real header */ - if (c == 32) { t->is_key = 0; break; } /* the space is a splitter in the statusline */ - } else { - if (t->is_key) { - if (c == ':') break; /* the : is the splitter between key and value */ - } - } - if (0 != http_resp_lookup_next_char(t, &c)) return PARSER_EOF; - } - - if (t->c == t->lookup_c && - t->offset == t->lookup_offset + 1) { - - ERROR("invalid char (%d) at pos: %zu", c, t->offset); - return PARSER_ERROR; - } - - tid = TK_STRING; - - /* the lookup points to the first invalid char */ - t->lookup_offset--; - - /* no overlapping string */ - if (t->c == t->lookup_c) { - buffer_copy_string_len(token, t->c->mem->ptr + t->offset - 1, t->lookup_offset - t->offset + 1); - } else { - /* first chunk */ - buffer_copy_string_len(token, t->c->mem->ptr + t->offset - 1, t->c->mem->used - t->offset); - - /* chunks in the middle */ - for (t->c = t->c->next; t->c != t->lookup_c; t->c = t->c->next) { - buffer_append_string_buffer(token, t->c->mem); - t->offset = t->c->mem->used - 1; - } - - /* last chunk */ - buffer_append_string_len(token, t->c->mem->ptr, t->lookup_offset); - } - - t->offset = t->lookup_offset; - - break; - } - } - - if (tid) { - *token_id = tid; - - return PARSER_OK; - } - - return PARSER_EOF; -} - -parse_status_t http_response_parse_cq(chunkqueue *cq, http_resp *resp) { - http_resp_tokenizer_t t; - void *pParser = NULL; - int token_id = 0; - buffer *token = NULL; - http_resp_ctx_t context; - parse_status_t ret = PARSE_UNSET; - http_resp_parser_t parser_ret; - int last_token_id = 0; - - if(!cq->first) return PARSE_NEED_MORE; - t.cq = cq; - t.c = cq->first; - t.offset = t.c->offset; - t.is_key = 1; - t.is_statusline = 1; - - context.ok = 1; - context.errmsg = buffer_init(); - context.resp = resp; - context.unused_buffers = buffer_pool_init(); - - array_reset(resp->headers); - resp->status = 0; - - pParser = http_resp_parserAlloc( malloc ); - token = buffer_pool_get(context.unused_buffers); -#if 0 - http_resp_parserTrace(stderr, "http-response: "); -#endif - - while((PARSER_OK == (parser_ret = http_resp_tokenizer(&t, &token_id, token))) && context.ok) { - http_resp_parser(pParser, token_id, token, &context); - - token = buffer_pool_get(context.unused_buffers); - - /* CRLF CRLF ... the header end sequence */ - if (last_token_id == TK_CRLF && - token_id == TK_CRLF) break; - - last_token_id = token_id; - } - - // Tokenizer failed - if (parser_ret == PARSER_ERROR) { - ret = PARSE_ERROR; - } - - /* oops, the parser failed */ - if (context.ok == 0) { - ret = PARSE_ERROR; - - if (!buffer_is_empty(context.errmsg)) { - TRACE("parsing failed: %s", SAFE_BUF_STR(context.errmsg)); - } else { - TRACE("%s", "parsing failed ..."); - } - } - - http_resp_parser(pParser, 0, token, &context); - http_resp_parserFree(pParser, free); - - if (!buffer_is_empty(context.errmsg)) { - TRACE("parsing failed: %s", SAFE_BUF_STR(context.errmsg)); - } - if (context.ok == 0) { - /* we are missing the some tokens */ - - if (!buffer_is_empty(context.errmsg)) { - TRACE("parsing failed: %s", SAFE_BUF_STR(context.errmsg)); - } - - if (ret == PARSE_UNSET) { - ret = buffer_is_empty(context.errmsg) ? PARSE_NEED_MORE : PARSE_ERROR; - } - } else if (parser_ret == PARSER_EOF) { // didn't see CRLF CRLF, no other error till now - ret = PARSE_NEED_MORE; - } else { - chunk *c; - - for (c = cq->first; c != t.c; c = c->next) { - c->offset = c->mem->used - 1; - cq->bytes_out += c->mem->used - 1; - } - - c->offset = t.offset; - cq->bytes_out += t.offset; - - ret = PARSE_SUCCESS; - } - - buffer_pool_append(context.unused_buffers, token); - buffer_pool_free(context.unused_buffers); - buffer_free(context.errmsg); - - if (resp->status && (resp->status < 100 || resp->status > 999)) { - ERROR("invalid status code %i", resp->status); - return PARSE_ERROR; - } - - return ret; -} - diff --git a/src/http_resp.h b/src/http_resp.h deleted file mode 100644 index ccc67f94..00000000 --- a/src/http_resp.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _HTTP_RESP_H_ -#define _HTTP_RESP_H_ - -#include <stdio.h> - -#include "buffer.h" -#include "array.h" -#include "array-static.h" -#include "chunk.h" -#include "http_parser.h" - -typedef struct { - int protocol; /* http/1.0, http/1.1 */ - int status; /* e.g. 200 */ - buffer *reason; /* e.g. Ok */ - array *headers; -} http_resp; - -typedef struct { - int ok; - buffer *errmsg; - - http_resp *resp; - - buffer_pool *unused_buffers; -} http_resp_ctx_t; - -LI_API http_resp * http_response_init(void); -LI_API void http_response_free(http_resp *resp); -LI_API void http_response_reset(http_resp *resp); - -LI_API parse_status_t http_response_parse_cq(chunkqueue *cq, http_resp *http_response); - -/* declare prototypes for the parser */ -void *http_resp_parserAlloc(void *(*mallocProc)(size_t)); -void http_resp_parserFree(void *p, void (*freeProc)(void*)); -void http_resp_parserTrace(FILE *TraceFILE, char *zTracePrompt); -void http_resp_parser(void *, int, buffer *, http_resp_ctx_t *); - -#endif diff --git a/src/http_resp_parser.y b/src/http_resp_parser.y deleted file mode 100644 index 7305f682..00000000 --- a/src/http_resp_parser.y +++ /dev/null @@ -1,143 +0,0 @@ -%token_prefix TK_ -%token_type {buffer *} -%extra_argument {http_resp_ctx_t *ctx} -%name http_resp_parser - -%include { -#include <assert.h> -#include <string.h> -#include "http_resp.h" -#include "keyvalue.h" -#include "array.h" -#include "log.h" -} - -%parse_failure { - ctx->ok = 0; -} - -%type protocol { int } -%type response_hdr { http_resp * } -%type number { int } -%type headers { array * } -%type header { data_string * } -%destructor reason { buffer_free($$); } -%token_destructor { buffer_free($$); } - -/* just headers + Status: ... */ -response_hdr ::= header headers CRLF . { - http_resp *resp = ctx->resp; - data_string *ds; - - resp->protocol = HTTP_VERSION_UNSET; - - buffer_copy_string(resp->reason, ""); /* no reason */ - - if (NULL == (ds = (data_string *)array_get_element(resp->headers, CONST_STR_LEN("Status")))) { - resp->status = 0; - } else { - char *err; - resp->status = strtol(ds->value->ptr, &err, 10); - - if (*err != '\0' && *err != ' ' && *err != '\r') { - buffer_copy_string(ctx->errmsg, "expected a number: "); - buffer_append_string_buffer(ctx->errmsg, ds->value); - buffer_append_string(ctx->errmsg, err); - - ctx->ok = 0; - } - } -} - -/* HTTP-Version SP Status-Code SP Reason-Phrase CRLF ... */ -response_hdr ::= protocol(B) number(C) reason(D) CRLF headers CRLF . { - http_resp *resp = ctx->resp; - - resp->status = C; - resp->protocol = B; - buffer_copy_string_buffer(resp->reason, D); - buffer_pool_append(ctx->unused_buffers, D); -} - -/* HTTP-Version SP Status-Code CRLF ... */ -response_hdr ::= protocol(B) number(C) CRLF headers CRLF . { - http_resp *resp = ctx->resp; - - resp->status = C; - resp->protocol = B; - buffer_reset(resp->reason); -} - -protocol(A) ::= STRING(B). { - if (buffer_is_equal_string(B, CONST_STR_LEN("HTTP/1.0"))) { - A = HTTP_VERSION_1_0; - } else if (buffer_is_equal_string(B, CONST_STR_LEN("HTTP/1.1"))) { - A = HTTP_VERSION_1_1; - } else { - buffer_copy_string(ctx->errmsg, "unknown protocol: "); - buffer_append_string_buffer(ctx->errmsg, B); - - ctx->ok = 0; - } - buffer_pool_append(ctx->unused_buffers, B); -} - -number(A) ::= STRING(B). { - char *err; - A = strtol(B->ptr, &err, 10); - - if (*err != '\0') { - buffer_copy_string(ctx->errmsg, "expected a number, got: "); - buffer_append_string_buffer(ctx->errmsg, B); - - ctx->ok = 0; - } - buffer_pool_append(ctx->unused_buffers, B); -} - -reason(A) ::= STRING(B). { - A = B; -} - -reason(A) ::= reason(C) STRING(B). { - A = C; - - buffer_append_string(A, " "); - buffer_append_string_buffer(A, B); - - buffer_pool_append(ctx->unused_buffers, B); -} - -headers ::= headers header. -headers ::= . - -header(HDR) ::= STRING(A) COLON STRING(B) CRLF. { - http_resp *resp = ctx->resp; - - if (NULL == (HDR = (data_string *)array_get_unused_element(resp->headers, TYPE_STRING))) { - HDR = data_response_init(); - } - - buffer_copy_string_buffer(HDR->key, A); - buffer_copy_string_buffer(HDR->value, B); - buffer_pool_append(ctx->unused_buffers, A); - buffer_pool_append(ctx->unused_buffers, B); - - array_insert_unique(resp->headers, (data_unset *)HDR); -} - -/* empty headers */ -header(HDR) ::= STRING(A) COLON CRLF. { - http_resp *resp = ctx->resp; - - if (NULL == (HDR = (data_string *)array_get_unused_element(resp->headers, TYPE_STRING))) { - HDR = data_response_init(); - } - - buffer_copy_string_buffer(HDR->key, A); - buffer_copy_string(HDR->value, ""); - buffer_pool_append(ctx->unused_buffers, A); - - array_insert_unique(resp->headers, (data_unset *)HDR); -} - diff --git a/src/http_resp_test.c b/src/http_resp_test.c deleted file mode 100644 index 2d91034d..00000000 --- a/src/http_resp_test.c +++ /dev/null @@ -1,141 +0,0 @@ -#include <stdio.h> -#include <string.h> -#include <tap.h> - -#include "http_resp.h" -#include "log.h" - -const char* chunkqueue_to_buffer(chunkqueue *cq, buffer *b) { - chunk *c; - - buffer_reset(b); - - for (c = cq->first; c; c = c->next) { - buffer_append_string(b, c->mem->ptr + c->offset); - } - - return b->ptr; -} - -int main(void) { - http_resp *resp = http_response_init(); - chunkqueue *cq = chunkqueue_init(); - buffer *b, *content = buffer_init(); - const char *body; - - log_init(); - plan_tests(9); - - /* basic response header + CRLF */ - b = chunkqueue_get_append_buffer(cq); - - buffer_copy_string(b, - "HTTP/1.0 304 Not Modified\r\n" - "Location: foobar\r\n" - "Content-Lenght: 24\r\n" - "\r\nABC" - ); - - ok(PARSE_SUCCESS == http_response_parse_cq(cq, resp), "good 304 response with CRLF"); - - chunkqueue_remove_finished_chunks(cq); - body = chunkqueue_to_buffer(cq, content); - ok(0 == strcmp("ABC", body), "content is ABC, got %s", body); - - http_response_free(resp); - - /* line-wrapping */ - - chunkqueue_reset(cq); - resp = http_response_init(); - - b = chunkqueue_get_append_buffer(cq); - buffer_copy_string(b, - "HTTP/1.0 304 Not Modified\n" - "Location: foobar\n" - "Content-Lenght: 24\n" - "\nABC" - ); - - ok(PARSE_SUCCESS == http_response_parse_cq(cq, resp), "good response with LF"); - - chunkqueue_remove_finished_chunks(cq); - body = chunkqueue_to_buffer(cq, content); - ok(0 == strcmp("ABC", body), "content is ABC, got %s", body); - - http_response_free(resp); - - /* no request line */ - - chunkqueue_reset(cq); - resp = http_response_init(); - - b = chunkqueue_get_append_buffer(cq); - - buffer_copy_string(b, - "Status: 200 Foobar\r\n" - "Location: foobar\r\n" - "Content-Lenght: 24\r\n" - "\r\nABC" - ); - - ok(PARSE_SUCCESS == http_response_parse_cq(cq, resp), "Status: 200 ..."); - - http_response_free(resp); - - /* LF as line-ending */ - - chunkqueue_reset(cq); - resp = http_response_init(); - - b = chunkqueue_get_append_buffer(cq); - - buffer_copy_string(b, - "Location: foobar\n" - "\nABC" - ); - ok(PARSE_SUCCESS == http_response_parse_cq(cq, resp), "no Status at all"); - - chunkqueue_remove_finished_chunks(cq); - body = chunkqueue_to_buffer(cq, content); - ok(0 == strcmp("ABC", body), "content is ABC, got %s", body); - - /* LF as line-ending */ - - chunkqueue_reset(cq); - resp = http_response_init(); - - b = chunkqueue_get_append_buffer(cq); - - buffer_copy_string(b, - "HTTP"); - - b = chunkqueue_get_append_buffer(cq); - buffer_copy_string_len(b, CONST_STR_LEN("/1.0 ")); - b = chunkqueue_get_append_buffer(cq); - buffer_copy_string_len(b, CONST_STR_LEN("30")); - b = chunkqueue_get_append_buffer(cq); - buffer_copy_string_len(b, CONST_STR_LEN("4 Not Modified\r")); - - b = chunkqueue_get_append_buffer(cq); - buffer_copy_string(b, "\n" - "Locati"); - - b = chunkqueue_get_append_buffer(cq); - buffer_copy_string(b, "on: foobar\r\n" - "Content-Lenght: 24\r\n" - "\r\nABC" - ); - ok(PARSE_SUCCESS == http_response_parse_cq(cq, resp), "chunked response"); - - chunkqueue_remove_finished_chunks(cq); - body = chunkqueue_to_buffer(cq, content); - ok(0 == strcmp("ABC", body), "content is ABC, got %s", body); - - http_response_free(resp); - chunkqueue_free(cq); - buffer_free(content); - log_free(); - - return exit_status(); -} diff --git a/src/inet_ntop_cache.c b/src/inet_ntop_cache.c deleted file mode 100644 index 51755df3..00000000 --- a/src/inet_ntop_cache.c +++ /dev/null @@ -1,53 +0,0 @@ -#include <sys/types.h> - -#include <string.h> - - -#include "base.h" -#include "inet_ntop_cache.h" -#include "sys-socket.h" - -const char * inet_ntop_cache_get_ip(server *srv, sock_addr *addr) { -#ifdef HAVE_IPV6 - size_t ndx = 0, i; - for (i = 0; i < INET_NTOP_CACHE_MAX; i++) { - if (srv->inet_ntop_cache[i].ts != 0 && srv->inet_ntop_cache[i].family == addr->plain.sa_family) { - if (srv->inet_ntop_cache[i].family == AF_INET6 && - 0 == memcmp(srv->inet_ntop_cache[i].addr.ipv6.s6_addr, addr->ipv6.sin6_addr.s6_addr, 16)) { - /* IPv6 found in cache */ - break; - } else if (srv->inet_ntop_cache[i].family == AF_INET && - srv->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 */ - - i = ndx; - inet_ntop(addr->plain.sa_family, - addr->plain.sa_family == AF_INET6 ? - (const void *) &(addr->ipv6.sin6_addr) : - (const void *) &(addr->ipv4.sin_addr), - srv->inet_ntop_cache[i].b2, INET6_ADDRSTRLEN); - - srv->inet_ntop_cache[i].ts = srv->cur_ts; - srv->inet_ntop_cache[i].family = addr->plain.sa_family; - - if (srv->inet_ntop_cache[i].family == AF_INET) { - srv->inet_ntop_cache[i].addr.ipv4.s_addr = addr->ipv4.sin_addr.s_addr; - } else if (srv->inet_ntop_cache[i].family == AF_INET6) { - memcpy(srv->inet_ntop_cache[i].addr.ipv6.s6_addr, addr->ipv6.sin6_addr.s6_addr, 16); - } - } - - return srv->inet_ntop_cache[i].b2; -#else - UNUSED(srv); - return inet_ntoa(addr->ipv4.sin_addr); -#endif -} diff --git a/src/inet_ntop_cache.h b/src/inet_ntop_cache.h deleted file mode 100644 index b2a769f2..00000000 --- a/src/inet_ntop_cache.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _INET_NTOP_CACHE_H_ -#define _INET_NTOP_CACHE_H_ - -#include "base.h" -LI_API const char * inet_ntop_cache_get_ip(server *srv, sock_addr *addr); - -#endif diff --git a/src/iosocket.c b/src/iosocket.c deleted file mode 100644 index 25d477ff..00000000 --- a/src/iosocket.c +++ /dev/null @@ -1,44 +0,0 @@ -#include <stdlib.h> - -#include "iosocket.h" -#include "sys-socket.h" -#include "sys-files.h" -#include "array-static.h" - -iosocket *iosocket_init(void) { - STRUCT_INIT(iosocket, sock); - - sock->fde_ndx = -1; - sock->fd = -1; - - sock->type = IOSOCKET_TYPE_SOCKET; - -#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT - sock->tlsext_server_name = buffer_init(); -#endif - - return sock; -} - -void iosocket_free(iosocket *sock) { - if (!sock) return; - - if (sock->fd != -1) { - switch (sock->type) { - case IOSOCKET_TYPE_SOCKET: - closesocket(sock->fd); - break; - case IOSOCKET_TYPE_PIPE: - close(sock->fd); - break; - default: - break; - } - } - -#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT - buffer_free(sock->tlsext_server_name); -#endif - - free(sock); -} diff --git a/src/iosocket.h b/src/iosocket.h deleted file mode 100644 index a11dbfe0..00000000 --- a/src/iosocket.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef _IOSOCKET_H_ -#define _IOSOCKET_H_ - -/** - * make sure we know about OPENSSL all the time - * - * if we don't include config.h here we run into different sizes - * for the iosocket-struct depending on config.h include before - * iosocket.h or not - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#if defined HAVE_LIBSSL && defined HAVE_OPENSSL_SSL_H -# define USE_OPENSSL -# include <openssl/ssl.h> -#endif - -#include "settings.h" -#include "buffer.h" - -typedef enum { - IOSOCKET_TYPE_UNSET, - IOSOCKET_TYPE_SOCKET, - IOSOCKET_TYPE_PIPE -} iosocket_t; - -/** - * a non-blocking fd - */ -typedef struct { - int fd; - int fde_ndx; - -#ifdef USE_OPENSSL - SSL *ssl; -#ifndef OPENSSL_NO_TLSEXT - buffer *tlsext_server_name; -#endif -#endif - - iosocket_t type; /**< sendfile on solaris doesn't work on pipes */ -} iosocket; - -LI_API iosocket * iosocket_init(void); -LI_API void iosocket_free(iosocket *sock); - -#endif diff --git a/src/joblist.c b/src/joblist.c deleted file mode 100644 index 8f68f496..00000000 --- a/src/joblist.c +++ /dev/null @@ -1,77 +0,0 @@ -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "base.h" -#include "joblist.h" -#include "log.h" - -void joblist_append(server *srv, connection *con) { - if (con->in_joblist) return; - con->in_joblist = 1; - - if (srv->joblist->size == 0) { - srv->joblist->size = 16; - srv->joblist->ptr = malloc(sizeof(*srv->joblist->ptr) * srv->joblist->size); - } 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); - } - - srv->joblist->ptr[srv->joblist->used++] = con; -} - -void joblist_free(server *srv, connections *joblist) { - UNUSED(srv); - - free(joblist->ptr); - free(joblist); -} - -#ifdef USE_GTHREAD -void joblist_async_append(server *srv, connection *con) { - g_async_queue_push(srv->joblist_queue, con); - - server_wakeup(srv); -} - -void server_wakeup(server *srv) { - if (g_atomic_int_compare_and_exchange(&srv->did_wakeup, 0, 1)) { - write(srv->wakeup_pipe[1], " ", 1); - } -} -#endif - -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); - } 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); - } - - 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/src/joblist.h b/src/joblist.h deleted file mode 100644 index 2f6bb236..00000000 --- a/src/joblist.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _JOB_LIST_H_ -#define _JOB_LIST_H_ - -#include "base.h" - -LI_API void joblist_append(server *srv, connection *con); -LI_API void joblist_free(server *srv, connections *joblist); - -#ifdef USE_GTHREAD -LI_API void joblist_async_append(server *srv, connection *con); - -LI_API void server_wakeup(server *srv); -#endif - -LI_API int fdwaitqueue_append(server *srv, connection *con); -LI_API void fdwaitqueue_free(server *srv, connections *fdwaitqueue); -LI_API connection* fdwaitqueue_unshift(server *srv, connections *fdwaitqueue); - -#endif diff --git a/src/keyvalue.c b/src/keyvalue.c deleted file mode 100644 index a615116d..00000000 --- a/src/keyvalue.c +++ /dev/null @@ -1,390 +0,0 @@ -#include <stdlib.h> -#include <string.h> -#include <stdio.h> - -#include "base.h" -#include "server.h" -#include "keyvalue.h" - -static keyvalue http_versions[] = { - { HTTP_VERSION_1_1, "HTTP/1.1" }, - { HTTP_VERSION_1_0, "HTTP/1.0" }, - { HTTP_VERSION_UNSET, NULL } -}; - -static keyvalue http_methods[] = { - { HTTP_METHOD_GET, "GET" }, - { HTTP_METHOD_POST, "POST" }, - { HTTP_METHOD_HEAD, "HEAD" }, - { HTTP_METHOD_PROPFIND, "PROPFIND" }, - { HTTP_METHOD_PROPPATCH, "PROPPATCH" }, - { HTTP_METHOD_REPORT, "REPORT" }, - { HTTP_METHOD_OPTIONS, "OPTIONS" }, - { HTTP_METHOD_MKCOL, "MKCOL" }, - { HTTP_METHOD_PUT, "PUT" }, - { HTTP_METHOD_DELETE, "DELETE" }, - { HTTP_METHOD_COPY, "COPY" }, - { HTTP_METHOD_MOVE, "MOVE" }, - { HTTP_METHOD_LABEL, "LABEL" }, - { HTTP_METHOD_CHECKOUT, "CHECKOUT" }, - { HTTP_METHOD_CHECKIN, "CHECKIN" }, - { HTTP_METHOD_MERGE, "MERGE" }, - { HTTP_METHOD_LOCK, "LOCK" }, - { HTTP_METHOD_UNLOCK, "UNLOCK" }, - { HTTP_METHOD_MKACTIVITY, "MKACTIVITY" }, - { HTTP_METHOD_UNCHECKOUT, "UNCHECKOUT" }, - { HTTP_METHOD_VERSION_CONTROL, "VERSION-CONTROL" }, - { HTTP_METHOD_CONNECT, "CONNECT" }, - - { HTTP_METHOD_UNSET, NULL } -}; - -static keyvalue http_status[] = { - { 100, "Continue" }, - { 101, "Switching Protocols" }, - { 102, "Processing" }, /* WebDAV */ - { 200, "OK" }, - { 201, "Created" }, - { 202, "Accepted" }, - { 203, "Non-Authoritative Information" }, - { 204, "No Content" }, - { 205, "Reset Content" }, - { 206, "Partial Content" }, - { 207, "Multi-status" }, /* WebDAV */ - { 300, "Multiple Choices" }, - { 301, "Moved Permanently" }, - { 302, "Found" }, - { 303, "See Other" }, - { 304, "Not Modified" }, - { 305, "Use Proxy" }, - { 306, "(Unused)" }, - { 307, "Temporary Redirect" }, - { 400, "Bad Request" }, - { 401, "Unauthorized" }, - { 402, "Payment Required" }, - { 403, "Forbidden" }, - { 404, "Not Found" }, - { 405, "Method Not Allowed" }, - { 406, "Not Acceptable" }, - { 407, "Proxy Authentication Required" }, - { 408, "Request Timeout" }, - { 409, "Conflict" }, - { 410, "Gone" }, - { 411, "Length Required" }, - { 412, "Precondition Failed" }, - { 413, "Request Entity Too Large" }, - { 414, "Request-URI Too Long" }, - { 415, "Unsupported Media Type" }, - { 416, "Requested Range Not Satisfiable" }, - { 417, "Expectation Failed" }, - { 422, "Unprocessable Entity" }, /* WebDAV */ - { 423, "Locked" }, /* WebDAV */ - { 424, "Failed Dependency" }, /* WebDAV */ - { 426, "Upgrade Required" }, /* TLS */ - { 500, "Internal Server Error" }, - { 501, "Not Implemented" }, - { 502, "Bad Gateway" }, - { 503, "Service Not Available" }, - { 504, "Gateway Timeout" }, - { 505, "HTTP Version Not Supported" }, - { 507, "Insufficient Storage" }, /* WebDAV */ - { 509, "Bandwidth Limit exceeded" }, - - { -1, NULL } -}; - -static keyvalue http_status_body[] = { - { 400, "400.html" }, - { 401, "401.html" }, - { 403, "403.html" }, - { 404, "404.html" }, - { 411, "411.html" }, - { 416, "416.html" }, - { 500, "500.html" }, - { 501, "501.html" }, - { 503, "503.html" }, - { 505, "505.html" }, - - { -1, NULL } -}; - - -const char *keyvalue_get_value(keyvalue *kv, int k) { - int i; - for (i = 0; kv[i].value; i++) { - if (kv[i].key == k) return kv[i].value; - } - return NULL; -} - -int keyvalue_get_key(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; -} - -keyvalue_buffer *keyvalue_buffer_init(void) { - keyvalue_buffer *kvb; - - kvb = calloc(1, sizeof(*kvb)); - - return kvb; -} - -int keyvalue_buffer_append(keyvalue_buffer *kvb, int key, const char *value) { - size_t i; - if (kvb->size == 0) { - kvb->size = 4; - - kvb->kv = malloc(kvb->size * sizeof(*kvb->kv)); - - for(i = 0; i < kvb->size; i++) { - kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); - } - } else if (kvb->used == kvb->size) { - kvb->size += 4; - - kvb->kv = realloc(kvb->kv, kvb->size * sizeof(*kvb->kv)); - - for(i = kvb->used; i < kvb->size; i++) { - kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); - } - } - - kvb->kv[kvb->used]->key = key; - kvb->kv[kvb->used]->value = strdup(value); - - kvb->used++; - - return 0; -} - -void keyvalue_buffer_free(keyvalue_buffer *kvb) { - size_t i; - - for (i = 0; i < kvb->size; i++) { - if (kvb->kv[i]->value) free(kvb->kv[i]->value); - free(kvb->kv[i]); - } - - if (kvb->kv) free(kvb->kv); - - free(kvb); -} - - -s_keyvalue_buffer *s_keyvalue_buffer_init(void) { - s_keyvalue_buffer *kvb; - - kvb = calloc(1, sizeof(*kvb)); - - return kvb; -} - -int s_keyvalue_buffer_append(s_keyvalue_buffer *kvb, const char *key, const char *value) { - size_t i; - if (kvb->size == 0) { - kvb->size = 4; - kvb->used = 0; - - kvb->kv = malloc(kvb->size * sizeof(*kvb->kv)); - - for(i = 0; i < kvb->size; i++) { - kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); - } - } else if (kvb->used == kvb->size) { - kvb->size += 4; - - kvb->kv = realloc(kvb->kv, kvb->size * sizeof(*kvb->kv)); - - for(i = kvb->used; i < kvb->size; i++) { - kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); - } - } - - kvb->kv[kvb->used]->key = key ? strdup(key) : NULL; - kvb->kv[kvb->used]->value = strdup(value); - - kvb->used++; - - return 0; -} - -void s_keyvalue_buffer_free(s_keyvalue_buffer *kvb) { - size_t i; - - for (i = 0; i < kvb->size; i++) { - if (kvb->kv[i]->key) free(kvb->kv[i]->key); - if (kvb->kv[i]->value) free(kvb->kv[i]->value); - free(kvb->kv[i]); - } - - if (kvb->kv) free(kvb->kv); - - free(kvb); -} - - -httpauth_keyvalue_buffer *httpauth_keyvalue_buffer_init(void) { - httpauth_keyvalue_buffer *kvb; - - kvb = calloc(1, sizeof(*kvb)); - - return kvb; -} - -int httpauth_keyvalue_buffer_append(httpauth_keyvalue_buffer *kvb, const char *key, const char *realm, httpauth_type type) { - size_t i; - if (kvb->size == 0) { - kvb->size = 4; - - kvb->kv = malloc(kvb->size * sizeof(*kvb->kv)); - - for(i = 0; i < kvb->size; i++) { - kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); - } - } else if (kvb->used == kvb->size) { - kvb->size += 4; - - kvb->kv = realloc(kvb->kv, kvb->size * sizeof(*kvb->kv)); - - for(i = kvb->used; i < kvb->size; i++) { - kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); - } - } - - kvb->kv[kvb->used]->key = strdup(key); - kvb->kv[kvb->used]->realm = strdup(realm); - kvb->kv[kvb->used]->type = type; - - kvb->used++; - - return 0; -} - -void httpauth_keyvalue_buffer_free(httpauth_keyvalue_buffer *kvb) { - size_t i; - - for (i = 0; i < kvb->size; i++) { - if (kvb->kv[i]->key) free(kvb->kv[i]->key); - if (kvb->kv[i]->realm) free(kvb->kv[i]->realm); - free(kvb->kv[i]); - } - - if (kvb->kv) free(kvb->kv); - - free(kvb); -} - - -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); -} - -const char *get_http_status_body_name(int i) { - return keyvalue_get_value(http_status_body, 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); -} - - - - -pcre_keyvalue_buffer *pcre_keyvalue_buffer_init(void) { - pcre_keyvalue_buffer *kvb; - - kvb = calloc(1, sizeof(*kvb)); - - return kvb; -} - -int pcre_keyvalue_buffer_append(pcre_keyvalue_buffer *kvb, const char *key, const char *value) { -#ifdef HAVE_PCRE_H - size_t i; - const char *errptr; - int erroff; - pcre_keyvalue *kv; -#endif - - if (!key) return -1; - -#ifdef HAVE_PCRE_H - if (kvb->size == 0) { - kvb->size = 4; - kvb->used = 0; - - kvb->kv = malloc(kvb->size * sizeof(*kvb->kv)); - - for(i = 0; i < kvb->size; i++) { - kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); - } - } else if (kvb->used == kvb->size) { - kvb->size += 4; - - kvb->kv = realloc(kvb->kv, kvb->size * sizeof(*kvb->kv)); - - for(i = kvb->used; i < kvb->size; i++) { - kvb->kv[i] = calloc(1, sizeof(**kvb->kv)); - } - } - - kv = kvb->kv[kvb->used]; - if (NULL == (kv->key = pcre_compile(key, - 0, &errptr, &erroff, NULL))) { - - fprintf(stderr, "%s.%d: rexexp compilation error at %s\n", __FILE__, __LINE__, errptr); - return -1; - } - - if (NULL == (kv->key_extra = pcre_study(kv->key, 0, &errptr)) && - errptr != NULL) { - return -1; - } - - kv->value = buffer_init_string(value); - - kvb->used++; - - return 0; -#else - UNUSED(kvb); - UNUSED(value); - - return -1; -#endif -} - -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); -} diff --git a/src/keyvalue.h b/src/keyvalue.h deleted file mode 100644 index e3b65c38..00000000 --- a/src/keyvalue.h +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef _KEY_VALUE_H_ -#define _KEY_VALUE_H_ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_PCRE_H -# include <pcre.h> -#endif - -typedef enum { - HTTP_METHOD_UNSET = -1, - HTTP_METHOD_GET, - HTTP_METHOD_POST, - HTTP_METHOD_HEAD, - HTTP_METHOD_OPTIONS, - HTTP_METHOD_PROPFIND, /* WebDAV */ - HTTP_METHOD_MKCOL, - HTTP_METHOD_PUT, - HTTP_METHOD_DELETE, - HTTP_METHOD_COPY, - HTTP_METHOD_MOVE, - HTTP_METHOD_PROPPATCH, - HTTP_METHOD_REPORT, /* DeltaV */ - HTTP_METHOD_CHECKOUT, - HTTP_METHOD_CHECKIN, - HTTP_METHOD_VERSION_CONTROL, - HTTP_METHOD_UNCHECKOUT, - HTTP_METHOD_MKACTIVITY, - HTTP_METHOD_MERGE, - HTTP_METHOD_LOCK, - HTTP_METHOD_UNLOCK, - HTTP_METHOD_LABEL, - HTTP_METHOD_CONNECT -} http_method_t; - -typedef enum { - HTTP_VERSION_UNSET = -1, - HTTP_VERSION_1_0, - HTTP_VERSION_1_1 -} http_version_t; - -typedef struct { - int key; - - char *value; -} keyvalue; - -typedef struct { - char *key; - - char *value; -} s_keyvalue; - -typedef struct { -#ifdef HAVE_PCRE_H - pcre *key; - pcre_extra *key_extra; -#endif - - buffer *value; -} pcre_keyvalue; - -typedef enum { HTTP_AUTH_BASIC, HTTP_AUTH_DIGEST } httpauth_type; - -typedef struct { - char *key; - - char *realm; - httpauth_type type; -} httpauth_keyvalue; - -#define KVB(x) \ -typedef struct {\ - x **kv; \ - size_t used;\ - size_t size;\ -} x ## _buffer - -KVB(keyvalue); -KVB(s_keyvalue); -KVB(httpauth_keyvalue); -KVB(pcre_keyvalue); - -LI_API const char * get_http_status_name(int i); -LI_API const char * get_http_version_name(int i); -LI_API const char * get_http_method_name(http_method_t i); -LI_API const char * get_http_status_body_name(int i); -LI_API int get_http_version_key(const char *s); -LI_API http_method_t get_http_method_key(const char *s); - -LI_API const char * keyvalue_get_value(keyvalue *kv, int k); -LI_API int keyvalue_get_key(keyvalue *kv, const char *s); - -LI_API keyvalue_buffer * keyvalue_buffer_init(void); -LI_API int keyvalue_buffer_append(keyvalue_buffer *kvb, int k, const char *value); -LI_API void keyvalue_buffer_free(keyvalue_buffer *kvb); - -LI_API s_keyvalue_buffer * s_keyvalue_buffer_init(void); -LI_API int s_keyvalue_buffer_append(s_keyvalue_buffer *kvb, const char *key, const char *value); -LI_API void s_keyvalue_buffer_free(s_keyvalue_buffer *kvb); - -LI_API httpauth_keyvalue_buffer * httpauth_keyvalue_buffer_init(void); -LI_API int httpauth_keyvalue_buffer_append(httpauth_keyvalue_buffer *kvb, const char *key, const char *realm, httpauth_type type); -LI_API void httpauth_keyvalue_buffer_free(httpauth_keyvalue_buffer *kvb); - -LI_API pcre_keyvalue_buffer * pcre_keyvalue_buffer_init(void); -LI_API int pcre_keyvalue_buffer_append(pcre_keyvalue_buffer *kvb, const char *key, const char *value); -LI_API void pcre_keyvalue_buffer_free(pcre_keyvalue_buffer *kvb); - -#endif diff --git a/src/lemon.c b/src/lemon.c deleted file mode 100644 index ac37f907..00000000 --- a/src/lemon.c +++ /dev/null @@ -1,4400 +0,0 @@ -/* -** 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 <unistd.h> - -extern void qsort(); -extern double strtod(); -extern long strtol(); -extern void free(); -extern int access(); -extern int atoi(); -extern char *getenv(); - -#ifndef __WIN32__ -# if defined(_WIN32) || defined(WIN32) -# define __WIN32__ -# endif -#endif - -#define PRIVATE static -/* #define PRIVATE */ - -#ifdef TEST -#define MAXRHS 5 /* Set low to exercise exception code */ -#else -#define MAXRHS 1000 -#endif - -char *msort(); -extern void *malloc(); - -extern void memory_error(); - -/******** From the file "action.h" *************************************/ -struct action *Action_new(); -struct action *Action_sort(); -void Action_add(); - -/********* From the file "assert.h" ************************************/ -void myassert(); -#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; - char *arg; - 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(/* */); -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 = 0; - struct action *new; - - if( freelist==0 ){ - 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,&ap->next,actioncmp); - return ap; -} - -void Action_add(app,type,sp,arg) -struct action **app; -enum e_action type; -struct symbol *sp; -char *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 plink *plp; - int progress; - int change; - - for(i=0; i<lemp->nstate; i++){ - for(cfp=lemp->sorted[i]->cfp; cfp; cfp=cfp->next){ - cfp->status = INCOMPLETE; - } - } - - do{ - progress = 0; - for(i=0; i<lemp->nstate; i++){ - for(cfp=lemp->sorted[i]->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. */ - Action_add(&lemp->sorted[0]->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) -struct action *apx; -struct action *apy; -{ - struct symbol *spx, *spy; - int errcnt = 0; - 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,&(current->next),Configcmp); - currentend = 0; - return; -} - -/* Sort the basis configuration list */ -void Configlist_sortbasis(){ - basis = (struct config *)msort(current,&(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); -} - - -/* 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_FLAG,0,0,0} - }; - int i; - struct lemon lem; - char *def_tmpl_name = "lempar.c"; - ( (void) argc ); // 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 */ - lem.nsymbol = Symbol_count(); - Symbol_new("{default}"); - lem.symbols = Symbol_arrayof(); - for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; - qsort(lem.symbols,lem.nsymbol+1,sizeof(struct symbol*), - (int(*)())Symbolcmpp); - for(i=0; i<=lem.nsymbol; i++) lem.symbols[i]->index = i; - for(i=1; isupper(lem.symbols[i]->name[0]); i++); - 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.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 -char *msort(list,next,cmp) -char *list; -char **next; -int (*cmp)(); -{ - 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, i; - spcnt = 0; - 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 thru to next case */ - 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(lemp->filename) + strlen(suffix) + 5 ); - 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,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 */ -PRIVATE 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 *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 = 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",lemp->tmplname); - lemp->errorcnt++; - return 0; - } - 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 if( lemp->vardest ){ - 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 = *plineno; /* 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 = hash*53 + 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; -{ - int h = 0; - while( *x) h = h*13 + *(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; - } - /* 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; - } - /* 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; -{ - int h=0; - while( a ){ - h = h*571 + a->rp->index*37 + 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; - } - /* 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 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; - } - /* 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/src/lempar.c b/src/lempar.c deleted file mode 100644 index c98720a6..00000000 --- a/src/lempar.c +++ /dev/null @@ -1,693 +0,0 @@ -/* 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 <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 = 0; -static char *yyTracePrompt = 0; -#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 = &pParser->yystack[pParser->yyidx]; - - if( pParser->yyidx<0 ) return 0; -#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==0 ) 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 && yyruleno>=0 - && (size_t) yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){ - fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt, - yyRuleName[yyruleno]); - } -#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; - ( (void) yymajor ); - ( (void) 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; /* Accept empty input */ - 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/src/log.c b/src/log.c deleted file mode 100644 index 401ab50d..00000000 --- a/src/log.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - * make sure _GNU_SOURCE is defined - */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include <sys/types.h> - -#include <errno.h> -#include <fcntl.h> -#include <time.h> -#include <string.h> -#include <stdlib.h> - -#include <stdarg.h> -#include <stdio.h> - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef _WIN32 -#undef HAVE_SYSLOG_H -#endif - -#ifdef HAVE_SYSLOG_H -#include <syslog.h> -#endif - -#include "log.h" -#include "array.h" - -#include "sys-files.h" - -#ifdef _WIN32 -#define STDERR_FILENO 2 -#endif - -#ifndef O_LARGEFILE -# define O_LARGEFILE 0 -#endif - -/* Close fd and _try_ to get a /dev/null for it instead. - * close() alone may trigger some bugs when a - * process opens another file and gets fd = STDOUT_FILENO or STDERR_FILENO - * and later tries to just print on stdout/stderr - * - * Returns 0 on success and -1 on failure (fd gets closed in all cases) - */ -int openDevNull(int fd) { - int tmpfd; - close(fd); -#if defined(__WIN32) - /* Cygwin should work with /dev/null */ - tmpfd = open("nul", O_RDWR); -#else - tmpfd = open("/dev/null", O_RDWR); -#endif - if (tmpfd != -1 && tmpfd != fd) { - dup2(tmpfd, fd); - close(tmpfd); - } - return (tmpfd != -1) ? 0 : -1; -} - -/** - * open the errorlog - * - * we have 3 possibilities: - * - stderr (default) - * - syslog - * - logfile - * - * if the open failed, report to the user and die - * - */ - - -typedef struct { - buffer *file; - unsigned short use_syslog; - - /* the errorlog */ - int fd; - enum { ERRORLOG_FILE, ERRORLOG_FD, ERRORLOG_SYSLOG } mode; - buffer *buf; - - time_t cached_ts; - buffer *cached_ts_str; -} errorlog; - -errorlog *myconfig = NULL; - -void log_init(void) { - /* use syslog */ - errorlog *err; - - err = calloc(1, sizeof(*err)); - - err->fd = STDERR_FILENO; - err->mode = ERRORLOG_FD; - err->buf = buffer_init(); - err->cached_ts_str = buffer_init(); - - myconfig = err; -} - -void log_free(void) { - errorlog *err = myconfig; - - if (!err) return; - - switch(err->mode) { - case ERRORLOG_FILE: - case ERRORLOG_FD: - if (-1 != err->fd) { - if (STDERR_FILENO != err->fd) - close(err->fd); - err->fd = -1; - } - break; - case ERRORLOG_SYSLOG: -#ifdef HAVE_SYSLOG_H - closelog(); -#endif - break; - } - - buffer_free(err->buf); - buffer_free(err->cached_ts_str); - - free(err); - - myconfig = NULL; -} - -int log_error_open(buffer *file, buffer *breakage_file, int use_syslog, int dont_daemonize) { - errorlog *err = myconfig; - -#ifdef HAVE_SYSLOG_H - /* perhaps someone wants to use syslog() */ - openlog("lighttpd", LOG_CONS | LOG_PID, LOG_DAEMON); -#endif - err->mode = ERRORLOG_FD; - err->fd = STDERR_FILENO; - - if (use_syslog) { - err->mode = ERRORLOG_SYSLOG; - } else if (!buffer_is_empty(file)) { - if (-1 == (err->fd = open(file->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) { - log_error_write(NULL, __FILE__, __LINE__, "SBSS", - "opening errorlog '", file, - "' failed: ", strerror(errno)); - - return -1; - } -#ifdef FD_CLOEXEC - /* close fd on exec (cgi) */ - fcntl(err->fd, F_SETFD, FD_CLOEXEC); -#endif - err->mode = ERRORLOG_FILE; - err->file = file; - } - - TRACE("%s", "server started"); - - if (err->mode == ERRORLOG_FD && !dont_daemonize) { - /* We can only log to stderr in dont-daemonize mode */ - err->fd = -1; - } - - if (!buffer_is_empty(breakage_file)) { - int breakage_fd; - - if (err->mode == ERRORLOG_FD) { - err->fd = dup(STDERR_FILENO); -#ifdef FD_CLOEXEC - fcntl(err->fd, F_SETFD, FD_CLOEXEC); -#endif - } - - if (-1 == (breakage_fd = open(breakage_file->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) { - log_error_write(NULL, __FILE__, __LINE__, "SBSS", - "opening breakagelog '", breakage_file, - "' failed: ", strerror(errno)); - - return -1; - } - - if (STDERR_FILENO != breakage_fd) { - dup2(breakage_fd, STDERR_FILENO); - close(breakage_fd); - } - } else if (!dont_daemonize) { - /* move stderr to /dev/null */ - openDevNull(STDERR_FILENO); - } - - - return 0; -} - -/** - * open the errorlog - * - * if the open failed, report to the user and die - * if no filename is given, use syslog instead - * - */ - -int log_error_cycle(void) { - /* only cycle if we are not in syslog-mode */ - - errorlog *err = myconfig; - - if (err->mode == ERRORLOG_FILE) { - buffer *file = err->file; - /* already check of opening time */ - - int new_fd; - - if (-1 == (new_fd = open(file->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) { - /* write to old log */ - log_error_write(NULL, __FILE__, __LINE__, "SBSSS", - "cycling errorlog '", file, - "' failed: ", strerror(errno), - ", falling back to syslog()"); - - close(err->fd); - err->fd = -1; -#ifdef HAVE_SYSLOG_H - err->mode = ERRORLOG_SYSLOG; -#endif - } else { - /* ok, new log is open, close the old one */ - close(err->fd); - err->fd = new_fd; - } - } - - return 0; -} - -int log_error_write(void *srv, const char *filename, unsigned int line, const char *fmt, ...) { - va_list ap; - time_t t; - - errorlog *err = myconfig; - - UNUSED(srv); - - switch(err->mode) { - case ERRORLOG_FILE: - case ERRORLOG_FD: - if (-1 == err->fd) return 0; - /* cache the generated timestamp */ - t = time(NULL); - - if (t != err->cached_ts) { - buffer_prepare_copy(err->cached_ts_str, 255); - strftime(err->cached_ts_str->ptr, err->cached_ts_str->size - 1, "%Y-%m-%d %H:%M:%S", localtime(&(t))); - err->cached_ts_str->used = strlen(err->cached_ts_str->ptr) + 1; - err->cached_ts = t; - } - - buffer_copy_string_buffer(err->buf, err->cached_ts_str); - buffer_append_string_len(err->buf, CONST_STR_LEN(" (")); - break; - case ERRORLOG_SYSLOG: - /* syslog is generating its own timestamps */ - buffer_copy_string_len(err->buf, CONST_STR_LEN("(")); - break; - } - - buffer_append_string(err->buf, REMOVE_PATH(filename)); - buffer_append_string_len(err->buf, CONST_STR_LEN(":")); - buffer_append_long(err->buf, line); - buffer_append_string_len(err->buf, CONST_STR_LEN(") ")); - - for(va_start(ap, fmt); *fmt; fmt++) { - int d; - char *s; - buffer *b; - off_t o; - - switch(*fmt) { - case 's': /* string */ - s = va_arg(ap, char *); - buffer_append_string(err->buf, s); - buffer_append_string_len(err->buf, CONST_STR_LEN(" ")); - break; - case 'b': /* buffer */ - b = va_arg(ap, buffer *); - buffer_append_string_buffer(err->buf, b); - buffer_append_string_len(err->buf, CONST_STR_LEN(" ")); - break; - case 'd': /* int */ - d = va_arg(ap, int); - buffer_append_long(err->buf, d); - buffer_append_string_len(err->buf, CONST_STR_LEN(" ")); - break; - case 'o': /* off_t */ - o = va_arg(ap, off_t); - buffer_append_off_t(err->buf, o); - buffer_append_string_len(err->buf, CONST_STR_LEN(" ")); - break; - case 'x': /* int (hex) */ - d = va_arg(ap, int); - buffer_append_string_len(err->buf, CONST_STR_LEN("0x")); - buffer_append_long_hex(err->buf, d); - buffer_append_string_len(err->buf, CONST_STR_LEN(" ")); - break; - case 'S': /* string */ - s = va_arg(ap, char *); - buffer_append_string(err->buf, s); - break; - case 'B': /* buffer */ - b = va_arg(ap, buffer *); - buffer_append_string_buffer(err->buf, b); - break; - case 'D': /* int */ - d = va_arg(ap, int); - buffer_append_long(err->buf, d); - break; - case '(': - case ')': - case '<': - case '>': - case ',': - case ' ': - buffer_append_string_len(err->buf, fmt, 1); - break; - } - } - va_end(ap); - - switch(err->mode) { - case ERRORLOG_FILE: - case ERRORLOG_FD: - buffer_append_string_len(err->buf, CONST_STR_LEN("\n")); - write(err->fd, err->buf->ptr, err->buf->used - 1); - break; -#ifdef HAVE_SYSLOG_H - case ERRORLOG_SYSLOG: - syslog(LOG_ERR, "%s", err->buf->ptr); - break; -#endif - } - - return 0; -} - -int log_trace(const char *fmt, ...) { - buffer *b; - int l, tries = 0; - errorlog *err = myconfig; - va_list ap; - time_t t; - int timestrsize = 0; - - b = buffer_init(); - buffer_prepare_copy(b, 4096); - - switch(err->mode) { - case ERRORLOG_FILE: - case ERRORLOG_FD: - if (-1 == err->fd) return 0; - /* cache the generated timestamp */ - t = time(NULL); - - if (t != err->cached_ts) { - buffer_prepare_copy(err->cached_ts_str, 255); - strftime(err->cached_ts_str->ptr, err->cached_ts_str->size - 1, "%Y-%m-%d %H:%M:%S", localtime(&(t))); - err->cached_ts_str->used = strlen(err->cached_ts_str->ptr) + 1; - err->cached_ts = t; - } - - buffer_copy_string_buffer(b, err->cached_ts_str); - buffer_append_string_len(b, CONST_STR_LEN(" ")); - timestrsize = b->used - 1; - break; - case ERRORLOG_SYSLOG: - /* syslog is generating its own timestamps */ - buffer_copy_string_len(b, CONST_STR_LEN("")); - timestrsize = b->used - 1; - break; - } - - do { - errno = 0; - va_start(ap, fmt); - l = vsnprintf(b->ptr+timestrsize, b->size-timestrsize, fmt, ap); - va_end(ap); - - /* if 'l' is between -1 and size we are good, - * otherwise we have to resize to size - */ - - if (l > -1 && ((unsigned int) l) < (b->size-timestrsize)) { - b->used += l; - - break; - } - - if (l > -1) { - /* C99: l is the mem-size we need */ - buffer_prepare_append(b, l + 1); /* allocate a bit more than we need */ - } else if (tries++ >= 3) { - int e = errno; - /* glibc 2.0.6 and earlier return -1 if the output was truncated - * so we try to increase the buffer size 3 times - so you cannot - * print error messages longer than 8 * 4096 = 32k with glib <= 2.0.6 - */ - buffer_copy_string_len(b, CONST_STR_LEN("log_trace: vsnprintf error: l = ")); - buffer_append_long(b, l); - if (e) { - buffer_append_string_len(b, CONST_STR_LEN(", errno = ")); - buffer_append_long(b, errno); - buffer_append_string_len(b, CONST_STR_LEN(": ")); - buffer_append_string(b, strerror(e)); - } - break; - } else { - buffer_prepare_append(b, 2*b->size); - } - } while(1); - - /* write b */ - switch(err->mode) { - case ERRORLOG_FILE: - case ERRORLOG_FD: - buffer_append_string_len(b, CONST_STR_LEN("\n")); - write(err->fd, b->ptr, b->used - 1); - break; -#ifdef HAVE_SYSLOG_H - case ERRORLOG_SYSLOG: - syslog(LOG_ERR, "%s", b->ptr); - break; -#endif - } - - buffer_free(b); - - return 0; -} - -#if REMOVE_PATH_FROM_FILE -const char *remove_path(const char *path) { - char *p = strrchr(path, DIR_SEPERATOR); - if (NULL != p && *(p) != '\0') { - return (p + 1); - } - return path; -} -#endif - diff --git a/src/log.h b/src/log.h deleted file mode 100644 index 616e6274..00000000 --- a/src/log.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef _LOG_H_ -#define _LOG_H_ - -#include "valgrind/valgrind.h" -#include "buffer.h" - -/* Close fd and _try_ to get a /dev/null for it instead. - * Returns 0 on success and -1 on failure (fd gets closed in all cases) - */ -LI_API int openDevNull(int fd); - -LI_API void log_init(void); -LI_API void log_free(void); - -LI_API int log_error_open(buffer* file, buffer* breakage_file, int use_syslog, int dont_daemonize); -LI_API int log_error_close(); -LI_API int log_error_write(void *srv, const char *filename, unsigned int line, const char *fmt, ...); -LI_API int log_error_cycle(); -#define REMOVE_PATH_FROM_FILE 1 -#if REMOVE_PATH_FROM_FILE -LI_API const char *remove_path(const char *path); -#define REMOVE_PATH(file) remove_path(file) -#else -#define REMOVE_PATH(file) file -#endif - -// TODO: perhaps make portable (detect if cc supports) -#define __ATTRIBUTE_PRINTF_FORMAT(fmt, arg) __attribute__ ((__format__ (__printf__, fmt, arg))) - -#define ERROR(fmt, ...) \ - log_trace("(error) (%s:%d) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__) - -#define TRACE(fmt, ...) \ - log_trace("(trace) (%s:%d) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__) - -#define SEGFAULT(fmt, ...) \ - do { \ - log_trace("(crashing) (%s:%d) "fmt, REMOVE_PATH(__FILE__), __LINE__, __VA_ARGS__); \ - VALGRIND_PRINTF_BACKTRACE(fmt, __VA_ARGS__);\ - abort();\ - } while(0) -LI_API int log_trace(const char *fmt, ...) __ATTRIBUTE_PRINTF_FORMAT(1, 2); -#endif diff --git a/src/md5.c b/src/md5.c deleted file mode 100644 index b365fc41..00000000 --- a/src/md5.c +++ /dev/null @@ -1,355 +0,0 @@ -/* 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. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef USE_OPENSSL -#include <string.h> - -#include "md5.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 (context) -li_MD5_CTX *context; /* 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 (context, input, inputLen) -li_MD5_CTX *context; /* context */ -const unsigned char *input; /* input block */ -unsigned int inputLen; /* length of input block */ -{ - unsigned int i, ndx, partLen; - - /* 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 (digest, context) -unsigned char digest[16]; /* message digest */ -li_MD5_CTX *context; /* 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 (state, block) -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 (output, input, len) -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 (output, input, len) -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 (output, input, len) -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 (output, value, len) -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/src/md5.h b/src/md5.h deleted file mode 100644 index fbcada0f..00000000 --- a/src/md5.h +++ /dev/null @@ -1,57 +0,0 @@ -/* 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. - */ -#include <limits.h> -#ifdef HAVE_STDINT_H -# include <stdint.h> -#endif -#ifdef HAVE_INTTYPES_H -# include <inttypes.h> -#endif - -#ifdef _WIN32 -#define UINT4 unsigned __int32 -#define UINT2 unsigned __int16 -#define POINTER unsigned char * -#else -#define UINT4 uint32_t -#define UINT2 uint16_t -#define POINTER unsigned char * -#endif - -#include "settings.h" - -/* MD5 context. */ -typedef struct { - UINT4 state[4]; /* state (ABCD) */ - UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ - unsigned char buffer[64]; /* input buffer */ -} li_MD5_CTX; - -LI_API void li_MD5_Init (li_MD5_CTX *); -LI_API void li_MD5_Update (li_MD5_CTX *, const unsigned char *, unsigned int); -LI_API void li_MD5_Final (unsigned char [16], li_MD5_CTX *); - - - diff --git a/src/mod_access.c b/src/mod_access.c deleted file mode 100644 index 03eee6da..00000000 --- a/src/mod_access.c +++ /dev/null @@ -1,230 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#include "sys-strings.h" - -#define PLUGIN_NAME "access" - -#define CONFIG_URL_ACCESS_DENY "url.access-deny" -#define CONFIG_ACCESS_DENY_ALL PLUGIN_NAME ".deny-all" - -typedef struct { - array *access_deny; - - unsigned short deny_all; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -INIT_FUNC(mod_access_init) { - plugin_data *p; - - UNUSED(srv); - - 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]; - - 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[] = { - { CONFIG_URL_ACCESS_DENY, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_ACCESS_DENY_ALL, NULL, T_CONFIG_BOOLEAN, 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->access_deny = array_init(); - s->deny_all = 0; - - cv[0].destination = s->access_deny; - cv[1].destination = &(s->deny_all); - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -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_OPTION(access_deny); - PATCH_OPTION(deny_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(CONFIG_URL_ACCESS_DENY))) { - PATCH_OPTION(access_deny); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ACCESS_DENY_ALL))) { - PATCH_OPTION(deny_all); - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_access_uri_handler) { - plugin_data *p = p_d; - int s_len; - size_t k; - - if (con->uri.path->used == 0) return HANDLER_GO_ON; - - mod_access_patch_connection(srv, con, p); - - if (con->conf.log_request_handling) { - TRACE("-- %s", "handling file in mod_access"); - } - - s_len = con->uri.path->used - 1; - - for (k = 0; k < p->conf.access_deny->used; k++) { - data_string *ds = (data_string *)p->conf.access_deny->data[k]; - int ct_len = ds->value->used - 1; - - if (ct_len > s_len) continue; - if (ds->value->used == 0) continue; - - /* if we have a case-insensitive FS we have to lower-case the URI here too */ - - if (con->conf.force_lowercase_filenames) { - if (0 == strncasecmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { - con->http_status = 403; - - if (con->conf.log_request_handling) { - TRACE("access denied, sending %d", con->http_status); - } - - return HANDLER_FINISHED; - } - } else { - if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { - con->http_status = 403; - - if (con->conf.log_request_handling) { - TRACE("access denied for %s as %s matched %d chars, sending %d", - SAFE_BUF_STR(con->uri.path), - SAFE_BUF_STR(ds->value), - ct_len, - con->http_status); - } - - return HANDLER_FINISHED; - } - } - } - - if (p->conf.deny_all) { - con->http_status = 403; - - if (con->conf.log_request_handling) { - TRACE("access.deny-all triggered, sending %d", con->http_status); - } - - return HANDLER_FINISHED; - } - - /* not found */ - return HANDLER_GO_ON; -} - -URIHANDLER_FUNC(mod_access_path_handler) { - plugin_data *p = p_d; - - mod_access_patch_connection(srv, con, p); - - if (con->conf.log_request_handling) { - TRACE("-- %s", "handling file in mod_access"); - } - - if (p->conf.deny_all) { - con->http_status = 403; - - if (con->conf.log_request_handling) { - TRACE("access denied, sending %d", con->http_status); - } - - return HANDLER_FINISHED; - } - - /* not found */ - return HANDLER_GO_ON; -} - -LI_EXPORT int mod_access_plugin_init(plugin *p); -LI_EXPORT int mod_access_plugin_init(plugin *p) { - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string(PLUGIN_NAME); - - p->init = mod_access_init; - p->set_defaults = mod_access_set_defaults; - p->handle_uri_clean = mod_access_uri_handler; - p->handle_start_backend = mod_access_path_handler; - p->cleanup = mod_access_free; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_accesslog.c b/src/mod_accesslog.c deleted file mode 100644 index 419fc463..00000000 --- a/src/mod_accesslog.c +++ /dev/null @@ -1,911 +0,0 @@ -/* - * make sure _GNU_SOURCE is defined - */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include <sys/types.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <fcntl.h> /* only the defines on windows */ -#include <errno.h> -#include <time.h> - -#include <stdio.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#include "inet_ntop_cache.h" - -#include "sys-socket.h" -#include "sys-files.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_MS, - 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_RESPONSE_HEADER - } type; -} format_mapping; - -/** - * - * - * "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" - * - */ - -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_MS }, - { 'e', FORMAT_ENV }, - { 'f', FORMAT_FILENAME }, - { 'H', FORMAT_REQUEST_PROTOCOL }, - { 'm', FORMAT_REQUEST_METHOD }, - { 'n', FORMAT_UNSUPPORTED }, /* we have no notes */ - { '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 } -}; - - -typedef struct { - enum { FIELD_UNSET, FIELD_STRING, FIELD_FORMAT } type; - - buffer *string; - int field; -} format_field; - -typedef struct { - format_field **ptr; - - size_t used; - size_t size; -} format_fields; - -typedef struct { - buffer *access_logfile; - buffer *format; - unsigned short use_syslog; - - - int log_access_fd; - time_t last_generated_accesslog_ts; - time_t *last_generated_accesslog_ts_ptr; - - - buffer *access_logbuffer; - buffer *ts_accesslog_str; - - format_fields *parsed_format; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - plugin_config **config_storage; - plugin_config conf; -} plugin_data; - -INIT_FUNC(mod_accesslog_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - return p; -} - -static void accesslog_append_escaped(buffer *dest, buffer *str) { - /* replaces non-printable chars with \xHH where HH is the hex representation of the byte */ - /* exceptions: " => \", \ => \\, whitespace chars => \n \t etc. */ - if (str->used == 0) return; - buffer_prepare_append(dest, str->used - 1); - - for (unsigned int i = 0; i < str->used - 1; i++) { - if (str->ptr[i] >= ' ' && str->ptr[i] <= '~') { - /* printable chars */ - buffer_append_string_len(dest, &str->ptr[i], 1); - } else switch (str->ptr[i]) { - 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 = str->ptr[i] / 16; - hh[2] = (h > 9) ? (h - 10 + 'A') : (h + '0'); - h = str->ptr[i] % 16; - hh[3] = (h > 9) ? (h - 10 + 'A') : (h + '0'); - buffer_append_string_len(dest, &hh[0], 4); - } - break; - } - } -} - -static int accesslog_parse_format(server *srv, format_fields *fields, buffer *format) { - size_t i, j, k = 0, start = 0; - - for (i = 0; i < format->used - 1; i++) { - - switch(format->ptr[i]) { - case '%': - 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++; - } - - - /* 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 '<': - /* only for s */ - - 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->used++; - - break; - } - - if (fmap[j].key == '\0') { - log_error_write(srv, __FILE__, __LINE__, "ss", "config: ", "failed"); - return -1; - } - - start = i + 3; - - break; - case '{': - /* go forward to } */ - - for (k = i+2; k < format->used - 1; k++) { - if (format->ptr[k] == '}') break; - } - - if (k == format->used - 1) { - log_error_write(srv, __FILE__, __LINE__, "ss", "config: ", "failed"); - return -1; - } - if (format->ptr[k+1] == '\0') { - log_error_write(srv, __FILE__, __LINE__, "ss", "config: ", "failed"); - 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(); - - 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__, "ss", "config: ", "failed"); - return -1; - } - - start = k + 2; - - break; - default: - 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->used++; - - break; - } - - if (fmap[j].key == '\0') { - log_error_write(srv, __FILE__, __LINE__, "ss", "config: ", "failed"); - return -1; - } - - start = i + 2; - - 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 (!s) continue; - - if (s->access_logbuffer->used) { - if (s->use_syslog) { -# ifdef HAVE_SYSLOG_H - if (s->access_logbuffer->used > 2) { - syslog(LOG_INFO, "%*s", (int) s->access_logbuffer->used - 2, s->access_logbuffer->ptr); - } -# endif - } else if (s->log_access_fd != -1) { - write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1); - } - } - - if (s->log_access_fd != -1) close(s->log_access_fd); - - 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); - } - - 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 }, - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - if (!p) return HANDLER_ERROR; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; 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); - - - cv[0].destination = s->access_logfile; - cv[1].destination = &(s->use_syslog); - cv[2].destination = s->format; - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - - if (i == 0 && buffer_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 (s->format->used) { - 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; - } -#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_is_empty(s->access_logfile)) continue; - - if (s->access_logfile->ptr[0] == '|') { -#ifdef HAVE_FORK - /* create write pipe and spawn process */ - - int to_log_fds[2]; - pid_t pid; - - if (pipe(to_log_fds)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno)); - return HANDLER_ERROR; - } - - /* fork, execve */ - switch (pid = fork()) { - case 0: - /* child */ - - close(STDIN_FILENO); - dup2(to_log_fds[0], STDIN_FILENO); - close(to_log_fds[0]); - /* not needed */ - close(to_log_fds[1]); - - /* we don't need the client socket */ - for (i = 3; i < 256; i++) { - close(i); - } - - /* exec the log-process (skip the | ) - * - */ - - execl("/bin/sh", "sh", "-c", s->access_logfile->ptr + 1, (char *)NULL); - - log_error_write(srv, __FILE__, __LINE__, "sss", - "spawning log-process failed: ", strerror(errno), - s->access_logfile->ptr + 1); - - exit(-1); - break; - case -1: - /* error */ - log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed: ", strerror(errno)); - break; - default: - close(to_log_fds[0]); - - s->log_access_fd = to_log_fds[1]; - - break; - } -#else - return -1; -#endif - } else if (-1 == (s->log_access_fd = - open(s->access_logfile->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) { - - log_error_write(srv, __FILE__, __LINE__, "ssb", - "opening access-log failed:", - strerror(errno), s->access_logfile); - - return HANDLER_ERROR; - } -#ifndef _WIN32 - fcntl(s->log_access_fd, F_SETFD, FD_CLOEXEC); -#endif - } - - 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; - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s = p->config_storage[i]; - - if (s->access_logbuffer->used) { - if (s->use_syslog) { -#ifdef HAVE_SYSLOG_H - if (s->access_logbuffer->used > 2) { - /* syslog appends a \n on its own */ - syslog(LOG_INFO, "%*s", (int) s->access_logbuffer->used - 2, s->access_logbuffer->ptr); - } -#endif - } else if (s->log_access_fd != -1) { - write(s->log_access_fd, s->access_logbuffer->ptr, s->access_logbuffer->used - 1); - } - - buffer_reset(s->access_logbuffer); - } - - if (s->use_syslog == 0 && - !buffer_is_empty(s->access_logfile) && - s->access_logfile->ptr[0] != '|') { - - close(s->log_access_fd); - - if (-1 == (s->log_access_fd = - open(s->access_logfile->ptr, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) { - - log_error_write(srv, __FILE__, __LINE__, "ss", "cycling access-log failed:", strerror(errno)); - - return HANDLER_ERROR; - } - } - } - - return HANDLER_GO_ON; -} - -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_OPTION(access_logfile); - PATCH_OPTION(format); - PATCH_OPTION(log_access_fd); - PATCH_OPTION(last_generated_accesslog_ts_ptr); - PATCH_OPTION(access_logbuffer); - PATCH_OPTION(ts_accesslog_str); - PATCH_OPTION(parsed_format); - PATCH_OPTION(use_syslog); - - /* 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_OPTION(access_logfile); - PATCH_OPTION(log_access_fd); - PATCH_OPTION(last_generated_accesslog_ts_ptr); - PATCH_OPTION(access_logbuffer); - PATCH_OPTION(ts_accesslog_str); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.format"))) { - PATCH_OPTION(format); - PATCH_OPTION(parsed_format); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("accesslog.use-syslog"))) { - PATCH_OPTION(use_syslog); - } - } - } - - return 0; -} - -REQUESTDONE_FUNC(log_access_write) { - plugin_data *p = p_d; - buffer *b; - size_t j; - - int newts = 0; - data_string *ds; - - mod_accesslog_patch_connection(srv, con, p); - - b = p->conf.access_logbuffer; - if (b->used == 0) { - buffer_copy_string_len(b, CONST_STR_LEN("")); - } - - for (j = 0; j < p->conf.parsed_format->used; j++) { - switch(p->conf.parsed_format->ptr[j]->type) { - case FIELD_STRING: - buffer_append_string_buffer(b, p->conf.parsed_format->ptr[j]->string); - break; - case FIELD_FORMAT: - switch(p->conf.parsed_format->ptr[j]->field) { - case FORMAT_TIMESTAMP: - - /* cache the generated timestamp */ - if (srv->cur_ts != *(p->conf.last_generated_accesslog_ts_ptr)) { - struct tm tm; -#if defined(HAVE_STRUCT_TM_GMTOFF) - long scd, hrs, min; -#endif - - buffer_prepare_copy(p->conf.ts_accesslog_str, 255); -#if defined(HAVE_STRUCT_TM_GMTOFF) -# ifdef HAVE_LOCALTIME_R - localtime_r(&(srv->cur_ts), &tm); - strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, "[%d/%b/%Y:%H:%M:%S ", &tm); -# else - strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, "[%d/%b/%Y:%H:%M:%S ", localtime(&(srv->cur_ts))); -# endif - p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1; - - buffer_append_string(p->conf.ts_accesslog_str, tm.tm_gmtoff >= 0 ? "+" : "-"); - - scd = abs(tm.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_long(p->conf.ts_accesslog_str, hrs); - - if (min < 10) buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("0")); - buffer_append_long(p->conf.ts_accesslog_str, min); - buffer_append_string_len(p->conf.ts_accesslog_str, CONST_STR_LEN("]")); -#else -#ifdef HAVE_GMTIME_R - gmtime_r(&(srv->cur_ts), &tm); - strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, "[%d/%b/%Y:%H:%M:%S +0000]", &tm); -#else - strftime(p->conf.ts_accesslog_str->ptr, p->conf.ts_accesslog_str->size - 1, "[%d/%b/%Y:%H:%M:%S +0000]", gmtime(&(srv->cur_ts))); -#endif - p->conf.ts_accesslog_str->used = strlen(p->conf.ts_accesslog_str->ptr) + 1; -#endif - - *(p->conf.last_generated_accesslog_ts_ptr) = srv->cur_ts; - newts = 1; - } - - buffer_append_string_buffer(b, p->conf.ts_accesslog_str); - - break; - case FORMAT_REMOTE_HOST: - - /* handle inet_ntop cache */ - - buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(con->dst_addr))); - - break; - case FORMAT_REMOTE_IDENT: - /* ident */ - buffer_append_string_len(b, CONST_STR_LEN("-")); - break; - case FORMAT_REMOTE_USER: - if (con->authed_user->used > 1) { - buffer_append_string_buffer(b, con->authed_user); - } else { - buffer_append_string_len(b, CONST_STR_LEN("-")); - } - break; - case FORMAT_REQUEST_LINE: - buffer_append_string(b, get_http_method_name(con->request.http_method)); - buffer_append_string_len(b, CONST_STR_LEN(" ")); - accesslog_append_escaped(b, con->request.orig_uri); - buffer_append_string_len(b, CONST_STR_LEN(" ")); - buffer_append_string(b, get_http_version_name(con->request.http_version)); - - break; - case FORMAT_STATUS: - buffer_append_long(b, con->http_status); - break; - - case FORMAT_BYTES_OUT_NO_HEADER: - if (con->bytes_written > 0) { - buffer_append_off_t(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 != (ds = (data_string *)array_get_element(con->request.headers, CONST_BUF_LEN(p->conf.parsed_format->ptr[j]->string)))) { - accesslog_append_escaped(b, ds->value); - } else { - buffer_append_string_len(b, CONST_STR_LEN("-")); - } - break; - case FORMAT_RESPONSE_HEADER: - if (NULL != (ds = (data_string *)array_get_element(con->response.headers, CONST_BUF_LEN(p->conf.parsed_format->ptr[j]->string)))) { - accesslog_append_escaped(b, ds->value); - } else { - buffer_append_string_len(b, CONST_STR_LEN("-")); - } - break; - case FORMAT_ENV: - if (NULL != (ds = (data_string *)array_get_element(con->environment, CONST_BUF_LEN(p->conf.parsed_format->ptr[j]->string)))) { - accesslog_append_escaped(b, ds->value); - } else { - buffer_append_string_len(b, CONST_STR_LEN("-")); - } - break; - case FORMAT_FILENAME: - if (con->physical.path->used > 1) { - 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_off_t(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_off_t(b, con->bytes_read); - } else { - buffer_append_string_len(b, CONST_STR_LEN("-")); - } - break; - case FORMAT_TIME_USED: - buffer_append_long(b, srv->cur_ts - con->request_start); - break; - case FORMAT_SERVER_NAME: - if (con->server_name->used > 1) { - buffer_append_string_buffer(b, con->server_name); - } else { - buffer_append_string_len(b, CONST_STR_LEN("-")); - } - break; - case FORMAT_HTTP_HOST: - if (con->uri.authority->used > 1) { - accesslog_append_escaped(b, con->uri.authority); - } else { - buffer_append_string_len(b, CONST_STR_LEN("-")); - } - break; - case FORMAT_REQUEST_PROTOCOL: - buffer_append_string(b, - con->request.http_version == HTTP_VERSION_1_1 ? "HTTP/1.1" : "HTTP/1.0"); - break; - case FORMAT_REQUEST_METHOD: - buffer_append_string(b, get_http_method_name(con->request.http_method)); - break; - case FORMAT_SERVER_PORT: - buffer_append_long(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: - switch(con->keep_alive) { - case 0: buffer_append_string_len(b, CONST_STR_LEN("-")); break; - default: buffer_append_string_len(b, CONST_STR_LEN("+")); break; - } - break; - default: - /* - { 'a', FORMAT_REMOTE_ADDR }, - { 'A', FORMAT_LOCAL_ADDR }, - { 'C', FORMAT_COOKIE }, - { 'D', FORMAT_TIME_USED_MS }, - */ - - break; - } - break; - default: - break; - } - } - - buffer_append_string_len(b, CONST_STR_LEN("\n")); - - if (p->conf.use_syslog || /* syslog doesn't cache */ - (p->conf.access_logfile->used && p->conf.access_logfile->ptr[0] != '|') || /* pipes don't cache */ - newts || - b->used > BUFFER_MAX_REUSE_SIZE) { - if (p->conf.use_syslog) { -#ifdef HAVE_SYSLOG_H - if (b->used > 2) { - /* syslog appends a \n on its own */ - syslog(LOG_INFO, "%*s", (int) b->used - 2, b->ptr); - } -#endif - } else if (p->conf.log_access_fd != -1) { - write(p->conf.log_access_fd, b->ptr, b->used - 1); - } - buffer_reset(b); - } - - return HANDLER_GO_ON; -} - - -LI_EXPORT int mod_accesslog_plugin_init(plugin *p); -LI_EXPORT 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_response_done = log_access_write; - p->handle_sighup = log_access_cycle; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_alias.c b/src/mod_alias.c deleted file mode 100644 index c03fb7cb..00000000 --- a/src/mod_alias.c +++ /dev/null @@ -1,204 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" -#include "sys-strings.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; - - UNUSED(srv); - - 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 (!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(specific_config *)); - - for (i = 0; i < srv->config_context->used; 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, ((data_config *)srv->config_context->data[i])->value, cv)) { - 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 (key->used < prefix->used) { - break; - } - if (memcmp(key->ptr, prefix->ptr, prefix->used - 1) != 0) { - break; - } - /* ok, they have same prefix. check position */ - if (a->sorted[j] < a->sorted[k]) { - fprintf(stderr, "url.alias: `%s' will never match as `%s' matched first\n", - key->ptr, - prefix->ptr); - return HANDLER_ERROR; - } - } - } - } - } - - return HANDLER_GO_ON; -} - -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_OPTION(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_OPTION(alias); - } - } - } - - return 0; -} - -PHYSICALPATH_FUNC(mod_alias_physical_handler) { - plugin_data *p = p_d; - int uri_len, basedir_len; - char *uri_ptr; - size_t k; - - if (con->physical.path->used == 0) return HANDLER_GO_ON; - - mod_alias_patch_connection(srv, con, p); - - /* not to include the tailing slash */ - basedir_len = (con->physical.basedir->used - 1) - 1; - uri_len = con->physical.path->used - 1 - basedir_len; - uri_ptr = con->physical.path->ptr + basedir_len; - - for (k = 0; k < p->conf.alias->used; k++) { - data_string *ds = (data_string *)p->conf.alias->data[k]; - int alias_len = ds->key->used - 1; - - if (alias_len > uri_len) continue; - if (ds->key->used == 0) continue; - - if (0 == (con->conf.force_lowercase_filenames ? - strncasecmp(uri_ptr, ds->key->ptr, alias_len) : - strncmp(uri_ptr, ds->key->ptr, alias_len))) { - /* matched */ - - buffer_copy_string_buffer(con->physical.basedir, ds->value); - buffer_copy_string_buffer(srv->tmp_buf, ds->value); - buffer_append_string(srv->tmp_buf, uri_ptr + alias_len); - buffer_copy_string_buffer(con->physical.path, srv->tmp_buf); - - return HANDLER_GO_ON; - } - } - - /* not found */ - return HANDLER_GO_ON; -} - -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_alias_plugin_init(plugin *p); -LI_EXPORT 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/src/mod_auth.c b/src/mod_auth.c deleted file mode 100644 index 5b8307cc..00000000 --- a/src/mod_auth.c +++ /dev/null @@ -1,672 +0,0 @@ -#include <sys/types.h> -#include <sys/stat.h> - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <fcntl.h> - -#include "settings.h" -#include "plugin.h" -#include "http_auth.h" -#include "log.h" -#include "response.h" - -#include "sys-strings.h" -#include "sys-files.h" - -handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s); -#ifdef USE_LDAP -void auth_ldap_cleanup(ldap_plugin_config* p); -#endif - -/** - * the basic and digest auth framework - * - * - config handling - * - protocol handling - * - * http_auth.c - * http_auth_digest.c - * - * do the real work - */ - -INIT_FUNC(mod_auth_init) { - mod_auth_plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->tmp_buf = buffer_init(); - - p->auth_user = buffer_init(); -#ifdef USE_LDAP - p->ldap_filter = buffer_init(); -#endif - - return p; -} - -FREE_FUNC(mod_auth_free) { - mod_auth_plugin_data *p = p_d; - - UNUSED(srv); - - if (!p) return HANDLER_GO_ON; - - buffer_free(p->tmp_buf); - buffer_free(p->auth_user); -#ifdef USE_LDAP - buffer_free(p->ldap_filter); -#endif - - if (p->config_storage) { - size_t i; - for (i = 0; i < srv->config_context->used; i++) { - mod_auth_plugin_config *s = p->config_storage[i]; - - if (!s) continue; - - array_free(s->auth_require); - buffer_free(s->auth_plain_groupfile); - buffer_free(s->auth_plain_userfile); - buffer_free(s->auth_htdigest_userfile); - buffer_free(s->auth_htpasswd_userfile); - buffer_free(s->auth_backend_conf); - - buffer_free(s->auth_ldap_url); - 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_cert); - buffer_free(s->auth_ldap_key); - -#ifdef USE_LDAP - buffer_free(s->ldap->ldap_filter_pre); - buffer_free(s->ldap->ldap_filter_post); - - auth_ldap_cleanup(s->ldap); - free(s->ldap); -#endif - - free(s); - } - free(p->config_storage); - } - - free(p); - - return HANDLER_GO_ON; -} - -static int mod_auth_patch_connection(server *srv, connection *con, mod_auth_plugin_data *p) { - size_t i, j; - mod_auth_plugin_config *s = p->config_storage[0]; - - PATCH_OPTION(auth_backend); - PATCH_OPTION(auth_plain_groupfile); - PATCH_OPTION(auth_plain_userfile); - PATCH_OPTION(auth_htdigest_userfile); - PATCH_OPTION(auth_htpasswd_userfile); - PATCH_OPTION(auth_require); - PATCH_OPTION(auth_debug); - PATCH_OPTION(auth_ldap_url); - PATCH_OPTION(auth_ldap_basedn); - PATCH_OPTION(auth_ldap_binddn); - PATCH_OPTION(auth_ldap_bindpw); - PATCH_OPTION(auth_ldap_filter); - PATCH_OPTION(auth_ldap_cafile); - PATCH_OPTION(auth_ldap_cert); - PATCH_OPTION(auth_ldap_key); - PATCH_OPTION(auth_ldap_starttls); - PATCH_OPTION(auth_ldap_allow_empty_pw); -#ifdef USE_LDAP - PATCH_OPTION(ldap); -#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("auth.backend"))) { - PATCH_OPTION(auth_backend); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.groupfile"))) { - PATCH_OPTION(auth_plain_groupfile); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.userfile"))) { - PATCH_OPTION(auth_plain_userfile); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htdigest.userfile"))) { - PATCH_OPTION(auth_htdigest_userfile); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htpasswd.userfile"))) { - PATCH_OPTION(auth_htpasswd_userfile); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.require"))) { - PATCH_OPTION(auth_require); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.debug"))) { - PATCH_OPTION(auth_debug); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.url"))) { - PATCH_OPTION(auth_ldap_url); -#ifdef USE_LDAP - PATCH_OPTION(ldap); -#endif - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.base-dn"))) { - PATCH_OPTION(auth_ldap_basedn); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-dn"))) { - PATCH_OPTION(auth_ldap_binddn); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.bind-pw"))) { - PATCH_OPTION(auth_ldap_bindpw); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.filter"))) { - PATCH_OPTION(auth_ldap_filter); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.ca-file"))) { - PATCH_OPTION(auth_ldap_cafile); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.cert"))) { - PATCH_OPTION(auth_ldap_cert); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.key"))) { - PATCH_OPTION(auth_ldap_key); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.starttls"))) { - PATCH_OPTION(auth_ldap_starttls); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.ldap.allow-empty-pw"))) { - PATCH_OPTION(auth_ldap_allow_empty_pw); - } - } - } - - return 0; -} - -static handler_t mod_auth_uri_handler(server *srv, connection *con, void *p_d) { - size_t k; - int auth_required = 0, auth_satisfied = 0; - char *http_authorization = NULL; - data_string *ds; - mod_auth_plugin_data *p = p_d; - array *req; - - /* select the right config */ - mod_auth_patch_connection(srv, con, p); - - if (p->conf.auth_require == NULL) return HANDLER_GO_ON; - - /* - * AUTH - * - */ - - /* do we have to ask for auth ? */ - - auth_required = 0; - auth_satisfied = 0; - - /* search auth-directives for path */ - for (k = 0; k < p->conf.auth_require->used; k++) { - buffer *auth_path = p->conf.auth_require->data[k]->key; - - if (auth_path->used == 0) continue; - if (con->uri.path->used < auth_path->used) continue; - - /* if we have a case-insensitive FS we have to lower-case the URI here too */ - - if (con->conf.force_lowercase_filenames) { - if (0 == strncasecmp(con->uri.path->ptr, auth_path->ptr, auth_path->used - 1)) { - auth_required = 1; - break; - } - } else { - if (0 == strncmp(con->uri.path->ptr, auth_path->ptr, auth_path->used - 1)) { - auth_required = 1; - break; - } - } - } - - /* nothing to do for us */ - if (auth_required == 0) return HANDLER_GO_ON; - - req = ((data_array *)(p->conf.auth_require->data[k]))->value; - - /* try to get Authorization-header */ - - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Authorization")))) { - http_authorization = ds->value->ptr; - } - - if (ds && ds->value && ds->value->used) { - char *auth_realm; - data_string *method; - - method = (data_string *)array_get_element(req, CONST_STR_LEN("method")); - - /* parse auth-header */ - if (NULL != (auth_realm = strchr(http_authorization, ' '))) { - int auth_type_len = auth_realm - http_authorization; - - if ((auth_type_len == 5) && - (0 == strncmp(http_authorization, "Basic", auth_type_len))) { - - if (buffer_is_equal_string(method->value, CONST_STR_LEN("basic"))) { - auth_satisfied = http_auth_basic_check(srv, con, p, req, con->uri.path, auth_realm+1); - } - } else if ((auth_type_len == 6) && - (0 == strncmp(http_authorization, "Digest", auth_type_len))) { - if (buffer_is_equal_string(method->value, CONST_STR_LEN("digest"))) { - if (-1 == (auth_satisfied = http_auth_digest_check(srv, con, p, req, con->uri.path, auth_realm+1))) { - con->http_status = 400; - - /* a field was missing */ - - return HANDLER_FINISHED; - } - } - } else { - log_error_write(srv, __FILE__, __LINE__, "ss", - "unknown authentification type:", - http_authorization); - } - } - } - - if (!auth_satisfied) { - data_string *method, *realm; - method = (data_string *)array_get_element(req, CONST_STR_LEN("method")); - realm = (data_string *)array_get_element(req, CONST_STR_LEN("realm")); - - con->http_status = 401; - - if (buffer_is_equal_string(method->value, CONST_STR_LEN("basic"))) { - buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Basic realm=\"")); - buffer_append_string_buffer(p->tmp_buf, realm->value); - buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\"")); - - response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf)); - } else if (buffer_is_equal_string(method->value, CONST_STR_LEN("digest"))) { - char hh[33]; - http_auth_digest_generate_nonce(srv, p, srv->tmp_buf, hh); - - buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("Digest realm=\"")); - buffer_append_string_buffer(p->tmp_buf, realm->value); - buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", nonce=\"")); - buffer_append_string(p->tmp_buf, hh); - buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("\", qop=\"auth\"")); - - response_header_insert(srv, con, CONST_STR_LEN("WWW-Authenticate"), CONST_BUF_LEN(p->tmp_buf)); - } else { - /* evil */ - } - return HANDLER_FINISHED; - } else { - /* the REMOTE_USER header */ - - buffer_copy_string_buffer(con->authed_user, p->auth_user); - } - - return HANDLER_GO_ON; -} - -SETDEFAULTS_FUNC(mod_auth_set_defaults) { - mod_auth_plugin_data *p = p_d; - size_t i; - - config_values_t cv[] = { - { "auth.backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { "auth.backend.plain.groupfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { "auth.backend.plain.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - { "auth.require", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ - { "auth.backend.ldap.url" , NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ - { "auth.backend.ldap.base-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ - { "auth.backend.ldap.filter", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ - { "auth.backend.ldap.ca-file", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ - { "auth.backend.ldap.cert", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ - { "auth.backend.ldap.key", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ - { "auth.backend.ldap.starttls", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ - { "auth.backend.ldap.bind-dn", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ - { "auth.backend.ldap.bind-pw", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ - { "auth.backend.ldap.allow-empty-pw", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ - { "auth.backend.htdigest.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ - { "auth.backend.htpasswd.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 15 */ - { "auth.debug", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 16 */ - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - mod_auth_plugin_config *s; - size_t n; - data_array *da; - array *ca; - - s = calloc(1, sizeof(mod_auth_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(); - s->auth_backend_conf = buffer_init(); - - s->auth_ldap_url = 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_cert = buffer_init(); - s->auth_ldap_key = buffer_init(); - s->auth_ldap_starttls = 0; - s->auth_debug = 0; - - s->auth_require = array_init(); - -#ifdef USE_LDAP - s->ldap = malloc (sizeof(ldap_plugin_config)); - s->ldap->ldap_filter_pre = buffer_init(); - s->ldap->ldap_filter_post = buffer_init(); - s->ldap->ldap = NULL; -#endif - - cv[0].destination = s->auth_backend_conf; - cv[1].destination = s->auth_plain_groupfile; - cv[2].destination = s->auth_plain_userfile; - cv[3].destination = s->auth_require; - cv[4].destination = s->auth_ldap_url; - cv[5].destination = s->auth_ldap_basedn; - cv[6].destination = s->auth_ldap_filter; - cv[7].destination = s->auth_ldap_cafile; - cv[8].destination = s->auth_ldap_cert; - cv[9].destination = s->auth_ldap_key; - cv[10].destination = &(s->auth_ldap_starttls); - cv[11].destination = s->auth_ldap_binddn; - cv[12].destination = s->auth_ldap_bindpw; - cv[13].destination = &(s->auth_ldap_allow_empty_pw); - cv[14].destination = s->auth_htdigest_userfile; - cv[15].destination = s->auth_htpasswd_userfile; - cv[16].destination = &(s->auth_debug); - - p->config_storage[i] = s; - ca = ((data_config *)srv->config_context->data[i])->value; - - if (0 != config_insert_values_global(srv, ca, cv)) { - return HANDLER_ERROR; - } - - if (!buffer_is_empty(s->auth_backend_conf)) { - if (buffer_is_equal_string(s->auth_backend_conf, CONST_STR_LEN("htpasswd"))) { - s->auth_backend = AUTH_BACKEND_HTPASSWD; - } else if (buffer_is_equal_string(s->auth_backend_conf, CONST_STR_LEN("htdigest"))) { - s->auth_backend = AUTH_BACKEND_HTDIGEST; - } else if (buffer_is_equal_string(s->auth_backend_conf, CONST_STR_LEN("plain"))) { - s->auth_backend = AUTH_BACKEND_PLAIN; - } else if (buffer_is_equal_string(s->auth_backend_conf, CONST_STR_LEN("ldap"))) { - s->auth_backend = AUTH_BACKEND_LDAP; - } else { - 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(ca, CONST_STR_LEN("auth.require")))) continue; - - if (da->type != TYPE_ARRAY) continue; - - for (n = 0; n < da->value->used; n++) { - size_t m; - data_array *da_file = (data_array *)da->value->data[n]; - buffer *method, *realm, *require; - - if (da->value->data[n]->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "auth.require should contain an array as in:", - "auth.require = ( \"...\" => ( ..., ...) )"); - - return HANDLER_ERROR; - } - - method = realm = require = NULL; - - for (m = 0; m < da_file->value->used; m++) { - data_string *ds_auth_req = (data_string *)da_file->value->data[m]; - - if (ds_auth_req->type != TYPE_STRING) { - log_error_write(srv, __FILE__, __LINE__, "ssbs", - "a string was expected for:", - "auth.require = ( \"...\" => ( ..., -> \"", - ds_auth_req->key, - "\" <- => \"...\" ) )"); - - return HANDLER_ERROR; - } - - if (buffer_is_equal_string(ds_auth_req->key, CONST_STR_LEN("method"))) { - method = ds_auth_req->value; - } else if (buffer_is_equal_string(ds_auth_req->key, CONST_STR_LEN("realm"))) { - realm = ds_auth_req->value; - } else if (buffer_is_equal_string(ds_auth_req->key, CONST_STR_LEN("require"))) { - require = ds_auth_req->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; - - } - } - - if (method == NULL) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "the require field is missing in:", - "auth.require = ( \"...\" => ( ..., \"method\" => \"...\" ) )"); - return HANDLER_ERROR; - } - if (!buffer_is_equal_string(method, CONST_STR_LEN("basic")) && - !buffer_is_equal_string(method, CONST_STR_LEN("digest"))) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "method has to be either \"basic\" or \"digest\" in", - "auth.require = ( \"...\" => ( ..., \"method\" => \"...\") )"); - return HANDLER_ERROR; - } - - if (realm == NULL) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "the require field is missing in:", - "auth.require = ( \"...\" => ( ..., \"realm\" => \"...\" ) )"); - return HANDLER_ERROR; - } - - if (require == NULL) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "the require field is missing in:", - "auth.require = ( \"...\" => ( ..., \"require\" => \"...\" ) )"); - return HANDLER_ERROR; - } - - if (method && realm && require) { - data_string *ds; - data_array *a; - - a = data_array_init(); - buffer_copy_string_buffer(a->key, da_file->key); - - ds = data_string_init(); - - buffer_copy_string_len(ds->key, CONST_STR_LEN("method")); - buffer_copy_string_buffer(ds->value, method); - - array_insert_unique(a->value, (data_unset *)ds); - - ds = data_string_init(); - - buffer_copy_string_len(ds->key, CONST_STR_LEN("realm")); - buffer_copy_string_buffer(ds->value, realm); - - array_insert_unique(a->value, (data_unset *)ds); - - ds = data_string_init(); - - buffer_copy_string_len(ds->key, CONST_STR_LEN("require")); - buffer_copy_string_buffer(ds->value, require); - - array_insert_unique(a->value, (data_unset *)ds); - - array_insert_unique(s->auth_require, (data_unset *)a); - } - } - } - - return HANDLER_GO_ON; -} - -handler_t auth_ldap_init(server *srv, mod_auth_plugin_config *s) { -#ifdef USE_LDAP - int ret; - struct berval credentials; - char *binddn_ptr = NULL; - - if (s->auth_ldap_filter->used) { - char *dollar; - - /* parse filter */ - - if (NULL == (dollar = 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; - } - - buffer_copy_string_len(s->ldap->ldap_filter_pre, s->auth_ldap_filter->ptr, dollar - s->auth_ldap_filter->ptr); - buffer_copy_string(s->ldap->ldap_filter_post, dollar+1); - } - - if (s->auth_ldap_url->used) { - if ((ret = ldap_initialize(&s->ldap->ldap, s->auth_ldap_url->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - - return HANDLER_ERROR; - } - - { - const int ldap_version = LDAP_VERSION3; - ret = ldap_set_option(s->ldap->ldap, LDAP_OPT_PROTOCOL_VERSION, &ldap_version); - } - if (ret != LDAP_OPT_SUCCESS) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - - ldap_memfree(s->ldap->ldap); - s->ldap->ldap = NULL; - - return HANDLER_ERROR; - } - - if (s->auth_ldap_starttls == 1) { - /* 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_is_empty(s->auth_ldap_cafile)) { - if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, - s->auth_ldap_cafile->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "Loading CA certificate failed:", ldap_err2string(ret)); - - ldap_memfree(s->ldap->ldap); - s->ldap->ldap = NULL; - - return HANDLER_ERROR; - } - } - - if (!buffer_is_empty(s->auth_ldap_cert)) { - if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE, - s->auth_ldap_cert->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "Loading TLS certificate failed:", ldap_err2string(ret)); - - ldap_memfree(s->ldap->ldap); - s->ldap->ldap = NULL; - - return HANDLER_ERROR; - } - } - - if (!buffer_is_empty(s->auth_ldap_key)) { - if (LDAP_OPT_SUCCESS != (ret = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE, - s->auth_ldap_key->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "Loading TLS key certificate failed:", ldap_err2string(ret)); - - ldap_memfree(s->ldap->ldap); - s->ldap->ldap = NULL; - - return HANDLER_ERROR; - } - } - - if (LDAP_OPT_SUCCESS != (ret = ldap_start_tls_s(s->ldap->ldap, NULL, NULL))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap startTLS failed:", ldap_err2string(ret)); - - ldap_memfree(s->ldap->ldap); - s->ldap->ldap = NULL; - - return HANDLER_ERROR; - } - } - - - /* 1. */ - if (s->auth_ldap_binddn->used) { - credentials.bv_val = s->auth_ldap_bindpw->ptr; - credentials.bv_len = s->auth_ldap_bindpw->used; - binddn_ptr = s->auth_ldap_binddn->ptr; - } else { - credentials.bv_val = NULL; - credentials.bv_len = 0; - binddn_ptr = NULL; - } - ret = ldap_sasl_bind_s(s->ldap->ldap, s->auth_ldap_binddn->ptr, LDAP_SASL_SIMPLE, &credentials, NULL, NULL, NULL); - if(ret != LDAP_SUCCESS) { - log_error_write(srv, __FILE__, __LINE__, "ss", "ldap:", ldap_err2string(ret)); - - ldap_memfree(s->ldap->ldap); - s->ldap->ldap = NULL; - return HANDLER_ERROR; - } - } -#else - UNUSED(s); - log_error_write(srv, __FILE__, __LINE__, "s", "no ldap support available"); - return HANDLER_ERROR; -#endif - return HANDLER_GO_ON; -} - -#ifdef USE_LDAP -void auth_ldap_cleanup(ldap_plugin_config *p) { - if (p->ldap != NULL) - ldap_unbind_ext_s(p->ldap, NULL, NULL); - p->ldap = NULL; -} -#endif - -LI_EXPORT int mod_auth_plugin_init(plugin *p); -LI_EXPORT 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; -} diff --git a/src/mod_auth.h b/src/mod_auth.h deleted file mode 100644 index e69de29b..00000000 --- a/src/mod_auth.h +++ /dev/null diff --git a/src/mod_cgi.c b/src/mod_cgi.c deleted file mode 100644 index 5e02826a..00000000 --- a/src/mod_cgi.c +++ /dev/null @@ -1,1312 +0,0 @@ -#include <sys/types.h> - -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <signal.h> -#include <ctype.h> -#include <assert.h> - -#include <stdio.h> -#include <fcntl.h> - -#include "server.h" -#include "stat_cache.h" -#include "keyvalue.h" -#include "log.h" -#include "connections.h" -#include "joblist.h" -#include "fdevent.h" -#include "inet_ntop_cache.h" - -#include "plugin.h" -#include "http_resp.h" - -#include "sys-files.h" -#include "sys-mmap.h" -#include "sys-socket.h" -#include "sys-strings.h" -#include "sys-process.h" - -#include "network_backends.h" - -#ifdef HAVE_SYS_FILIO_H -# include <sys/filio.h> -#endif - -enum {EOL_UNSET, EOL_N, EOL_RN}; - -typedef struct { - char **ptr; - - size_t size; - size_t used; -} char_array; - -#define pid_t int -typedef struct { - pid_t *ptr; - size_t used; - size_t size; -} buffer_pid_t; - -typedef struct { - array *cgi; - unsigned short execute_all; - unsigned short execute_x_only; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - buffer_pid_t cgi_pid; - - buffer *tmp_buf; - - http_resp *resp; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -typedef enum { - CGI_STATE_UNSET, - CGI_STATE_CONNECTING, - CGI_STATE_READ_RESPONSE_HEADER, - CGI_STATE_READ_RESPONSE_CONTENT -} cgi_state_t; - -typedef struct { - pid_t pid; - - iosocket *sock; - iosocket *sock_err; - iosocket *wb_sock; - - chunkqueue *rb; - chunkqueue *rb_err; - chunkqueue *wb; - - cgi_state_t state; - - connection *remote_con; /* dumb pointer */ -} cgi_session; - -static cgi_session * cgi_session_init() { - cgi_session *sess = calloc(1, sizeof(*sess)); - assert(sess); - - sess->sock = iosocket_init(); - sess->sock_err = iosocket_init(); - sess->wb_sock = iosocket_init(); - sess->wb = chunkqueue_init(); - sess->rb = chunkqueue_init(); - sess->rb_err = chunkqueue_init(); - - return sess; -} - -static void cgi_session_free(cgi_session *sess) { - if (!sess) return; - - iosocket_free(sess->sock); - iosocket_free(sess->sock_err); - iosocket_free(sess->wb_sock); - - chunkqueue_free(sess->wb); - chunkqueue_free(sess->rb); - chunkqueue_free(sess->rb_err); - - free(sess); -} - -INIT_FUNC(mod_cgi_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - assert(p); - - p->tmp_buf = buffer_init(); - p->resp = http_response_init(); - - 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]; - - array_free(s->cgi); - - free(s); - } - free(p->config_storage); - } - - - if (r->ptr) free(r->ptr); - - buffer_free(p->tmp_buf); - http_response_free(p->resp); - - free(p); - - return HANDLER_GO_ON; -} - -#define PLUGIN_NAME "cgi" -#define CONFIG_ASSIGN PLUGIN_NAME ".assign" -#define CONFIG_EXECUTE_ALL PLUGIN_NAME ".execute-all" -#define CONFIG_EXECUTE_X_ONLY PLUGIN_NAME ".execute-x-only" - -SETDEFAULTS_FUNC(mod_cgi_set_defaults) { - plugin_data *p = p_d; - size_t i = 0; - - config_values_t cv[] = { - { CONFIG_ASSIGN, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { CONFIG_EXECUTE_ALL, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { CONFIG_EXECUTE_X_ONLY, 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - assert(s); - - s->cgi = array_init(); - s->execute_all = 0; - s->execute_x_only = 0; - - cv[0].destination = s->cgi; - cv[1].destination = &(s->execute_all); - cv[2].destination = &(s->execute_x_only); - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - - -static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) { - int m = -1; - size_t i; - buffer_pid_t *r = &(p->cgi_pid); - - UNUSED(srv); - - for (i = 0; i < r->used; i++) { - if (r->ptr[i] > m) m = r->ptr[i]; - } - - if (r->size == 0) { - r->size = 16; - r->ptr = malloc(sizeof(*r->ptr) * r->size); - } else if (r->used == r->size) { - r->size += 16; - r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size); - } - - r->ptr[r->used++] = pid; - - return m; -} - -static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) { - size_t i; - buffer_pid_t *r = &(p->cgi_pid); - - UNUSED(srv); - - for (i = 0; i < r->used; i++) { - if (r->ptr[i] == pid) break; - } - - if (i != r->used) { - /* found */ - - if (i != r->used - 1) { - r->ptr[i] = r->ptr[r->used - 1]; - } - r->used--; - } - - return 0; -} - -/** - * Copy decoded response content to client connection. - */ -static int cgi_copy_response(server *srv, connection *con, cgi_session *sess) { - chunk *c; - int we_have = 0; - - UNUSED(srv); - - chunkqueue_remove_finished_chunks(sess->rb); - /* copy the content to the next cq */ - for (c = sess->rb->first; c; c = c->next) { - if (c->mem->used == 0) continue; - - we_have = chunkqueue_steal_chunk(con->send, c); - sess->rb->bytes_out += we_have; - con->send->bytes_in += we_have; - } - chunkqueue_remove_finished_chunks(sess->rb); - - if(sess->rb->is_closed) { - con->send->is_closed = 1; - } - return 0; -} - - -static int cgi_demux_response(server *srv, connection *con, plugin_data *p) { - cgi_session *sess = con->plugin_ctx[p->id]; - - switch(srv->network_backend_read(srv, con, sess->sock, sess->rb)) { - case NETWORK_STATUS_CONNECTION_CLOSE: - fdevent_event_del(srv->ev, sess->sock); - - /* connection closed. close the read chunkqueue. */ - sess->rb->is_closed = 1; - case NETWORK_STATUS_SUCCESS: - /* we got content */ - break; - case NETWORK_STATUS_WAIT_FOR_EVENT: - return 0; - default: - /* oops */ - ERROR("%s", "oops, read-pipe-read failed and I don't know why"); - return -1; - } - - /* looks like we got some content - * - * split off the header from the incoming stream - */ - - if (con->file_started == 0) { - size_t i; - int have_content_length = 0; - - http_response_reset(p->resp); - - /* the response header is not fully received yet, - * - * extract the http-response header from the rb-cq - */ - switch (http_response_parse_cq(sess->rb, p->resp)) { - case PARSE_UNSET: - case PARSE_ERROR: - /* parsing failed */ - - TRACE("%s", "response parser failed"); - - con->http_status = 502; /* Bad Gateway */ - return -1; - case PARSE_NEED_MORE: - if (sess->rb->is_closed) { - /* backend died before sending a header */ - con->http_status = 502; /* Bad Gateway */ - return -1; - } - return 0; - case PARSE_SUCCESS: - con->http_status = p->resp->status; - - chunkqueue_remove_finished_chunks(sess->rb); - - /* copy the http-headers */ - for (i = 0; i < p->resp->headers->used; i++) { - const char *ign[] = { "Status", "Connection", NULL }; - size_t j; - data_string *ds; - - data_string *header = (data_string *)p->resp->headers->data[i]; - - /* some headers are ignored by default */ - for (j = 0; ign[j]; j++) { - if (0 == strcasecmp(ign[j], header->key->ptr)) break; - } - if (ign[j]) continue; - - if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("Location"))) { - /* CGI/1.1 rev 03 - 7.2.1.2 */ - if (con->http_status == 0) con->http_status = 302; - } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("Content-Length"))) { - have_content_length = 1; - } - - if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { - ds = data_response_init(); - } - buffer_copy_string_buffer(ds->key, header->key); - buffer_copy_string_buffer(ds->value, header->value); - - array_insert_unique(con->response.headers, (data_unset *)ds); - } - - con->file_started = 1; - /* if Status: ... is not set, 200 is our default status-code */ - if (con->http_status == 0) con->http_status = 200; - sess->state = CGI_STATE_READ_RESPONSE_CONTENT; - - if (con->request.http_version == HTTP_VERSION_1_1 && - !have_content_length) { - con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; - } - - break; - } - } - - /* FIXME: pass the response-header to the other plugins to - * setup the filter-queue - * - * - use next-queue instead of con->write_queue - */ - - /* copy the resopnse content */ - cgi_copy_response(srv, con, sess); - - joblist_append(srv, con); - - return 0; -} - -static handler_t cgi_connection_close(server *srv, connection *con, plugin_data *p) { - cgi_session *sess = con->plugin_ctx[p->id]; - int status; - pid_t pid; - - if (NULL == sess) return HANDLER_GO_ON; - if (con->mode != p->id) return HANDLER_GO_ON; - -#ifndef _WIN32 - - /* the connection to the browser went away, but we still have a connection - * to the CGI script - * - * close cgi-connection - */ - - if (sess->sock->fd != -1) { - /* close connection to the cgi-script */ - fdevent_event_del(srv->ev, sess->sock); - fdevent_unregister(srv->ev, sess->sock); - } - - if (sess->sock_err->fd != -1) { - /* close connection to the cgi-script */ - fdevent_event_del(srv->ev, sess->sock_err); - fdevent_unregister(srv->ev, sess->sock_err); - } - - if (sess->wb_sock->fd != -1) { - close(sess->wb_sock->fd); - sess->wb_sock->fd = -1; - } - - pid = sess->pid; - - con->plugin_ctx[p->id] = NULL; - - /* is this a good idea ? */ - cgi_session_free(sess); - sess = NULL; - - /* if waitpid hasn't been called by response.c yet, do it here */ - if (pid) { - /* check if the CGI-script is already gone */ -#ifndef _WIN32 - switch(waitpid(pid, &status, WNOHANG)) { - case 0: - /* not finished yet */ -#if 0 - log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid); -#endif - break; - case -1: - /* */ - if (errno == EINTR) break; - - /* - * errno == ECHILD happens if _subrequest catches the process-status before - * we have read the response of the cgi process - * - * -> catch status - * -> WAIT_FOR_EVENT - * -> read response - * -> we get here with waitpid == ECHILD - * - */ - if (errno == ECHILD) return HANDLER_GO_ON; - - log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); - return HANDLER_ERROR; - default: - /* Send an error if we haven't sent any data yet */ - if (0 == con->file_started) { - if (con->http_status == 0) con->http_status = 500; - con->mode = DIRECT; - } - - if (WIFEXITED(status)) { -#if 0 - log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid); -#endif - pid = 0; - - return HANDLER_GO_ON; - } else { - log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid); - pid = 0; - return HANDLER_GO_ON; - } - } - - - kill(pid, SIGTERM); -#endif - /* cgi-script is still alive, queue the PID for removal */ - cgi_pid_add(srv, p, pid); - } -#endif - return HANDLER_GO_ON; -} - -static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) { - plugin_data *p = p_d; - - return cgi_connection_close(srv, con, p); -} - -static handler_t cgi_handle_fdevent(void *s, void *ctx, int revents) { - server *srv = (server *)s; - cgi_session *sess = ctx; - connection *con = sess->remote_con; - - if (revents & FDEVENT_IN) { - switch (sess->state) { - case CGI_STATE_READ_RESPONSE_HEADER: - /* parse the header and set file-started, the demuxer will care about it */ - joblist_append(srv, con); - - break; - case CGI_STATE_READ_RESPONSE_CONTENT: - /* just forward the content to the out-going queue */ - - chunkqueue_remove_finished_chunks(sess->rb); - - switch (srv->network_backend_read(srv, con, sess->sock, sess->rb)) { - case NETWORK_STATUS_CONNECTION_CLOSE: - fdevent_event_del(srv->ev, sess->sock); - - /* connection closed. close the read chunkqueue. */ - sess->rb->is_closed = 1; - case NETWORK_STATUS_SUCCESS: - /* read even more, do we have all the content */ - - /* how much do we want to read ? */ - - /* copy the resopnse content */ - cgi_copy_response(srv, con, sess); - - break; - default: - ERROR("%s", "oops, we failed to read"); - break; - } - - joblist_append(srv, con); - break; - default: - TRACE("unexpected state for a FDEVENT_IN: %d", sess->state); - break; - } - } - - if (revents & FDEVENT_OUT) { - /* nothing to do */ - } - - /* perhaps this issue is already handled */ - if (revents & FDEVENT_HUP) { - con->send->is_closed = 1; - - fdevent_event_del(srv->ev, sess->sock); - - joblist_append(srv, con); - } else if (revents & FDEVENT_ERR) { - con->send->is_closed = 1; - - /* kill all connections to the cgi process */ - fdevent_event_del(srv->ev, sess->sock); - joblist_append(srv, con); - } - - return HANDLER_FINISHED; -} - -/* so all cgi errors have the same source line as origin */ -static void cgi_log_err(const char *msg) { - ERROR("error from cgi: %s", msg); -} - -static void cgi_copy_err(chunkqueue *cq) { - buffer *line = buffer_init(); - chunk *c; - - for (c = cq->first; c; c = c->next) { - off_t we_have; - char *str, *nl; - - if (c->type != MEM_CHUNK) { - ERROR("%s", "wrong chunk type"); - chunk_set_done(c); - continue; - } - - we_have = c->mem->used - 1 - c->offset; - str = c->mem->ptr + c->offset; - if (we_have <= 0) continue; - - for ( ; NULL != (nl = strchr(str, '\n')); str = nl+1) { - *nl = '\0'; - if (!buffer_is_empty(line)) { - buffer_append_string(line, str); - cgi_log_err(SAFE_BUF_STR(line)); - buffer_reset(line); - } else { - cgi_log_err(str); - } - } - if (*str) { - buffer_append_string(line, str); - } - chunk_set_done(c); - } - - if (!buffer_is_empty(line)) { - cgi_log_err(SAFE_BUF_STR(line)); - } - chunkqueue_remove_finished_chunks(cq); -} - -static handler_t cgi_handle_err_fdevent(void *s, void *ctx, int revents) { - server *srv = (server *)s; - cgi_session *sess = ctx; - connection *con = sess->remote_con; - - if (revents & (FDEVENT_IN | FDEVENT_HUP)) { - switch (srv->network_backend_read(srv, con, sess->sock_err, sess->rb_err)) { - case NETWORK_STATUS_CONNECTION_CLOSE: - fdevent_event_del(srv->ev, sess->sock_err); - - /* connection closed. close the read chunkqueue. */ - sess->rb_err->is_closed = 1; - break; - case NETWORK_STATUS_SUCCESS: - break; - default: - ERROR("%s", "oops, we failed to read"); - break; - } - - cgi_copy_err(sess->rb_err); - } - - if (revents & FDEVENT_ERR) { - fdevent_event_del(srv->ev, sess->sock_err); - } - - return HANDLER_FINISHED; -} - - -static int cgi_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); - 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->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)); - } - - env->ptr[env->used++] = dst; - - return 0; -} - -static const char *sock_addr_to_p(server *srv, sock_addr *addr) { - switch (addr->plain.sa_family) { - case AF_INET: - return inet_ntoa(addr->ipv4.sin_addr); -#ifdef HAVE_IPV6 - case AF_INET6: - return inet_ntop_cache_get_ip(srv, addr); -#endif -#ifdef HAVE_SYS_UN_H - case AF_UNIX: - return addr->un.sun_path; -#endif - } - return ""; -} - -static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) { - pid_t pid; - - int to_cgi_fds[2]; - int from_cgi_fds[2]; - int from_cgi_err_fds[2]; - struct stat st; - -#ifndef _WIN32 - - if (cgi_handler && cgi_handler->used > 1) { - /* stat the exec file */ - if (-1 == (stat(cgi_handler->ptr, &st))) { - log_error_write(srv, __FILE__, __LINE__, "sbss", - "stat for cgi-handler", cgi_handler, - "failed:", strerror(errno)); - return -1; - } - } - - if (pipe(to_cgi_fds)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); - return -1; - } - - if (pipe(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; - } - - if (pipe(from_cgi_err_fds)) { - close(to_cgi_fds[0]); close(to_cgi_fds[1]); - close(from_cgi_fds[0]); close(from_cgi_fds[1]); - log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); - return -1; - } - - /* fork, execve */ - switch (pid = fork()) { - case 0: { - /* child */ - char **args; - int argc; - int i = 0; - char buf[32]; - size_t n; - char_array env; - char *c; - const char *s; - server_socket *srv_sock = con->srv_socket; - - /* move stdout to from_cgi_fd[1] */ - close(STDOUT_FILENO); - dup2(from_cgi_fds[1], STDOUT_FILENO); - close(from_cgi_fds[1]); - /* not needed */ - close(from_cgi_fds[0]); - - /* move stderr to from_cgi_err_fd[1] */ - close(STDERR_FILENO); - dup2(from_cgi_err_fds[1], STDERR_FILENO); - close(from_cgi_err_fds[1]); - /* not needed */ - close(from_cgi_err_fds[0]); - - /* move the stdin to to_cgi_fd[0] */ - close(STDIN_FILENO); - dup2(to_cgi_fds[0], STDIN_FILENO); - close(to_cgi_fds[0]); - /* not needed */ - close(to_cgi_fds[1]); - - /* create environment */ - env.ptr = NULL; - env.size = 0; - env.used = 0; - - cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_NAME"/"PACKAGE_VERSION)); - - s = sock_addr_to_p(srv, &srv_sock->addr); - cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); - /* !!! careful: s maybe reused for SERVER_NAME !!! */ - - if (!buffer_is_empty(con->server_name)) { - size_t len = con->server_name->used - 1; - char *colon = strchr(con->server_name->ptr, ':'); - if (colon) len = colon - con->server_name->ptr; - - cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len); - } else { - /* use SERVER_ADDR */ - cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); - } - cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); - - s = get_http_version_name(con->request.http_version); - - cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); - - LI_ltostr(buf, sock_addr_get_port(&srv_sock->addr)); - cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)); - - s = get_http_method_name(con->request.http_method); - cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); - - if (!buffer_is_empty(con->request.pathinfo)) { - cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); - } - cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); - if (!buffer_is_empty(con->uri.query)) { - cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); - } else { - /* set a empty QUERY_STRING */ - cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN("")); - } - if (!buffer_is_empty(con->request.orig_uri)) { - cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); - } - - s = sock_addr_to_p(srv, &con->dst_addr); - cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); - - LI_ltostr(buf, sock_addr_get_port(&con->dst_addr)); - cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)); - - if (!buffer_is_empty(con->authed_user)) { - cgi_env_add(&env, CONST_STR_LEN("REMOTE_USER"), - CONST_BUF_LEN(con->authed_user)); - } - -#ifdef USE_OPENSSL - if (srv_sock->is_ssl) { - cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); - } -#endif - - /* request.content_length < SSIZE_MAX, see request.c */ - if (con->request.content_length > 0) { - LI_ltostr(buf, con->request.content_length); - cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); - } - cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); - cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); - cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)); - - /* for valgrind */ - if (NULL != (s = getenv("LD_PRELOAD"))) { - cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s)); - } - - if (NULL != (s = getenv("LD_LIBRARY_PATH"))) { - cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s)); - } -#ifdef __CYGWIN__ - /* CYGWIN needs SYSTEMROOT */ - if (NULL != (s = getenv("SYSTEMROOT"))) { - cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s)); - } -#endif - - for (n = 0; n < con->request.headers->used; n++) { - data_string *ds; - - ds = (data_string *)con->request.headers->data[n]; - - if (ds->value->used && ds->key->used) { - size_t j; - - buffer_reset(p->tmp_buf); - - if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { - buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_")); - p->tmp_buf->used--; /* strip \0 after HTTP_ */ - } - - buffer_prepare_append(p->tmp_buf, ds->key->used + 2); - - for (j = 0; j < ds->key->used - 1; j++) { - char cr = '_'; - if (light_isalpha(ds->key->ptr[j])) { - /* upper-case */ - cr = ds->key->ptr[j] & ~32; - } else if (light_isdigit(ds->key->ptr[j])) { - /* copy */ - cr = ds->key->ptr[j]; - } - p->tmp_buf->ptr[p->tmp_buf->used++] = cr; - } - p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; - - cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); - } - } - - for (n = 0; n < con->environment->used; n++) { - data_string *ds; - - ds = (data_string *)con->environment->data[n]; - - if (ds->value->used && ds->key->used) { - size_t j; - - buffer_reset(p->tmp_buf); - - buffer_prepare_append(p->tmp_buf, ds->key->used + 2); - - for (j = 0; j < ds->key->used - 1; j++) { - char cr = '_'; - if (light_isalpha(ds->key->ptr[j])) { - /* upper-case */ - cr = ds->key->ptr[j] & ~32; - } else if (light_isdigit(ds->key->ptr[j])) { - /* copy */ - cr = ds->key->ptr[j]; - } - p->tmp_buf->ptr[p->tmp_buf->used++] = cr; - } - p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; - - cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); - } - } - - if (env.size == env.used) { - env.size += 16; - env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr)); - } - - env.ptr[env.used] = NULL; - - /* set up args */ - argc = 3; - args = malloc(sizeof(*args) * argc); - i = 0; - - if (cgi_handler && cgi_handler->used > 1) { - args[i++] = cgi_handler->ptr; - } - args[i++] = con->physical.path->ptr; - args[i++] = NULL; - - /* search for the last / */ - if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) { - *c = '\0'; - - /* change to the physical directory */ - if (-1 == chdir(con->physical.path->ptr)) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path); - } - *c = '/'; - } - - /* we don't need the client socket */ - for (i = 3; i < 256; i++) { - close(i); - } - - /* exec the cgi */ - execve(args[0], args, env.ptr); - - /* */ - SEGFAULT("execve(%s) failed: %s", args[0], strerror(errno)); - break; - } - case -1: - /* error */ - ERROR("fork() failed: %s", strerror(errno)); - close(to_cgi_fds[0]); close(to_cgi_fds[1]); - close(from_cgi_fds[0]); close(from_cgi_fds[1]); - close(from_cgi_err_fds[0]); close(from_cgi_err_fds[1]); - return -1; - break; - default: { - cgi_session *sess; - /* father */ - - close(from_cgi_fds[1]); - close(from_cgi_err_fds[1]); - close(to_cgi_fds[0]); - - /* register PID and wait for them asyncronously */ - con->mode = p->id; - buffer_reset(con->physical.path); - - sess = cgi_session_init(); - - sess->remote_con = con; - sess->pid = pid; - - assert(sess->sock); - - sess->sock->fd = from_cgi_fds[0]; - sess->sock->type = IOSOCKET_TYPE_PIPE; - sess->sock_err->fd = from_cgi_err_fds[0]; - sess->sock_err->type = IOSOCKET_TYPE_PIPE; - sess->wb_sock->fd = to_cgi_fds[1]; - sess->wb_sock->type = IOSOCKET_TYPE_PIPE; - - if (-1 == fdevent_fcntl_set(srv->ev, sess->sock)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); - - cgi_session_free(sess); - - return -1; - } - - if (-1 == fdevent_fcntl_set(srv->ev, sess->sock_err)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); - - cgi_session_free(sess); - - return -1; - } - - con->plugin_ctx[p->id] = sess; - - fdevent_register(srv->ev, sess->sock, cgi_handle_fdevent, sess); - fdevent_event_add(srv->ev, sess->sock, FDEVENT_IN); - - fdevent_register(srv->ev, sess->sock_err, cgi_handle_err_fdevent, sess); - fdevent_event_add(srv->ev, sess->sock_err, FDEVENT_IN); - - sess->state = CGI_STATE_READ_RESPONSE_HEADER; - - break; - } - } - - return 0; -#else - return -1; -#endif -} - -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_OPTION(cgi); - PATCH_OPTION(execute_all); - PATCH_OPTION(execute_x_only); - - /* 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_ASSIGN))) { - PATCH_OPTION(cgi); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXECUTE_ALL))) { - PATCH_OPTION(execute_all); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXECUTE_X_ONLY))) { - PATCH_OPTION(execute_x_only); - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_cgi_start_backend) { - size_t k, s_len; - plugin_data *p = p_d; - buffer *fn = con->physical.path; - stat_cache_entry *sce = NULL; - - if (fn->used == 0) return HANDLER_GO_ON; - - mod_cgi_patch_connection(srv, con, p); - - if (p->conf.cgi->used == 0 && p->conf.execute_all == 0) { - return HANDLER_GO_ON; - } - - if (con->conf.log_request_handling) { - TRACE("-- checking request in mod_%s", "cgi"); - } - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) return HANDLER_GO_ON; - if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON; - if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON; - - s_len = fn->used - 1; - - for (k = 0; k < p->conf.cgi->used; k++) { - data_string *ds = (data_string *)p->conf.cgi->data[k]; - size_t ct_len = ds->key->used - 1; - - if (ds->key->used == 0) continue; - if (s_len < ct_len) continue; - - if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) { - if (cgi_create_env(srv, con, p, ds->value)) { - con->http_status = 500; - - buffer_reset(con->physical.path); - return HANDLER_FINISHED; - } - return HANDLER_FINISHED; - } - } - - if (p->conf.execute_all) { - if (cgi_create_env(srv, con, p, NULL)) { - con->http_status = 500; - - buffer_reset(con->physical.path); - return HANDLER_FINISHED; - } - return HANDLER_FINISHED; - } - - return HANDLER_GO_ON; -} - -TRIGGER_FUNC(cgi_trigger) { - plugin_data *p = p_d; - size_t ndx; - /* the trigger handle only cares about lonely PID which we have to wait for */ -#ifndef _WIN32 - - for (ndx = 0; ndx < p->cgi_pid.used; ndx++) { - int status; - - switch(waitpid(p->cgi_pid.ptr[ndx], &status, WNOHANG)) { - case 0: - /* not finished yet */ -#if 0 - log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", p->cgi_pid.ptr[ndx]); -#endif - break; - case -1: - log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); - - return HANDLER_ERROR; - default: - - if (WIFEXITED(status)) { -#if 0 - log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]); -#endif - } else if (WIFSIGNALED(status)) { - log_error_write(srv, __FILE__, __LINE__, "sdsd", "cgi signaled, pid:", p->cgi_pid.ptr[ndx], ", signal", WTERMSIG(status)); - } else { - log_error_write(srv, __FILE__, __LINE__, "sdsd", "cgi died, pid:", p->cgi_pid.ptr[ndx], ", status", status); - } - - cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]); - /* del modified the buffer structure - * and copies the last entry to the current one - * -> recheck the current index - */ - ndx--; - } - } -#endif - return HANDLER_GO_ON; -} - -SUBREQUEST_FUNC(mod_cgi_read_response_content) { - int status; - plugin_data *p = p_d; - cgi_session *sess = con->plugin_ctx[p->id]; - - if (con->mode != p->id) return HANDLER_GO_ON; - if (NULL == sess) return HANDLER_GO_ON; - - switch (cgi_demux_response(srv, con, p)) { - case 0: - break; - case 1: - cgi_connection_close(srv, con, p); - - /* if we get a IN|HUP and have read everything don't exec the close twice */ - return HANDLER_FINISHED; - case -1: - cgi_connection_close(srv, con, p); - - if (0 == con->http_status) con->http_status = 500; - con->mode = DIRECT; - - return HANDLER_FINISHED; - } - -#if 0 - log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", sess, sess->pid); -#endif - if (sess->pid == 0) return HANDLER_FINISHED; -#ifndef _WIN32 - switch(waitpid(sess->pid, &status, WNOHANG)) { - case 0: - /* we only have for events here if we don't have the header yet, - * otherwise the event-handler will send us the incoming data */ - - if (!con->file_started) return HANDLER_WAIT_FOR_EVENT; - if (con->send->is_closed) return HANDLER_FINISHED; - - return HANDLER_GO_ON; - case -1: - if (errno == EINTR) return HANDLER_WAIT_FOR_EVENT; - - if (errno == ECHILD && con->file_started == 0) { - /* - * second round but still not response - */ - return HANDLER_WAIT_FOR_EVENT; - } - - log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); - con->mode = DIRECT; - con->http_status = 500; - - sess->pid = 0; - - fdevent_event_del(srv->ev, sess->sock); - fdevent_unregister(srv->ev, sess->sock); - - fdevent_event_del(srv->ev, sess->sock_err); - fdevent_unregister(srv->ev, sess->sock_err); - - cgi_session_free(sess); - sess = NULL; - - con->plugin_ctx[p->id] = NULL; - - return HANDLER_FINISHED; - default: - con->send->is_closed = 1; - - if (WIFEXITED(status)) { - /* nothing */ - } else { - log_error_write(srv, __FILE__, __LINE__, "s", "cgi died ?"); - - con->mode = DIRECT; - con->http_status = 500; - - } - - sess->pid = 0; - - fdevent_event_del(srv->ev, sess->sock); - fdevent_unregister(srv->ev, sess->sock); - - fdevent_event_del(srv->ev, sess->sock_err); - fdevent_unregister(srv->ev, sess->sock_err); - - cgi_session_free(sess); - sess = NULL; - - con->plugin_ctx[p->id] = NULL; - return HANDLER_FINISHED; - } -#else - return HANDLER_ERROR; -#endif -} - -URIHANDLER_FUNC(mod_cgi_send_request_content) { - plugin_data *p = p_d; - cgi_session *sess = con->plugin_ctx[p->id]; - - if (p->id != con->mode) return HANDLER_GO_ON; - - if (con->request.content_length > 0 && con->recv->bytes_in > con->recv->bytes_out) { - /* write request content. */ - switch (network_write_chunkqueue_write(srv, con, sess->wb_sock, con->recv)) { - case NETWORK_STATUS_SUCCESS: - /** fall through, still have data to write. */ - case NETWORK_STATUS_WAIT_FOR_EVENT: - /** fall through */ - case NETWORK_STATUS_WAIT_FOR_AIO_EVENT: - break; - case NETWORK_STATUS_CONNECTION_CLOSE: - /* the script might have written a response already. */ - break; - default: - TRACE("%s", "(error)"); - return HANDLER_ERROR; - } - chunkqueue_remove_finished_chunks(con->recv); - } - /* we have to close the pipe to finish the request. */ - if ((con->recv->is_closed && con->recv->bytes_in == con->recv->bytes_out) || - con->request.content_length <= 0) { - close(sess->wb_sock->fd); - sess->wb_sock->fd = -1; - } else { - /* there is more data to write. */ - return HANDLER_GO_ON; - } - - return mod_cgi_read_response_content(srv, con, p_d); -} - - -LI_EXPORT int mod_cgi_plugin_init(plugin *p); -LI_EXPORT 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_start_backend = mod_cgi_start_backend; - p->handle_send_request_content = mod_cgi_send_request_content; - p->handle_read_response_content = mod_cgi_read_response_content; - - p->handle_trigger = cgi_trigger; - p->init = mod_cgi_init; - p->cleanup = mod_cgi_free; - p->set_defaults = mod_cgi_set_defaults; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_chunked.c b/src/mod_chunked.c deleted file mode 100644 index 567a3bdb..00000000 --- a/src/mod_chunked.c +++ /dev/null @@ -1,381 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" -#include "response.h" -#include "filter.h" - -#include "plugin.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define CONFIG_CHUNKED_ENCODING "chunked.encoding" -#define CONFIG_CHUNKED_DEBUG "chunked.debug" - -/** - * This plugin adds HTTP/1.1 chunked encoding support - * as a filter module. - * - */ - -/* plugin config for all request/connections */ - -typedef struct { - unsigned short encoding; - unsigned short debug; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -typedef struct { - unsigned short debug; - filter *fl; -} handler_ctx; - -static handler_ctx * handler_ctx_init() { - handler_ctx * hctx; - - hctx = calloc(1, sizeof(*hctx)); - hctx->debug = 0; - hctx->fl = NULL; - - return hctx; -} - -static void handler_ctx_free(handler_ctx *hctx) { - - free(hctx); -} - -/* init the plugin data */ -INIT_FUNC(mod_chunked_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - return p; -} - -/* destroy the plugin data */ -FREE_FUNC(mod_chunked_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; - - free(s); - } - free(p->config_storage); - } - - free(p); - - return HANDLER_GO_ON; -} - -/* handle plugin config and check values */ - -SETDEFAULTS_FUNC(mod_chunked_set_defaults) { - plugin_data *p = p_d; - size_t i = 0; - - config_values_t cv[] = { - { CONFIG_CHUNKED_ENCODING, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { CONFIG_CHUNKED_DEBUG, NULL, T_CONFIG_SHORT, 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - s->encoding = 1; - s->debug = 0; - - cv[0].destination = &(s->encoding); - cv[1].destination = &(s->debug); - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -REQUESTDONE_FUNC(mod_chunked_reset) { - plugin_data *p = p_d; - handler_ctx *hctx = con->plugin_ctx[p->id]; - - UNUSED(srv); - - if (NULL == hctx) return HANDLER_GO_ON; - - handler_ctx_free(hctx); - con->plugin_ctx[p->id] = NULL; - - return HANDLER_GO_ON; -} - -static int mod_chunked_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i, j; - plugin_config *s = p->config_storage[0]; - - PATCH_OPTION(encoding); - PATCH_OPTION(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(CONFIG_CHUNKED_ENCODING))) { - PATCH_OPTION(encoding); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_CHUNKED_DEBUG))) { - PATCH_OPTION(debug); - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_chunked_response_header) { - plugin_data *p = p_d; - handler_ctx *hctx; - filter *fl; - chunkqueue *in; - int use_chunked = 0; - - mod_chunked_patch_connection(srv, con, p); - - /* get filter. */ - fl = filter_chain_get_filter(con->send_filters, p->id); - if (!fl) { - if (p->conf.debug > 0) TRACE("%s", "add chunked filter to filter chain"); - fl = filter_chain_create_filter(con->send_filters, p->id); - } - /* get our input and output chunkqueues. */ - if (!fl || !fl->prev) { - filter_chain_remove_filter(con->send_filters, fl); - return HANDLER_GO_ON; - } - in = fl->prev->cq; - - if (in->is_closed && (con->response.content_length < 0 || con->request.http_method != HTTP_METHOD_HEAD)) { - con->response.content_length = chunkqueue_length(in); - } - /* check if response needs chunked encoding. */ - if (con->request.http_method != HTTP_METHOD_HEAD) { - if(con->response.content_length >= 0) { - if (p->conf.debug > 0) TRACE("response content length known, disabling chunked encoding. len=%jd", (intmax_t) con->response.content_length); - use_chunked = 0; - } else { - /* a HEAD request never gets a chunk-encoding, but might stay with keep-alive - * in case the queue was closed already (above) we still have the content-length */ - - /* we don't know the size of the content yet - * - either enable chunking - * - or disable keep-alive */ - - if (con->request.http_version == HTTP_VERSION_1_1 && p->conf.encoding) { - use_chunked = 1; - } else { - if (p->conf.debug > 0) - TRACE("%s", "content length unknown and can't use chunked encoding. disable keep-alive"); - con->keep_alive = 0; - use_chunked = 0; - } - } - } - - if (!use_chunked) { - /* chunked encoding disabled. remove filter */ - con->response.transfer_encoding &= ~HTTP_TRANSFER_ENCODING_CHUNKED; - filter_chain_remove_filter(con->send_filters, fl); - return HANDLER_GO_ON; - } - - /* enable chunked encoding */ - con->response.transfer_encoding |= HTTP_TRANSFER_ENCODING_CHUNKED; - hctx = handler_ctx_init(); - hctx->debug = p->conf.debug; - con->plugin_ctx[p->id] = hctx; - hctx->fl = fl; - - if (hctx->debug > 0) - TRACE("%s", "chunked encoding enabled"); - - return HANDLER_GO_ON; -} - -static int http_chunk_append_len(chunkqueue *cq, size_t len) { - size_t i, olen = len, j; - buffer *b; - - b = buffer_init(); - - if (len == 0) { - buffer_copy_string_len(b, CONST_STR_LEN("0")); - } else { - for (i = 0; i < 8 && len; i++) { - len >>= 4; - } - - /* i is the number of hex digits we have */ - buffer_prepare_copy(b, i + 1); - - for (j = i-1, len = olen; j+1 > 0; j--) { - b->ptr[j] = (len & 0xf) + (((len & 0xf) <= 9) ? '0' : 'a' - 10); - len >>= 4; - } - b->used = i; - b->ptr[b->used++] = '\0'; - } - - buffer_append_string_len(b, CONST_STR_LEN("\r\n")); - chunkqueue_append_buffer(cq, b); - len = b->used - 1; - - buffer_free(b); - - return len; -} - -/** - * apply HTTP/1.1 chunked encoding if necessary - */ -URIHANDLER_FUNC(mod_chunked_encode_response_content) { - plugin_data *p = p_d; - handler_ctx *hctx = con->plugin_ctx[p->id]; - chunkqueue *in; - chunkqueue *out; - int we_have = 0; - chunk *c; - - UNUSED(srv); - - /* check if chunk-encoding is enabled. */ - if (!hctx) return HANDLER_GO_ON; - - /* get our input and output chunkqueues. */ - if (!hctx->fl || !hctx->fl->prev) return HANDLER_GO_ON; - in = hctx->fl->prev->cq; - out = hctx->fl->cq; - - /* we are all done already (see the end of the function) */ - if (out->is_closed) return HANDLER_GO_ON; - - /* move all chunks to the out queue - * and append a HTTP/1.1 chunked header to each chunk - */ - for (c = in->first; c; c = c->next) { - switch (c->type) { - case MEM_CHUNK: - if (c->mem->used == 0) continue; - - we_have = c->mem->used - c->offset - 1; - in->bytes_out += we_have; - if(we_have == 0) continue; - we_have += http_chunk_append_len(out, we_have); - chunkqueue_append_buffer(out, c->mem); - - chunk_set_done(c); - - break; - case FILE_CHUNK: - if (c->file.length == 0) continue; - - we_have = c->file.length; - in->bytes_out += we_have; - we_have += http_chunk_append_len(out, c->file.length); - - if(c->file.is_temp) { - chunkqueue_steal_tempfile(out, c); - } else { - chunkqueue_append_file(out, c->file.name, c->file.start, c->file.length); - } - - chunk_set_done(c); - - break; - case UNUSED_CHUNK: - break; - } - chunkqueue_append_mem(out, CONST_STR_LEN("\r\n")); - we_have += 2; - out->bytes_in += we_have; - } - - /* terminate the last chunk */ - if (in->is_closed) { - chunkqueue_append_mem(out, CONST_STR_LEN("0\r\n\r\n")); - out->bytes_in += 5; - } - - if (hctx->debug > 1) TRACE("chunk encoded: in=%jd, out=%jd", (intmax_t) in->bytes_out, (intmax_t) out->bytes_in); - - chunkqueue_remove_finished_chunks(in); - - /* the in side is closed, close out too */ - if (in->is_closed) { - /* mark the output queue as finished. */ - out->is_closed = 1; - } - - return HANDLER_GO_ON; -} - -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_chunked_plugin_init(plugin *p); -LI_EXPORT int mod_chunked_plugin_init(plugin *p) { - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("chunked"); - - p->init = mod_chunked_init; - p->set_defaults = mod_chunked_set_defaults; - p->handle_response_header = mod_chunked_response_header; - p->handle_filter_response_content = mod_chunked_encode_response_content; - p->connection_reset = mod_chunked_reset; - p->cleanup = mod_chunked_free; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_compress.c b/src/mod_compress.c deleted file mode 100644 index 7e72fb4a..00000000 --- a/src/mod_compress.c +++ /dev/null @@ -1,848 +0,0 @@ -#include <sys/types.h> -#include <sys/stat.h> - -#include <fcntl.h> -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <time.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" -#include "response.h" -#include "stat_cache.h" - -#include "plugin.h" - -#include "crc32.h" -#include "etag.h" - -#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 - -#include "sys-mmap.h" -#include "sys-files.h" - -/* 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) - -#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; -} 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; - - UNUSED(srv); - - 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 (!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)) { - ERROR("creating cache-directory \"%s\" failed: %s", filename, strerror(errno)); - *p = '/'; - return -1; - } - - *p++ = '/'; - if (!*p) { - ERROR("unexpected trailing slash for filename \"%s\"", filename); - return -1; - } - } - - 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 }, - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; 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; - - 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 */ - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - - if (encodings_arr->used) { - size_t j = 0; - for (j = 0; j < encodings_arr->used; j++) { - data_string *ds = (data_string *)encodings_arr->data[j]; -#ifdef USE_ZLIB - if (NULL != strstr(ds->value->ptr, "gzip")) - s->allowed_encodings |= HTTP_ACCEPT_ENCODING_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; -#endif - } - } else { - /* default encodings */ - s->allowed_encodings = 0 -#ifdef USE_ZLIB - | HTTP_ACCEPT_ENCODING_GZIP | HTTP_ACCEPT_ENCODING_DEFLATE -#endif -#ifdef USE_BZ2LIB - | HTTP_ACCEPT_ENCODING_BZIP2 -#endif - ; - } - - array_free(encodings_arr); - - if (!buffer_is_empty(s->compress_cache_dir)) { - struct stat st; - if (0 != stat(s->compress_cache_dir->ptr, &st)) { - - ERROR("can't stat compress.cache-dir (%s), attempting to create '%s'", strerror(errno),SAFE_BUF_STR(s->compress_cache_dir)); - mkdir_recursive(s->compress_cache_dir->ptr); - - if (0 != stat(s->compress_cache_dir->ptr, &st)) { - ERROR("can't stat compress.cache-dir (%s), failed to create '%s'", strerror(errno),SAFE_BUF_STR(s->compress_cache_dir)); - 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; - - 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_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 */ - - p->b->used = 10; - z.next_out = (unsigned char *)p->b->ptr + p->b->used; - z.avail_out = p->b->size - p->b->used - 8; - z.total_out = 0; - - if (Z_STREAM_END != deflate(&z, Z_FINISH)) { - deflateEnd(&z); - return -1; - } - - /* trailer */ - p->b->used += z.total_out; - - crc = generate_crc32c(start, st_size); - - c = (unsigned char *)p->b->ptr + p->b->used; - - 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; - p->b->used += 8; - - 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_prepare_copy(p->b, (z.avail_in * 1.1) + 12); - - z.next_out = (unsigned char *)p->b->ptr; - z.avail_out = p->b->size; - z.total_out = 0; - - if (Z_STREAM_END != deflate(&z, Z_FINISH)) { - deflateEnd(&z); - return -1; - } - - /* trailer */ - p->b->used += z.total_out; - - if (Z_OK != deflateEnd(&z)) { - return -1; - } - - 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_prepare_copy(p->b, (bz.avail_in * 1.1) + 12); - - bz.next_out = p->b->ptr; - bz.avail_out = p->b->size; - bz.total_out_lo32 = 0; - bz.total_out_hi32 = 0; - - if (BZ_STREAM_END != BZ2_bzCompress(&bz, BZ_FINISH)) { - BZ2_bzCompressEnd(&bz); - return -1; - } - - /* file is too large for now */ - if (bz.total_out_hi32) return -1; - - /* trailer */ - p->b->used = bz.total_out_lo32; - - if (BZ_OK != BZ2_bzCompressEnd(&bz)) { - return -1; - } - - return 0; -} -#endif - -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 = -1; - void *start; - const char *filename = fn->ptr; - ssize_t r; - stat_cache_entry *compressed_sce = NULL; - - if (buffer_is_empty(p->conf.compress_cache_dir)) return -1; - - /* 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_string_buffer(p->ofn, p->conf.compress_cache_dir); - PATHNAME_APPEND_SLASH(p->ofn); - - if (0 == strncmp(con->physical.path->ptr, con->physical.doc_root->ptr, con->physical.doc_root->used-1)) { - buffer_append_string(p->ofn, con->physical.path->ptr + con->physical.doc_root->used - 1); - buffer_copy_string_buffer(p->b, p->ofn); - } else { - buffer_append_string_buffer(p->ofn, con->uri.path); - } - - switch(type) { - case HTTP_ACCEPT_ENCODING_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: - buffer_append_string_len(p->ofn, CONST_STR_LEN("-bzip2-")); - break; - default: - ERROR("unknown compression type %d", type); - return -1; - } - - buffer_append_string_buffer(p->ofn, sce->etag); - - - if (HANDLER_ERROR != stat_cache_get_entry(srv, con, p->ofn, &compressed_sce)) { - /* file exists */ - if (con->conf.log_request_handling) TRACE("file exists in the cache (%s), sending it", SAFE_BUF_STR(p->ofn)); - - chunkqueue_reset(con->send); - chunkqueue_append_file(con->send, p->ofn, 0, compressed_sce->st.st_size); - con->send->is_closed = 1; - - return 0; - } - - if (-1 == (ofd = open(p->ofn->ptr, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600))) { - if (-1 == mkdir_for_file(p->ofn->ptr)) { - return -1; // error message in mkdir_for_file - } else if (-1 == (ofd = open(p->ofn->ptr, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0600))) { - ERROR("creating cachefile '%s' failed: %s", SAFE_BUF_STR(p->ofn), strerror(errno)); - return -1; - } - } - - if (-1 == (ifd = open(filename, O_RDONLY | O_BINARY))) { - ERROR("opening plain-file '%s' failed: %s", SAFE_BUF_STR(fn), strerror(errno)); - close(ofd); - return -1; - } - - - if (MAP_FAILED == (start = mmap(NULL, sce->st.st_size, PROT_READ, MAP_SHARED, ifd, 0))) { - ERROR("mmaping '%s' failed: %s", SAFE_BUF_STR(fn), strerror(errno)); - close(ofd); - close(ifd); - return -1; - } - - switch(type) { -#ifdef USE_ZLIB - case HTTP_ACCEPT_ENCODING_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: - ret = deflate_file_to_buffer_bzip2(srv, con, p, start, sce->st.st_size); - break; -#endif - default: - ret = -1; - break; - } - - if (-1 == (r = write(ofd, p->b->ptr, p->b->used))) { - munmap(start, sce->st.st_size); - close(ofd); - close(ifd); - return -1; - } - - if ((size_t)r != p->b->used) { - - } - - munmap(start, sce->st.st_size); - close(ofd); - close(ifd); - - if (ret != 0) return -1; - - chunkqueue_reset(con->send); - chunkqueue_append_file(con->send, p->ofn, 0, r); - con->send->is_closed = 1; - - 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; - void *start; - buffer *b; - - /* 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 (-1 == (ifd = open(fn->ptr, O_RDONLY | O_BINARY))) { - ERROR("opening plain-file '%s' failed: %s", SAFE_BUF_STR(fn), strerror(errno)); - - return -1; - } - - start = mmap(NULL, sce->st.st_size, PROT_READ, MAP_SHARED, ifd, 0); - - close(ifd); - - if (MAP_FAILED == start) { - ERROR("mmaping '%s' failed: %s", SAFE_BUF_STR(fn), strerror(errno)); - - return -1; - } - - switch(type) { -#ifdef USE_ZLIB - case HTTP_ACCEPT_ENCODING_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: - ret = deflate_file_to_buffer_bzip2(srv, con, p, start, sce->st.st_size); - break; -#endif - default: - ret = -1; - break; - } - - munmap(start, sce->st.st_size); - - if (ret != 0) return -1; - - chunkqueue_reset(con->send); - b = chunkqueue_get_append_buffer(con->send); - buffer_copy_memory(b, p->b->ptr, p->b->used + 1); - con->send->bytes_in += b->used-1; - - buffer_reset(con->physical.path); - - con->send->is_closed = 1; - con->file_started = 1; - - return 0; -} - -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_OPTION(compress_cache_dir); - PATCH_OPTION(compress); - PATCH_OPTION(compress_max_filesize); - PATCH_OPTION(allowed_encodings); - - /* 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_OPTION(compress_cache_dir); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.filetype"))) { - PATCH_OPTION(compress); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.max-filesize"))) { - PATCH_OPTION(compress_max_filesize); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("compress.allowed-encodings"))) { - PATCH_OPTION(allowed_encodings); - } - } - } - - return 0; -} - -PHYSICALPATH_FUNC(mod_compress_physical) { - plugin_data *p = p_d; - size_t m; - off_t max_fsize; - stat_cache_entry *sce = NULL; - data_string *ds; - int accept_encoding = 0; - char *value; - int matched_encodings = 0; - const char *dflt_gzip = "gzip"; - const char *dflt_deflate = "deflate"; - const char *dflt_bzip2 = "bzip2"; - - const char *compression_name = NULL; - int compression_type = 0; - buffer *mtime, *content_type; - - if (con->mode != DIRECT) return HANDLER_GO_ON; - - if (con->conf.log_request_handling) TRACE("-- %s", "handling in mod_compress"); - - /* 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_is_empty(con->physical.path)) { - return HANDLER_GO_ON; - } - - mod_compress_patch_connection(srv, con, p); - - max_fsize = p->conf.compress_max_filesize; - - if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { - if (con->conf.log_request_handling) TRACE("file '%s' not found", SAFE_BUF_STR(con->physical.path)); - 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) { - if (con->conf.log_request_handling) TRACE("file '%s' is too large: %jd", - SAFE_BUF_STR(con->physical.path), - (intmax_t) sce->st.st_size); - - return HANDLER_GO_ON; - } - - /* compressing the file might lead to larger files instead */ - if (sce->st.st_size < 128) { - if (con->conf.log_request_handling) TRACE("file '%s' is too small: %jd", - SAFE_BUF_STR(con->physical.path), - (intmax_t) sce->st.st_size); - - return HANDLER_GO_ON; - } - - /* check if mimetype is in compress-config */ - content_type = 0; - if (sce->content_type->ptr) { - char *c; - if ( (c = strchr(BUF_STR(sce->content_type), ';')) != 0) { - content_type = buffer_init(); - buffer_copy_string_len(content_type, BUF_STR(sce->content_type), c - BUF_STR(sce->content_type)); - } - } - for (m = 0; m < p->conf.compress->used; m++) { - data_string *compress_ds = (data_string *)p->conf.compress->data[m]; - - if (!compress_ds) { - ERROR("evil: %s .. %s", SAFE_BUF_STR(con->physical.path), SAFE_BUF_STR(con->uri.path)); - - return HANDLER_GO_ON; - } - - if (buffer_is_equal(compress_ds->value, sce->content_type) - || (content_type && buffer_is_equal(compress_ds->value, content_type))) { - break; - } - } - buffer_free(content_type); - - if (m == p->conf.compress->used) { - return HANDLER_GO_ON; - } - /* mimetype found */ - - - if (con->send->is_closed == 0) { - if (con->conf.log_request_handling) TRACE("we can't compress streams: is_closed = %d", con->send->is_closed); - return HANDLER_GO_ON; - } - - if (con->send->first == NULL) { - if (con->conf.log_request_handling) TRACE("we can't compress streams: ->first = %p", NULL); - return HANDLER_GO_ON; - } - - if (con->send->first->next != NULL) { - if (con->conf.log_request_handling) TRACE("we can't compress streams: ->first->next = %p", NULL); - return HANDLER_GO_ON; - } - - if (con->send->first->type != FILE_CHUNK) { - if (con->conf.log_request_handling) TRACE("we can compress file-chunks: ->type = %d", con->send->first->type); - return HANDLER_GO_ON; - } - - /* the response might change according to Accept-Encoding */ - response_header_insert(srv, con, CONST_STR_LEN("Vary"), CONST_STR_LEN("Accept-Encoding")); - - if (NULL == (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Accept-Encoding")))) { - if (con->conf.log_request_handling) TRACE("couldn't find a Accept-Encoding header: %s", ""); - return HANDLER_GO_ON; - } - - value = ds->value->ptr; - - /* get client side support encodings */ -#ifdef USE_ZLIB - if (NULL != strstr(value, "gzip")) accept_encoding |= HTTP_ACCEPT_ENCODING_GZIP; - if (NULL != strstr(value, "deflate")) accept_encoding |= HTTP_ACCEPT_ENCODING_DEFLATE; - if (NULL != strstr(value, "compress")) accept_encoding |= HTTP_ACCEPT_ENCODING_COMPRESS; -#endif -#ifdef USE_BZ2LIB - if (NULL != strstr(value, "bzip2")) accept_encoding |= HTTP_ACCEPT_ENCODING_BZIP2; -#endif - if (NULL != strstr(value, "identity")) accept_encoding |= HTTP_ACCEPT_ENCODING_IDENTITY; - - /* find matching entries */ - matched_encodings = accept_encoding & p->conf.allowed_encodings; - if (0 == matched_encodings) { - if (con->conf.log_request_handling) TRACE("we don't support the requested encoding: %s", value); - return HANDLER_GO_ON; - } - - mtime = strftime_cache_get(srv, sce->st.st_mtime); - etag_mutate(con->physical.etag, sce->etag); - - response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); - response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); - - /* perhaps we don't even have to compress the file as the browser still has the - * current version */ - if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { - if (con->conf.log_request_handling) TRACE("%s is still the same, caching", SAFE_BUF_STR(con->physical.path)); - 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_GZIP) { - compression_type = HTTP_ACCEPT_ENCODING_GZIP; - compression_name = dflt_gzip; - } else if (matched_encodings & HTTP_ACCEPT_ENCODING_DEFLATE) { - compression_type = HTTP_ACCEPT_ENCODING_DEFLATE; - compression_name = dflt_deflate; - } - - if (con->conf.log_request_handling) TRACE("we are fine, let's compress: %s", ""); - - /* deflate it to file (cached) or to memory */ - if (0 == deflate_file_to_file(srv, con, p, - con->physical.path, sce, compression_type) || - 0 == deflate_file_to_buffer(srv, con, p, - con->physical.path, sce, compression_type)) { - - response_header_overwrite(srv, con, - CONST_STR_LEN("Content-Encoding"), - compression_name, strlen(compression_name)); - - response_header_overwrite(srv, con, - CONST_STR_LEN("Content-Type"), - CONST_BUF_LEN(sce->content_type)); - - con->response.content_length = chunkqueue_length(con->send); - - if (con->conf.log_request_handling) TRACE("looks like %s could be compressed", SAFE_BUF_STR(con->physical.path)); - return HANDLER_FINISHED; - } - - return HANDLER_GO_ON; -} - -LI_EXPORT int mod_compress_plugin_init(plugin *p); -LI_EXPORT 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; - - /* we have to hook into the response-header settings */ - p->handle_response_header = mod_compress_physical; - - p->cleanup = mod_compress_free; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_deflate.c b/src/mod_deflate.c deleted file mode 100644 index 79deb11d..00000000 --- a/src/mod_deflate.c +++ /dev/null @@ -1,1408 +0,0 @@ -/* - * mod_deflate - dynamic compression - * - * This plugin is a response filter. - * - */ -#include <sys/types.h> -#include <sys/stat.h> - -#include <fcntl.h> -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <time.h> -#include <assert.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" -#include "response.h" -#include "joblist.h" -#include "stat_cache.h" -#include "filter.h" - -#include "plugin.h" - -#include "crc32.h" -#include "etag.h" -#include "inet_ntop_cache.h" - -#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 - -#include "sys-mmap.h" - -/* 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) - -/* encoding names */ -#define ENCODING_NAME_IDENTITY "identity" -#define ENCODING_NAME_GZIP "gzip" -#define ENCODING_NAME_DEFLATE "deflate" -#define ENCODING_NAME_COMPRESS "compress" -#define ENCODING_NAME_BZIP2 "bzip2" - - -#define CONFIG_DEFLATE_OUTPUT_BUFFER_SIZE "deflate.output-buffer-size" -#define CONFIG_DEFLATE_MIMETYPES "deflate.mimetypes" -#define CONFIG_DEFLATE_COMPRESSION_LEVEL "deflate.compression-level" -#define CONFIG_DEFLATE_MEM_LEVEL "deflate.mem-level" -#define CONFIG_DEFLATE_WINDOW_SIZE "deflate.window-size" -#define CONFIG_DEFLATE_MIN_COMPRESS_SIZE "deflate.min-compress-size" -#define CONFIG_DEFLATE_WORK_BLOCK_SIZE "deflate.work-block-size" -#define CONFIG_DEFLATE_ENABLED "deflate.enabled" -#define CONFIG_DEFLATE_DEBUG "deflate.debug" -#define CONFIG_DEFLATE_ALLOWED_ENCODINGS "deflate.allowed_encodings" -#define CONFIG_DEFLATE_SYNC_FLUSH "deflate.sync-flush" - -#define KByte * 1024 -#define MByte * 1024 KByte -#define GByte * 1024 MByte - -typedef struct { - unsigned short debug; - unsigned short enabled; - unsigned short sync_flush; - unsigned short output_buffer_size; - unsigned short min_compress_size; - unsigned short work_block_size; - int allowed_encodings; - short mem_level; - short compression_level; - short window_size; - array *mimetypes; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - buffer *tmp_buf; - array *encodings_arr; - - plugin_config **config_storage; - plugin_config conf; -} plugin_data; - -typedef struct { - off_t bytes_in; - filter *fl; - chunkqueue *in; - chunkqueue *out; - buffer *output; - /* compression type & state */ - int compression_type; - int stream_open; -#ifdef USE_ZLIB - unsigned long crc; - z_stream z; - unsigned short gzip_header; -#endif -#ifdef USE_BZ2LIB - bz_stream bz; -#endif - plugin_data *plugin_data; -} handler_ctx; - -static handler_ctx *handler_ctx_init() { - handler_ctx *hctx; - - hctx = calloc(1, sizeof(*hctx)); - hctx->in = NULL; - hctx->out = NULL; - - return hctx; -} - -static void handler_ctx_free(handler_ctx *hctx) { - free(hctx); -} - -INIT_FUNC(mod_deflate_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->tmp_buf = buffer_init(); - p->encodings_arr = array_init(); - - 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_arr); - - free(p); - - return HANDLER_GO_ON; -} - -SETDEFAULTS_FUNC(mod_deflate_setdefaults) { - plugin_data *p = p_d; - size_t i = 0; - - config_values_t cv[] = { - { CONFIG_DEFLATE_OUTPUT_BUFFER_SIZE, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_MIMETYPES, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_COMPRESSION_LEVEL, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_MEM_LEVEL, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_WINDOW_SIZE, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_MIN_COMPRESS_SIZE, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_WORK_BLOCK_SIZE, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_ENABLED, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_DEBUG, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_SYNC_FLUSH, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, - { CONFIG_DEFLATE_ALLOWED_ENCODINGS, 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - array_reset(p->encodings_arr); - - s = calloc(1, sizeof(plugin_config)); - s->enabled = 1; - s->sync_flush = 0; - s->allowed_encodings = 0; - s->debug = 0; - s->output_buffer_size = 0; - s->mem_level = 9; - s->window_size = 15; - s->min_compress_size = 0; - s->work_block_size = 2048; - s->compression_level = -1; - s->mimetypes = array_init(); - - cv[0].destination = &(s->output_buffer_size); - cv[1].destination = s->mimetypes; - cv[2].destination = &(s->compression_level); - cv[3].destination = &(s->mem_level); - cv[4].destination = &(s->window_size); - cv[5].destination = &(s->min_compress_size); - cv[6].destination = &(s->work_block_size); - cv[7].destination = &(s->enabled); - cv[8].destination = &(s->debug); - cv[9].destination = &(s->sync_flush); - cv[10].destination = p->encodings_arr; /* temp array for allowed encodings list */ - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - - if (p->encodings_arr->used) { - size_t j = 0; - for (j = 0; j < p->encodings_arr->used; j++) { - data_string *ds = (data_string *)p->encodings_arr->data[j]; -#ifdef USE_ZLIB - if (NULL != strstr(BUF_STR(ds->value), ENCODING_NAME_GZIP)) - s->allowed_encodings |= HTTP_ACCEPT_ENCODING_GZIP; - if (NULL != strstr(BUF_STR(ds->value), ENCODING_NAME_DEFLATE)) - s->allowed_encodings |= HTTP_ACCEPT_ENCODING_DEFLATE; -#endif - /* - if (NULL != strstr(BUF_STR(ds->value), ENCODING_NAME_COMPRESS)) - s->allowed_encodings |= HTTP_ACCEPT_ENCODING_COMPRESS; - */ -#ifdef USE_BZ2LIB - if (NULL != strstr(BUF_STR(ds->value), ENCODING_NAME_BZIP2)) - s->allowed_encodings |= HTTP_ACCEPT_ENCODING_BZIP2; -#endif - } - } else { - /* default encodings */ - s->allowed_encodings = HTTP_ACCEPT_ENCODING_IDENTITY | HTTP_ACCEPT_ENCODING_GZIP | - HTTP_ACCEPT_ENCODING_DEFLATE | HTTP_ACCEPT_ENCODING_COMPRESS | HTTP_ACCEPT_ENCODING_BZIP2; - } - - if((s->compression_level < 1 || s->compression_level > 9) && - s->compression_level != -1) { - ERROR("compression-level must be between 1 and 9: %i", s->compression_level); - return HANDLER_ERROR; - } - - if(s->mem_level < 1 || s->mem_level > 9) { - ERROR("mem-level must be between 1 and 9: %i", s->mem_level); - return HANDLER_ERROR; - } - - if(s->window_size < 1 || s->window_size > 15) { - ERROR("window-size must be between 1 and 15: %i", s->window_size); - return HANDLER_ERROR; - } - s->window_size = 0 - s->window_size; - - if(s->sync_flush) { - s->output_buffer_size = 0; - } - } - - return HANDLER_GO_ON; - -} - -#ifdef USE_ZLIB -/* Copied gzip_header from apache 2.2's mod_deflate.c */ -/* RFC 1952 Section 2.3 defines the gzip header: - * - * +---+---+---+---+---+---+---+---+---+---+ - * |ID1|ID2|CM |FLG| MTIME |XFL|OS | - * +---+---+---+---+---+---+---+---+---+---+ - */ -static const char gzip_header[10] = -{ '\037', '\213', Z_DEFLATED, 0, - 0, 0, 0, 0, /* mtime */ - 0, 0x03 /* Unix OS_CODE */ -}; -static int stream_deflate_init(server *srv, connection *con, handler_ctx *hctx) { - plugin_data *p = hctx->plugin_data; - z_stream *z; - int r, compression_level; - - UNUSED(srv); - UNUSED(con); - - z = &(hctx->z); - z->zalloc = Z_NULL; - z->zfree = Z_NULL; - z->opaque = Z_NULL; - z->total_in = 0; - z->total_out = 0; - z->next_out = NULL; - z->avail_out = 0; - - compression_level = p->conf.compression_level; - if(compression_level == -1) - compression_level = Z_DEFAULT_COMPRESSION; - - if(p->conf.debug) { - TRACE("output-buffer-size: %i", p->conf.output_buffer_size); - TRACE("compression-level: %i", compression_level); - TRACE("mem-level: %i", p->conf.mem_level); - TRACE("window-size: %i", p->conf.window_size); - TRACE("min-compress-size: %i", p->conf.min_compress_size); - TRACE("work-block-size: %i", p->conf.work_block_size); - } - if (Z_OK != (r = deflateInit2(z, - compression_level, - Z_DEFLATED, - p->conf.window_size, /* supress zlib-header */ - p->conf.mem_level, - Z_DEFAULT_STRATEGY))) { - ERROR("deflateInit2() failed with %d", r); - return -1; - } - hctx->stream_open = 1; - - return 0; -} - -static int stream_deflate_compress(server *srv, connection *con, handler_ctx *hctx, unsigned char *start, off_t st_size) { - plugin_data *p = hctx->plugin_data; - z_stream *z; - int len; - int in = 0, out = 0; - - UNUSED(srv); - UNUSED(con); - - z = &(hctx->z); - - if(z->next_out == NULL) { - z->next_out = (unsigned char *)hctx->output->ptr; - z->avail_out = hctx->output->size; - } - - if(hctx->compression_type == HTTP_ACCEPT_ENCODING_GZIP) { - if(hctx->gzip_header == 0) { - hctx->gzip_header = 1; - /* copy gzip header into output buffer */ - buffer_copy_memory(hctx->output, gzip_header, sizeof(gzip_header)); - if(p->conf.debug) { - TRACE("gzip_header len=%zu", sizeof(gzip_header)); - } - /* initialize crc32 */ - hctx->crc = crc32(0L, Z_NULL, 0); - z->next_out = (unsigned char *)(hctx->output->ptr + sizeof(gzip_header)); - z->avail_out = hctx->output->size - sizeof(gzip_header); - } - hctx->crc = crc32(hctx->crc, start, st_size); - } - - z->next_in = start; - z->avail_in = st_size; - hctx->bytes_in += st_size; - - /* compress data */ - in = z->avail_in; - do { - if (Z_OK != deflate(z, Z_NO_FLUSH)) { - deflateEnd(z); - hctx->stream_open = 0; - return -1; - } - - if(z->avail_out == 0 || z->avail_in > 0) { - len = hctx->output->size - z->avail_out; - out += len; - chunkqueue_append_mem(hctx->out, hctx->output->ptr, len); - hctx->out->bytes_in += len; - z->next_out = (unsigned char *)hctx->output->ptr; - z->avail_out = hctx->output->size; - } - } while (z->avail_in > 0); - - if(p->conf.debug) { - TRACE("compress: in=%i, out=%i", in, out); - } - return st_size; -} - -static int stream_deflate_flush(server *srv, connection *con, handler_ctx *hctx, int end) { - plugin_data *p = hctx->plugin_data; - z_stream *z; - int len; - int rc = 0; - int done; - int flush = 1; - int in = 0, out = 0; - - UNUSED(srv); - UNUSED(con); - - z = &(hctx->z); - - if(z->next_out == NULL) { - z->next_out = (unsigned char *)hctx->output->ptr; - z->avail_out = hctx->output->size; - } - /* compress data */ - in = z->avail_in; - do { - done = 1; - if(end) { - rc = deflate(z, Z_FINISH); - if (rc == Z_OK) { - done = 0; - } else if (rc != Z_STREAM_END) { - deflateEnd(z); - hctx->stream_open = 0; - return -1; - } - } else { - if(p->conf.sync_flush) { - rc = deflate(z, Z_SYNC_FLUSH); - } else if(z->avail_in > 0) { - if(p->conf.output_buffer_size > 0) flush = 0; - rc = deflate(z, Z_NO_FLUSH); - } else { - if(p->conf.output_buffer_size > 0) flush = 0; - rc = Z_OK; - } - if (rc != Z_OK) { - deflateEnd(z); - hctx->stream_open = 0; - return -1; - } - } - - len = hctx->output->size - z->avail_out; - if(z->avail_out == 0 || (flush && len > 0)) { - out += len; - chunkqueue_append_mem(hctx->out, hctx->output->ptr, len); - hctx->out->bytes_in += len; - z->next_out = (unsigned char *)hctx->output->ptr; - z->avail_out = hctx->output->size; - } - } while (z->avail_in != 0 || !done); - - - if(p->conf.debug) { - TRACE("flush: in=%i, out=%i", in, out); - } - if(p->conf.sync_flush) { - z->next_out = NULL; - z->avail_out = 0; - } - return 0; -} - -static int stream_deflate_end(server *srv, connection *con, handler_ctx *hctx) { - plugin_data *p = hctx->plugin_data; - z_stream *z; - int rc; - - UNUSED(srv); - UNUSED(con); - - z = &(hctx->z); - if(!hctx->stream_open) return 0; - hctx->stream_open = 0; - - if(hctx->compression_type == HTTP_ACCEPT_ENCODING_GZIP && (hctx->gzip_header)) { - /* write gzip footer */ - unsigned char c[8]; - - c[0] = (hctx->crc >> 0) & 0xff; - c[1] = (hctx->crc >> 8) & 0xff; - c[2] = (hctx->crc >> 16) & 0xff; - c[3] = (hctx->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; - /* append footer to write_queue */ - chunkqueue_append_mem(hctx->out, (char *)c, 8); - hctx->out->bytes_in += 8; - if(p->conf.debug) { - TRACE("gzip_footer len=%i", 8); - } - } - - if ((rc = deflateEnd(z)) != Z_OK) { - if(rc == Z_DATA_ERROR) return 0; - if(z->msg != NULL) { - ERROR("deflateEnd error ret=%i, msg='%s'", rc, z->msg); - } else { - ERROR("deflateEnd error ret=%i", rc); - } - return -1; - } - return 0; -} - -#endif - -#ifdef USE_BZ2LIB -static int stream_bzip2_init(server *srv, connection *con, handler_ctx *hctx) { - plugin_data *p = hctx->plugin_data; - bz_stream *bz; - int compression_level; - - UNUSED(srv); - UNUSED(con); - - bz = &(hctx->bz); - 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; - - compression_level = p->conf.compression_level; - if(compression_level == -1) - compression_level = 9; - - if(p->conf.debug) { - TRACE("output-buffer-size: %i", p->conf.output_buffer_size); - TRACE("compression-level: %i", compression_level); - TRACE("mem-level: %i", p->conf.mem_level); - TRACE("window-size: %i", p->conf.window_size); - TRACE("min-compress-size: %i", p->conf.min_compress_size); - TRACE("work-block-size: %i", p->conf.work_block_size); - } - if (BZ_OK != BZ2_bzCompressInit(bz, - compression_level, /* blocksize */ - 0, /* no output */ - 30)) { /* workFactor: default */ - return -1; - } - hctx->stream_open = 1; - - return 0; -} - -static int stream_bzip2_compress(server *srv, connection *con, handler_ctx *hctx, unsigned char *start, off_t st_size) { - plugin_data *p = hctx->plugin_data; - bz_stream *bz; - int len; - int rc; - int in = 0, out = 0; - - UNUSED(srv); - UNUSED(con); - - bz = &(hctx->bz); - - if(bz->next_out == NULL) { - bz->next_out = hctx->output->ptr; - bz->avail_out = hctx->output->size; - } - - bz->next_in = (char *)start; - bz->avail_in = st_size; - hctx->bytes_in += st_size; - - /* compress data */ - in = bz->avail_in; - do { - rc = BZ2_bzCompress(bz, BZ_RUN); - if (rc != BZ_RUN_OK) { - BZ2_bzCompressEnd(bz); - hctx->stream_open = 0; - return -1; - } - - if(bz->avail_out == 0 || bz->avail_in > 0) { - len = hctx->output->size - bz->avail_out; - out += len; - chunkqueue_append_mem(hctx->out, hctx->output->ptr, len); - hctx->out->bytes_in += len; - bz->next_out = hctx->output->ptr; - bz->avail_out = hctx->output->size; - } - } while (bz->avail_in > 0); - if(p->conf.debug) { - TRACE("compress: in=%i, out=%i", in, out); - } - return st_size; -} - -static int stream_bzip2_flush(server *srv, connection *con, handler_ctx *hctx, int end) { - plugin_data *p = hctx->plugin_data; - bz_stream *bz; - int len; - int rc; - int done; - int flush = 1; - int in = 0, out = 0; - - UNUSED(srv); - UNUSED(con); - - bz = &(hctx->bz); - - if(bz->next_out == NULL) { - bz->next_out = hctx->output->ptr; - bz->avail_out = hctx->output->size; - } - /* compress data */ - in = bz->avail_in; - do { - done = 1; - if(end) { - rc = BZ2_bzCompress(bz, BZ_FINISH); - if (rc == BZ_FINISH_OK) { - done = 0; - } else if (rc != BZ_STREAM_END) { - BZ2_bzCompressEnd(bz); - hctx->stream_open = 0; - return -1; - } - } else if(bz->avail_in > 0) { - rc = BZ2_bzCompress(bz, BZ_RUN); - if (rc != BZ_RUN_OK) { - BZ2_bzCompressEnd(bz); - hctx->stream_open = 0; - return -1; - } - if(p->conf.output_buffer_size > 0) flush = 0; - } - - len = hctx->output->size - bz->avail_out; - if(bz->avail_out == 0 || (flush && len > 0)) { - out += len; - chunkqueue_append_mem(hctx->out, hctx->output->ptr, len); - hctx->out->bytes_in += len; - bz->next_out = hctx->output->ptr; - bz->avail_out = hctx->output->size; - } - } while (bz->avail_in != 0 || !done); - if(p->conf.debug) { - TRACE("flush: in=%i, out=%i", in, out); - } - if(p->conf.sync_flush) { - bz->next_out = NULL; - bz->avail_out = 0; - } - return 0; -} - -static int stream_bzip2_end(server *srv, connection *con, handler_ctx *hctx) { - plugin_data *p = hctx->plugin_data; - bz_stream *bz; - int rc; - - UNUSED(p); - UNUSED(srv); - UNUSED(con); - - bz = &(hctx->bz); - if(!hctx->stream_open) return 0; - hctx->stream_open = 0; - - if ((rc = BZ2_bzCompressEnd(bz)) != BZ_OK) { - if(rc == BZ_DATA_ERROR) return 0; - ERROR("BZ2_bzCompressEnd error ret=%i", rc); - return -1; - } - return 0; -} - -#endif - -static int mod_deflate_compress(server *srv, connection *con, handler_ctx *hctx, unsigned char *start, off_t st_size) { - int ret = -1; - if(st_size == 0) return 0; - switch(hctx->compression_type) { -#ifdef USE_ZLIB - case HTTP_ACCEPT_ENCODING_GZIP: - case HTTP_ACCEPT_ENCODING_DEFLATE: - ret = stream_deflate_compress(srv, con, hctx, start, st_size); - break; -#endif -#ifdef USE_BZ2LIB - case HTTP_ACCEPT_ENCODING_BZIP2: - ret = stream_bzip2_compress(srv, con, hctx, start, st_size); - break; -#endif - default: - ret = -1; - break; - } - - return ret; -} - -static int mod_deflate_stream_flush(server *srv, connection *con, handler_ctx *hctx, int end) { - int ret = -1; - - if(hctx->bytes_in == 0) return 0; - switch(hctx->compression_type) { -#ifdef USE_ZLIB - case HTTP_ACCEPT_ENCODING_GZIP: - case HTTP_ACCEPT_ENCODING_DEFLATE: - ret = stream_deflate_flush(srv, con, hctx, end); - break; -#endif -#ifdef USE_BZ2LIB - case HTTP_ACCEPT_ENCODING_BZIP2: - ret = stream_bzip2_flush(srv, con, hctx, end); - break; -#endif - default: - ret = -1; - break; - } - - return ret; -} - -static int mod_deflate_stream_end(server *srv, connection *con, handler_ctx *hctx) { - int ret = -1; - switch(hctx->compression_type) { -#ifdef USE_ZLIB - case HTTP_ACCEPT_ENCODING_GZIP: - case HTTP_ACCEPT_ENCODING_DEFLATE: - ret = stream_deflate_end(srv, con, hctx); - break; -#endif -#ifdef USE_BZ2LIB - case HTTP_ACCEPT_ENCODING_BZIP2: - ret = stream_bzip2_end(srv, con, hctx); - break; -#endif - default: - ret = -1; - break; - } - - return ret; -} - -static int mod_deflate_file_chunk(server *srv, connection *con, handler_ctx *hctx, chunk *c, off_t st_size) { - plugin_data *p = hctx->plugin_data; - off_t abs_offset; - off_t toSend; - stat_cache_entry *sce = NULL; - size_t we_want_to_mmap = 2 MByte; - size_t we_want_to_send = st_size; - char *start = NULL; - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { - ERROR("stat_cache_get_entry('%s') failed: %s", - SAFE_BUF_STR(c->file.name), strerror(errno)); - return -1; - } - - abs_offset = c->file.start + c->offset; - - if (c->file.length + c->file.start > sce->st.st_size) { - ERROR("file '%s' was shrinked: was %ju, is %ju (%ju, %ju)", - SAFE_BUF_STR(c->file.name), (intmax_t) c->file.length + c->file.start, (intmax_t) sce->st.st_size, - (intmax_t) c->file.start, (intmax_t) c->offset); - - return -1; - } - - we_want_to_send = st_size; - /* 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 */ - size_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 + (off_t) 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 (-1 == c->file.fd) { /* open the file if not already open */ - if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { - ERROR("open failed for '%s': %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - return -1; - } -#ifdef FD_CLOEXEC - fcntl(c->file.fd, F_SETFD, FD_CLOEXEC); -#endif - } - - if (MAP_FAILED == (c->file.mmap.start = mmap(0, to_mmap, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) { - /* close it here, otherwise we'd have to set FD_CLOEXEC */ - - ERROR("mmap failed for '%s' (%i): %s", SAFE_BUF_STR(c->file.name), c->file.fd, strerror(errno)); - return -1; - } - - c->file.mmap.length = to_mmap; -#ifdef LOCAL_BUFFERING - buffer_copy_string_len(c->mem, c->file.mmap.start, c->file.mmap.length); -#else -#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)) { - ERROR("madvise failed for '%s' (%i): %s", SAFE_BUF_STR(c->file.name), c->file.fd, strerror(errno)); - } -#endif -#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 > (off_t) we_want_to_send) toSend = we_want_to_send; - - if (toSend < 0) { - ERROR("toSend is negative: %u %u %u %u", - (unsigned int) toSend, - (unsigned int) c->file.mmap.length, - (unsigned int) abs_offset, - (unsigned int) c->file.mmap.offset); - assert(toSend < 0); - } - -#ifdef LOCAL_BUFFERING - start = c->mem->ptr; -#else - start = c->file.mmap.start; -#endif - - if(p->conf.debug) { - TRACE("compress file chunk: offset=%i, toSend=%i", (int)c->offset, (int)toSend); - } - if (mod_deflate_compress(srv, con, hctx, - (unsigned char *)start + (abs_offset - c->file.mmap.offset), toSend) < 0) { - ERROR("%s", "compress failed."); - return -1; - } - - return toSend; -} - -static int deflate_compress_cleanup(server *srv, connection *con, handler_ctx *hctx) { - plugin_data *p = hctx->plugin_data; - int rc; - - rc = mod_deflate_stream_end(srv, con, hctx); - if(rc < 0) { - TRACE("error closing compressed stream for '%s', compressing with %d: %d", - SAFE_BUF_STR(con->uri.path_raw), hctx->compression_type, rc); - } - - if(p->conf.debug && hctx->bytes_in < hctx->out->bytes_in) { - TRACE("compressing uri '%s' increased the sent content-size from %jd to %jd", - SAFE_BUF_STR(con->uri.path_raw), (intmax_t) hctx->bytes_in, (intmax_t) hctx->out->bytes_in); - } - - /* cleanup compression state */ - if(hctx->output != p->tmp_buf) { - buffer_free(hctx->output); - } - handler_ctx_free(hctx); - con->plugin_ctx[p->id] = NULL; - - return 0; -} - -/** - * compress the in queue and move the content to the out queue - * - */ -static handler_t deflate_compress_response(server *srv, connection *con, handler_ctx *hctx, int end) { - plugin_data *p = hctx->plugin_data; - chunk *c; - size_t chunks_written = 0; - int chunk_finished = 0; - int rc=-1; - int we_want = 0, we_have = 0; - int out = 0, max = 0; - - we_have = chunkqueue_length(hctx->in); - if (p->conf.debug) { - TRACE("compress: in_queue len=%i", we_have); - } - /* calculate max bytes to compress for this call. */ - if (!end) { - max = p->conf.work_block_size * 1024; - if(max == 0 || max > we_have) max = we_have; - } else { - max = we_have; - } - - /* Compress chunks from in queue into chunks for out queue */ - for (c = hctx->in->first; c && max > 0; c = c->next) { - chunk_finished = 0; - we_have = 0; - we_want = 0; - - switch(c->type) { - case MEM_CHUNK: - if (c->mem->used == 0) continue; - - we_have = c->mem->used - c->offset - 1; - if (we_have == 0) continue; - - we_want = we_have < max ? we_have : max; - - if (mod_deflate_compress(srv, con, hctx, (unsigned char *)(c->mem->ptr + c->offset), we_want) < 0) { - ERROR("%s", "compress failed."); - return HANDLER_ERROR; - } - - break; - case FILE_CHUNK: - if (c->file.length == 0) continue; - - we_have = c->file.length - c->offset; - if (we_have == 0) continue; - - we_want = we_have < max ? we_have : max; - - if ((we_want = mod_deflate_file_chunk(srv, con, hctx, c, we_want)) < 0) { - ERROR("%s", "compress file chunk failed."); - return HANDLER_ERROR; - } - - break; - default: - ERROR("type not known: %d", c->type); - - return HANDLER_ERROR; - } - - hctx->in->bytes_out += we_want; - c->offset += we_want; - out += we_want; - max -= we_want; - - if (c->type == FILE_CHUNK && - c->offset == c->file.length && - c->file.mmap.start != MAP_FAILED) { - /* we don't need the mmaping anymore */ - munmap(c->file.mmap.start, c->file.mmap.length); - c->file.mmap.start = MAP_FAILED; - } - - if (we_have == we_want) { - /* chunk finished */ - chunk_finished = 1; - chunks_written++; - } - /* make sure we finished compressing the chunk before going to the next chunk */ - if(!chunk_finished) break; - } - - if (p->conf.debug) { - TRACE("compressed bytes: %i", out); - } - - if (chunks_written > 0) { - chunkqueue_remove_finished_chunks(hctx->in); - } - - /* check if we finished compressing all the content. */ - end = (hctx->in->is_closed && hctx->in->bytes_in == hctx->in->bytes_out); - - if (p->conf.debug) { - TRACE("end: %d - %jd - %jd", hctx->in->is_closed, (intmax_t) hctx->in->bytes_in, (intmax_t) hctx->in->bytes_out); - } - - /* flush the output buffer to make room for more data. */ - rc = mod_deflate_stream_flush(srv, con, hctx, end); - - if (rc < 0) { - ERROR("%s", "flush error"); - } - - if (end) { - hctx->out->is_closed = 1; - if(p->conf.debug) { - TRACE("finished uri: '%s', query: '%s'", SAFE_BUF_STR(con->uri.path_raw), SAFE_BUF_STR(con->uri.query)); - } - } else if (hctx->in->first) { - /* We have more data to compress. */ - joblist_append(srv, con); - } - - return HANDLER_GO_ON; -} - -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_OPTION(output_buffer_size); - PATCH_OPTION(mimetypes); - PATCH_OPTION(compression_level); - PATCH_OPTION(mem_level); - PATCH_OPTION(window_size); - PATCH_OPTION(min_compress_size); - PATCH_OPTION(work_block_size); - PATCH_OPTION(enabled); - PATCH_OPTION(debug); - PATCH_OPTION(allowed_encodings); - PATCH_OPTION(sync_flush); - - /* 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_DEFLATE_OUTPUT_BUFFER_SIZE))) { - PATCH_OPTION(output_buffer_size); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_MIMETYPES))) { - PATCH_OPTION(mimetypes); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_COMPRESSION_LEVEL))) { - PATCH_OPTION(compression_level); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_MEM_LEVEL))) { - PATCH_OPTION(mem_level); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_WINDOW_SIZE))) { - PATCH_OPTION(window_size); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_MIN_COMPRESS_SIZE))) { - PATCH_OPTION(min_compress_size); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_WORK_BLOCK_SIZE))) { - PATCH_OPTION(work_block_size); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_ENABLED))) { - PATCH_OPTION(enabled); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_DEBUG))) { - PATCH_OPTION(debug); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_ALLOWED_ENCODINGS))) { - PATCH_OPTION(allowed_encodings); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEFLATE_SYNC_FLUSH))) { - PATCH_OPTION(sync_flush); - } - } - } - - return 0; -} - -PHYSICALPATH_FUNC(mod_deflate_handle_response_header) { - plugin_data *p = p_d; - handler_ctx *hctx; - filter *fl; - chunkqueue *in; - data_string *ds; - int accept_encoding = 0; - char *value; - int matched_encodings = 0; - const char *compression_name = NULL; - int file_len=0; - int rc=-2; - int end = 0; - size_t m; - - /* 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); - - /* is compression allowed */ - if(!p->conf.enabled) { - if(p->conf.debug) { - TRACE("%s", "compression disabled."); - } - return HANDLER_GO_ON; - } - - /* Check if response has a Content-Encoding. */ - if (NULL != (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Content-Encoding")))) { - return HANDLER_GO_ON; - } - - /* Check Accept-Encoding for supported encoding. */ - if (NULL == (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Accept-Encoding")))) { - return HANDLER_GO_ON; - } - - /* get client side support encodings */ - value = ds->value->ptr; -#ifdef USE_ZLIB - if (NULL != strstr(value, ENCODING_NAME_GZIP)) accept_encoding |= HTTP_ACCEPT_ENCODING_GZIP; - if (NULL != strstr(value, ENCODING_NAME_DEFLATE)) accept_encoding |= HTTP_ACCEPT_ENCODING_DEFLATE; -#endif - /* if (NULL != strstr(value, ENCODING_NAME_COMPRESS)) accept_encoding |= HTTP_ACCEPT_ENCODING_COMPRESS; */ -#ifdef USE_BZ2LIB - if (NULL != strstr(value, ENCODING_NAME_BZIP2)) accept_encoding |= HTTP_ACCEPT_ENCODING_BZIP2; -#endif - if (NULL != strstr(value, ENCODING_NAME_IDENTITY)) accept_encoding |= HTTP_ACCEPT_ENCODING_IDENTITY; - - /* find matching encodings */ - matched_encodings = accept_encoding & p->conf.allowed_encodings; - if (!matched_encodings) { - return HANDLER_GO_ON; - } - -#if 0 - /* TODO: add option to disable compression for HTTP 1.0 clients. */ - if (con->request.true_http_10_client) { - /*disable gzip/bzip2 when we meet HTTP 1.0 client with Accept-Encoding - * maybe old buggy proxy server - */ - /* most of buggy clients are Yahoo Slurp;) */ - if (p->conf.debug) TRACE("Buggy HTTP 1.0 client sending 'Accept Encoding: gzip, deflate': %i", - (char *) inet_ntop_cache_get_ip(srv, &(con->dst_addr))); - return HANDLER_GO_ON; - } -#endif - - /* get filter. */ - fl = filter_chain_get_filter(con->send_filters, p->id); - if (!fl) { - if(p->conf.debug) TRACE("%s", "add deflate filter to filter chain"); - fl = filter_chain_create_filter(con->send_filters, p->id); - } - /* get our input and output chunkqueues. */ - if (!fl || !fl->prev) { - filter_chain_remove_filter(con->send_filters, fl); - return HANDLER_GO_ON; - } - in = fl->prev->cq; - - /* check if size of response is below min-compress-size */ - if(in->is_closed && con->request.http_method != HTTP_METHOD_HEAD) { - file_len = chunkqueue_length(in); - if(file_len == 0) { - filter_chain_remove_filter(con->send_filters, fl); - return HANDLER_GO_ON; - } - } else { - file_len = 0; - } - - if(file_len > 0 && p->conf.min_compress_size > 0 && file_len < p->conf.min_compress_size) { - if(p->conf.debug) { - TRACE("Content-Length smaller then min_compress_size: file_len=%i", file_len); - } - filter_chain_remove_filter(con->send_filters, fl); - return HANDLER_GO_ON; - } - - /* Check mimetype in response header "Content-Type" */ - if (NULL != (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Content-Type")))) { - int found = 0; - if(p->conf.debug) { - TRACE("Content-Type: %s", SAFE_BUF_STR(ds->value)); - } - for (m = 0; m < p->conf.mimetypes->used; m++) { - data_string *mimetype = (data_string *)p->conf.mimetypes->data[m]; - - if(p->conf.debug) { - TRACE("mime-type: %s", SAFE_BUF_STR(mimetype->value)); - } - if (strncmp(mimetype->value->ptr, ds->value->ptr, mimetype->value->used-1) == 0) { - /* mimetype found */ - found = 1; - break; - } - } - if(!found && p->conf.mimetypes->used > 0) { - if(p->conf.debug) { - TRACE("No compression for mimetype: %s", SAFE_BUF_STR(ds->value)); - } - filter_chain_remove_filter(con->send_filters, fl); - return HANDLER_GO_ON; - } -#if 0 - if(strncasecmp(ds->value->ptr, "application/x-javascript", 24) == 0) { - /*reset compress type to deflate for javascript - * prevent buggy IE6 SP1 doesn't work for js in IFrame - */ - matched_encodings = HTTP_ACCEPT_ENCODING_DEFLATE; - } -#endif - } - - /* the response might change according to Accept-Encoding */ - if (NULL != (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Vary")))) { - /* append Accept-Encoding to Vary header */ - if (NULL == strstr(ds->value->ptr, "Accept-Encoding")) { - buffer_append_string_len(ds->value, CONST_STR_LEN(",Accept-Encoding")); - if (p->conf.debug) { - TRACE("appending ,Accept-Encoding for '%s'", SAFE_BUF_STR(con->uri.path)); - } - } - } else { - if (p->conf.debug) { - TRACE("add Vary: Accept-Encoding for '%s'", SAFE_BUF_STR(con->uri.path)); - } - response_header_insert(srv, con, CONST_STR_LEN("Vary"), - CONST_STR_LEN("Accept-Encoding")); - } - - /* enable compression */ - hctx = handler_ctx_init(); - hctx->plugin_data = p; - hctx->fl = fl; - hctx->in = fl->prev->cq; - hctx->out = fl->cq; - - rc = -1; - - /* select best matching encoding */ - if (matched_encodings & HTTP_ACCEPT_ENCODING_BZIP2) { -#ifdef USE_BZ2LIB - hctx->compression_type = HTTP_ACCEPT_ENCODING_BZIP2; - compression_name = ENCODING_NAME_BZIP2; - rc = stream_bzip2_init(srv, con, hctx); -#endif - } else if (matched_encodings & HTTP_ACCEPT_ENCODING_GZIP) { -#ifdef USE_ZLIB - hctx->compression_type = HTTP_ACCEPT_ENCODING_GZIP; - compression_name = ENCODING_NAME_GZIP; - rc = stream_deflate_init(srv, con, hctx); - } else if (matched_encodings & HTTP_ACCEPT_ENCODING_DEFLATE) { - hctx->compression_type = HTTP_ACCEPT_ENCODING_DEFLATE; - compression_name = ENCODING_NAME_DEFLATE; - rc = stream_deflate_init(srv, con, hctx); -#endif - } else if (matched_encodings & HTTP_ACCEPT_ENCODING_IDENTITY) { - /* no compression */ - } else { - ERROR("Failed to initialize compression for '%s' with compression = %s", - SAFE_BUF_STR(con->uri.path), - value); - } - - if (rc == -1 && compression_name) { - ERROR("Failed to initialize compression for '%s' with compression = %s", - SAFE_BUF_STR(con->uri.path), - compression_name); - } - - if (rc < 0) { - filter_chain_remove_filter(con->send_filters, hctx->fl); - handler_ctx_free(hctx); - return HANDLER_GO_ON; - } - - if (p->conf.debug) { - TRACE("compressing '%s' with compression = %s", - SAFE_BUF_STR(con->uri.path), - compression_name); - } - - /* setup output buffer. */ - if(p->conf.sync_flush || p->conf.output_buffer_size == 0) { - buffer_prepare_copy(p->tmp_buf, 32 * 1024); - hctx->output = p->tmp_buf; - } else { - hctx->output = buffer_init(); - buffer_prepare_copy(hctx->output, p->conf.output_buffer_size); - } - con->plugin_ctx[p->id] = hctx; - - /* set Content-Encoding to show selected compression type. */ - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name)); - - /* clear old Content-Length */ - con->response.content_length = -1; - - if (hctx->in->is_closed && - (p->conf.work_block_size == 0 || file_len < (p->conf.work_block_size * 1024)) - && con->request.http_method != HTTP_METHOD_HEAD) { - end = 1; - if(p->conf.debug) { - TRACE("Compress all content and use Content-Length header: uncompress len=%i", file_len); - } - } - - if (p->conf.debug) - TRACE("end = %i for uri '%s'", end, SAFE_BUF_STR(con->uri.path)); - - rc = deflate_compress_response(srv, con, hctx, end); - /* check if we finished compressing all the content. */ - if (rc == HANDLER_GO_ON && hctx->out->is_closed) { - con->response.content_length = chunkqueue_length(hctx->out); - - deflate_compress_cleanup(srv, con, hctx); - } - - return rc; -} - -CONNECTION_FUNC(mod_deflate_handle_filter_response_content) { - plugin_data *p = p_d; - handler_ctx *hctx = con->plugin_ctx[p->id]; - handler_t ret; - - if (hctx == NULL) return HANDLER_GO_ON; - if (!hctx->stream_open) return HANDLER_GO_ON; - - /** - * HEAD requests don't have a content body */ - if (con->request.http_method == HTTP_METHOD_HEAD) return HANDLER_GO_ON; - - /** compress the streams */ - ret = deflate_compress_response(srv, con, hctx, 0); - - if (ret == HANDLER_GO_ON && hctx->out->is_closed) { - deflate_compress_cleanup(srv, con, hctx); - } - - return ret; -} - -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(hctx == NULL) return HANDLER_GO_ON; - - if(p->conf.debug && hctx->stream_open) { - TRACE("stream open at cleanup. uri='%s', query='%s'", SAFE_BUF_STR(con->uri.path_raw), SAFE_BUF_STR(con->uri.query)); - } - - deflate_compress_cleanup(srv, con, hctx); - - return HANDLER_GO_ON; -} - -LI_EXPORT int mod_deflate_plugin_init(plugin *p); -LI_EXPORT 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_connection_close = mod_deflate_cleanup; - p->handle_response_header = mod_deflate_handle_response_header; - p->handle_filter_response_content = mod_deflate_handle_filter_response_content; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_dirlisting.c b/src/mod_dirlisting.c deleted file mode 100644 index 63f412dc..00000000 --- a/src/mod_dirlisting.c +++ /dev/null @@ -1,957 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <errno.h> -#include <stdio.h> -#include <time.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#include "response.h" -#include "stat_cache.h" -#include "stream.h" -#include "etag.h" - -#include "sys-strings.h" - -/** - * this is a dirlisting for a lighttpd plugin - */ - - -#ifdef HAVE_SYS_SYSLIMITS_H -#include <sys/syslimits.h> -#endif - -#ifdef HAVE_XATTR -#include <attr/attributes.h> -#endif - -#include "sys-files.h" -#include "sys-strings.h" - -/* 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 show_readme; - unsigned short hide_readme_file; - unsigned short show_header; - unsigned short hide_header_file; - - excludes_buffer *excludes; - - buffer *external_css; - buffer *encoding; - buffer *set_footer; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - buffer *tmp_buf; - buffer *content_charset; - buffer *path; - - 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; -} - -static int excludes_buffer_append(excludes_buffer *exb, buffer *string) { -#ifdef HAVE_PCRE_H - 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_string_buffer(exb->ptr[exb->used]->string, string); - - exb->used++; - - return 0; -#else - UNUSED(exb); - UNUSED(string); - - return -1; -#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; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->tmp_buf = buffer_init(); - p->content_charset = buffer_init(); - p->path = buffer_init(); - - return p; -} - -/* destroy 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->external_css); - buffer_free(s->encoding); - buffer_free(s->set_footer); - - free(s); - } - free(p->config_storage); - } - - buffer_free(p->tmp_buf); - buffer_free(p->path); - buffer_free(p->content_charset); - - free(p); - - return HANDLER_GO_ON; -} - -static int parse_config_entry(server *srv, plugin_config *s, array *ca, const char *option) { - data_unset *du; - - if (NULL != (du = array_get_element(ca, option, strlen(option)))) { - data_array *da = (data_array *)du; - size_t j; - - if (du->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "sss", - "unexpected type for key: ", option, "array of strings"); - - return HANDLER_ERROR; - } - - da = (data_array *)du; - - for (j = 0; j < da->value->used; j++) { - if (da->value->data[j]->type != TYPE_STRING) { - log_error_write(srv, __FILE__, __LINE__, "sssbs", - "unexpected type for key: ", option, "[", - da->value->data[j]->key, "](string)"); - - return HANDLER_ERROR; - } - - if (0 != excludes_buffer_append(s->excludes, - ((data_string *)(da->value->data[j]))->value)) { -#ifdef HAVE_PCRE_H - log_error_write(srv, __FILE__, __LINE__, "sb", - "pcre-compile failed for", ((data_string *)(da->value->data[j]))->value); -#else - log_error_write(srv, __FILE__, __LINE__, "s", - "pcre support is missing, please install libpcre and the headers"); -#endif - } - } - } - - return 0; -} - -/* 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_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" - - -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_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ - { CONFIG_HIDE_README_FILE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ - { CONFIG_SHOW_HEADER, NULL, T_CONFIG_BOOLEAN, 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 */ - - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - if (!p) return HANDLER_ERROR; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - array *ca; - - s = calloc(1, sizeof(plugin_config)); - s->excludes = excludes_buffer_init(); - s->dir_listing = 0; - s->external_css = buffer_init(); - s->hide_dot_files = 0; - s->show_readme = 0; - s->hide_readme_file = 0; - s->show_header = 0; - s->hide_header_file = 0; - 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; - - p->config_storage[i] = s; - ca = ((data_config *)srv->config_context->data[i])->value; - - if (0 != config_insert_values_global(srv, ca, cv)) { - return HANDLER_ERROR; - } - - parse_config_entry(srv, s, ca, CONFIG_EXCLUDE); - } - - return HANDLER_GO_ON; -} - -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_OPTION(dir_listing); - PATCH_OPTION(external_css); - PATCH_OPTION(hide_dot_files); - PATCH_OPTION(encoding); - PATCH_OPTION(show_readme); - PATCH_OPTION(hide_readme_file); - PATCH_OPTION(show_header); - PATCH_OPTION(hide_header_file); - PATCH_OPTION(excludes); - PATCH_OPTION(set_footer); - - /* 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_OPTION(dir_listing); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_DOTFILES))) { - PATCH_OPTION(hide_dot_files); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXTERNAL_CSS))) { - PATCH_OPTION(external_css); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODING))) { - PATCH_OPTION(encoding); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SHOW_README))) { - PATCH_OPTION(show_readme); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_README_FILE))) { - PATCH_OPTION(hide_readme_file); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SHOW_HEADER))) { - PATCH_OPTION(show_header); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_HEADER_FILE))) { - PATCH_OPTION(hide_header_file); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SET_FOOTER))) { - PATCH_OPTION(set_footer); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXCLUDE))) { - PATCH_OPTION(excludes); - } - } - } - - return 0; -} - -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, off_t size) { - const char unit[] = "KMGTPE"; /* Kilo, Mega, Tera, Peta, Exa */ - const char *u = unit - 1; /* u will always increment at least once */ - int remain; - char *out = buf; - - 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++; - } - - out += LI_ltostr(out, size); - out[0] = '.'; - out[1] = remain + '0'; - out[2] = *u; - out[3] = '\0'; - - return (out + 3 - buf); -} - -static void http_list_directory_header(server *srv, connection *con, plugin_data *p, buffer *out) { - UNUSED(srv); - - buffer_append_string_len(out, CONST_STR_LEN( - "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n" - "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n" - "<head>\n" - "<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 (p->conf.external_css->used > 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")); - - /* HEADER.txt */ - if (p->conf.show_header) { - stream s; - /* if we have a HEADER file, display it in <pre class="header"></pre> */ - - buffer_copy_string_buffer(p->tmp_buf, con->physical.path); - PATHNAME_APPEND_SLASH(p->tmp_buf); - buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("HEADER.txt")); - - if (-1 != stream_open(&s, p->tmp_buf)) { - buffer_append_string_len(out, CONST_STR_LEN("<pre class=\"header\">")); - buffer_append_string_encoded(out, s.start, s.size, ENCODING_MINIMAL_XML); - buffer_append_string_len(out, CONST_STR_LEN("</pre>")); - } - stream_close(&s); - } - - 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" - "<tr>" - "<td class=\"n\"><a href=\"../\">Parent Directory</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 (p->conf.show_readme) { - stream s; - /* if we have a README file, display it in <pre class="readme"></pre> */ - - buffer_copy_string_buffer(p->tmp_buf, con->physical.path); - PATHNAME_APPEND_SLASH(p->tmp_buf); - buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("README.txt")); - - if (-1 != stream_open(&s, p->tmp_buf)) { - buffer_append_string_len(out, CONST_STR_LEN("<pre class=\"readme\">")); - buffer_append_string_encoded(out, s.start, s.size, ENCODING_MINIMAL_XML); - buffer_append_string_len(out, CONST_STR_LEN("</pre>")); - } - stream_close(&s); - } - - buffer_append_string_len(out, CONST_STR_LEN( - "<div class=\"foot\">" - )); - - if (p->conf.set_footer->used > 1) { - buffer_append_string_buffer(out, p->conf.set_footer); - } else if (buffer_is_empty(con->conf.server_tag)) { - buffer_append_string_len(out, CONST_STR_LEN(PACKAGE_NAME "/" PACKAGE_VERSION)); - } else { - buffer_append_string_buffer(out, con->conf.server_tag); - } - - buffer_append_string_len(out, CONST_STR_LEN( - "</div>\n" - "</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; - 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")]; - size_t k; - const char *content_type; - long name_max; - -#ifdef HAVE_XATTR - char attrval[128]; - int attrlen; -#endif -#ifdef HAVE_LOCALTIME_R - struct tm tm; -#endif - - /* empty pathname, never ... */ - if (buffer_is_empty(dir)) return -1; - - /* max-length for the opendir */ -#ifdef HAVE_PATHCONF - if (-1 == (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) { -#ifdef NAME_MAX - name_max = NAME_MAX; -#else - name_max = 256; /* stupid default */ -#endif - } -#elif defined _WIN32 - name_max = FILENAME_MAX; -#else - name_max = NAME_MAX; -#endif - - buffer_copy_string_buffer(p->path, dir); - PATHNAME_APPEND_SLASH(p->path); - -#ifdef _WIN32 - /* append *.* to the path */ - buffer_append_string_len(p->path, CONST_STR_LEN("*.*")); -#endif - - if (NULL == (dp = opendir(p->path->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "opendir failed:", dir, strerror(errno)); - - return -1; - } - - dirs.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); - assert(dirs.ent); - dirs.size = DIRLIST_BLOB_SIZE; - dirs.used = 0; - files.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); - assert(files.ent); - files.size = DIRLIST_BLOB_SIZE; - files.used = 0; - - while ((dent = readdir(dp)) != NULL) { - unsigned short exclude_match = 0; - - 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) { - if (strcmp(dent->d_name, "README.txt") == 0) - continue; - } - if (p->conf.hide_header_file) { - if (strcmp(dent->d_name, "HEADER.txt") == 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); - - return -1; - } - } - 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; - - /* build the dirname */ - buffer_copy_string_buffer(p->path, dir); - PATHNAME_APPEND_SLASH(p->path); - buffer_append_string(p->path, dent->d_name); - - if (stat(p->path->ptr, &st) != 0) { - fprintf(stderr, "%s.%d: %s, %s\r\n", __FILE__, __LINE__, p->path->ptr, strerror(errno)); - 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); - 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_get_append_buffer(con->send); - buffer_copy_string_len(out, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"")); - if (buffer_is_empty(p->conf.encoding)) { - buffer_append_string_len(out, CONST_STR_LEN("iso-8859-1")); - } else { - buffer_append_string_buffer(out, p->conf.encoding); - } - buffer_append_string_len(out, CONST_STR_LEN("\"?>\n")); - 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><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; - -#ifdef HAVE_XATTR - if (con->conf.use_xattr) { - /* build the dirname */ - buffer_copy_string_buffer(p->path, dir); - PATHNAME_APPEND_SLASH(p->path); - buffer_append_string_len(p->path, DIRLIST_ENT_NAME(tmp), tmp->namelen); - - attrlen = sizeof(attrval) - 1; - if (attr_get(p->path->ptr, "Content-Type", attrval, &attrlen, 0) == 0) { - attrval[attrlen] = '\0'; - content_type = attrval; - } - } -#endif - - if (content_type == NULL) { - content_type = "application/octet-stream"; - for (k = 0; k < con->conf.mimetypes->used; k++) { - data_string *ds = (data_string *)con->conf.mimetypes->data[k]; - size_t ct_len; - - if (ds->key->used == 0) - continue; - - ct_len = ds->key->used - 1; - if (tmp->namelen < ct_len) - continue; - - if (0 == strncasecmp(DIRLIST_ENT_NAME(tmp) + tmp->namelen - ct_len, ds->key->ptr, ct_len)) { - content_type = ds->value->ptr; - break; - } - } - } - -#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, 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); - - http_list_directory_footer(srv, con, p, out); - - /* Insert possible charset to Content-Type */ - if (buffer_is_empty(p->conf.encoding)) { - response_header_insert(srv, con, 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); - response_header_insert(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->content_charset)); - } - - con->send->bytes_in += out->used - 1; - - con->send->is_closed = 1; - - return 0; -} - - - -URIHANDLER_FUNC(mod_dirlisting_subrequest) { - plugin_data *p = p_d; - stat_cache_entry *sce = NULL; - buffer *mtime; - data_string *ds; - - /* 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; - } - - if (con->uri.path->used < 2) return HANDLER_GO_ON; - if (con->uri.path->ptr[con->uri.path->used - 2] != '/') return HANDLER_GO_ON; - if (con->physical.path->used == 0) return HANDLER_GO_ON; - - mod_dirlisting_patch_connection(srv, con, p); - - if (!p->conf.dir_listing) return HANDLER_GO_ON; - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { - /* just a second ago the file was still there */ - return HANDLER_GO_ON; - } - - if (!S_ISDIR(sce->st.st_mode)) 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); - } - - /* perhaps this a cachable request - * - we use the etag of the directory - * */ - - etag_mutate(con->physical.etag, sce->etag); - response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); - - /* prepare header */ - if (NULL == (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Last-Modified")))) { - mtime = strftime_cache_get(srv, sce->st.st_mtime); - response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); - } else { - mtime = ds->value; - } - - if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { - return HANDLER_FINISHED; - } - - 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 */ - -LI_EXPORT int mod_dirlisting_plugin_init(plugin *p); -LI_EXPORT 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_start_backend = mod_dirlisting_subrequest; - p->set_defaults = mod_dirlisting_set_defaults; - p->cleanup = mod_dirlisting_free; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_evasive.c b/src/mod_evasive.c deleted file mode 100644 index 1c0e5586..00000000 --- a/src/mod_evasive.c +++ /dev/null @@ -1,200 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#include "inet_ntop_cache.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; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -INIT_FUNC(mod_evasive_init) { - plugin_data *p; - - UNUSED(srv); - - 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]; - - 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 }, - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - s->max_conns = 0; - - cv[0].destination = &(s->max_conns); - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -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_OPTION(max_conns); - - /* 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_OPTION(max_conns); - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_evasive_uri_handler) { - plugin_data *p = p_d; - size_t conns_by_ip = 0; - size_t j; - - if (con->uri.path->used == 0) 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; - - switch (con->dst_addr.plain.sa_family) { - case AF_INET: -#ifdef HAVE_IPV6 - case AF_INET6: -#endif - break; - default: // Address family not supported - 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->dst_addr.plain.sa_family != con->dst_addr.plain.sa_family) continue; - if (c->state <= CON_STATE_HANDLE_REQUEST_HEADER) continue; - - switch (con->dst_addr.plain.sa_family) { - case AF_INET: - if (c->dst_addr.ipv4.sin_addr.s_addr != con->dst_addr.ipv4.sin_addr.s_addr) continue; - break; -#ifdef HAVE_IPV6 - case AF_INET6: - if (0 != memcmp(c->dst_addr.ipv6.sin6_addr.s6_addr, con->dst_addr.ipv6.sin6_addr.s6_addr, 16)) continue; - break; -#endif - default: // Address family not supported, should never be reached - continue; - }; - conns_by_ip++; - - if (conns_by_ip > p->conf.max_conns) { - log_error_write(srv, __FILE__, __LINE__, "ss", - inet_ntop_cache_get_ip(srv, &(con->dst_addr)), - "turned away. Too many connections."); - - con->http_status = 403; - return HANDLER_FINISHED; - } - } - - return HANDLER_GO_ON; -} - - -LI_EXPORT int mod_evasive_plugin_init(plugin *p); -LI_EXPORT 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/src/mod_evhost.c b/src/mod_evhost.c deleted file mode 100644 index 8b8d016e..00000000 --- a/src/mod_evhost.c +++ /dev/null @@ -1,347 +0,0 @@ -#include <string.h> -#include <errno.h> -#include <ctype.h> - -#include "plugin.h" -#include "log.h" -#include "response.h" -#include "stat_cache.h" - -#include "sys-files.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; - - UNUSED(srv); - - 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 (!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 void 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 == '%') { - 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(); - - buffer_copy_string_len(s->path_pieces[s->len],pos,ptr-pos); - pos = ptr + 2; - - buffer_copy_string_len(s->path_pieces[s->len+1],ptr++,2); - - 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; - } -} - -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(specific_config *)); - - for (i = 0; i < srv->config_context->used; 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, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - - if (s->path_pieces_raw->used != 0) { - mod_evhost_parse_pattern(s); - } - } - - 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 int mod_evhost_parse_host(connection *con,array *host) { - /* con->uri.authority->used is always > 0 if we come here */ - register char *ptr = con->uri.authority->ptr + con->uri.authority->used - 1; - char *colon = ptr; /* needed to filter out the colon (if exists) */ - int first = 1; - data_string *ds; - int i; - - /* first, find the domain + tld */ - for(;ptr > con->uri.authority->ptr;ptr--) { - if(*ptr == '.') { - if(first) first = 0; - else break; - } else if(*ptr == ':') { - colon = ptr; - first = 1; - } - } - - ds = data_string_init(); - buffer_copy_string_len(ds->key,CONST_STR_LEN("%0")); - - /* if we stopped at a dot, skip the dot */ - if (*ptr == '.') ptr++; - buffer_copy_string_len(ds->value, ptr, colon-ptr); - - array_insert_unique(host,(data_unset *)ds); - - /* if the : is not the start of the authority, go on parsing the hostname */ - - if (colon != con->uri.authority->ptr) { - for(ptr = colon - 1, i = 1; ptr > con->uri.authority->ptr; ptr--) { - if(*ptr == '.') { - if (ptr != colon - 1) { - /* is something between the dots */ - ds = data_string_init(); - buffer_copy_string_len(ds->key,CONST_STR_LEN("%")); - buffer_append_long(ds->key, i++); - buffer_copy_string_len(ds->value,ptr+1,colon-ptr-1); - - array_insert_unique(host,(data_unset *)ds); - } - colon = ptr; - } - } - - /* if the . is not the first charactor of the hostname */ - if (colon != ptr) { - ds = data_string_init(); - buffer_copy_string_len(ds->key,CONST_STR_LEN("%")); - buffer_append_long(ds->key, i++); - buffer_copy_string_len(ds->value,ptr,colon-ptr); - - array_insert_unique(host,(data_unset *)ds); - } - } - - return 0; -} - -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_OPTION(path_pieces); - PATCH_OPTION(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_OPTION(path_pieces); - PATCH_OPTION(len); - } - } - } - - return 0; -} - -static handler_t mod_evhost_uri_handler(server *srv, connection *con, void *p_d) { - plugin_data *p = p_d; - size_t i; - array *parsed_host; - register char *ptr; - int not_good = 0; - stat_cache_entry *sce = NULL; - - /* not authority set */ - if (con->uri.authority->used == 0) 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; - } - - parsed_host = array_init(); - - mod_evhost_parse_host(con, parsed_host); - - /* build document-root */ - buffer_reset(p->tmp_buf); - - for (i = 0; i < p->conf.len; i++) { - ptr = p->conf.path_pieces[i]->ptr; - if (*ptr == '%') { - data_string *ds; - - if (*(ptr+1) == '%') { - /* %% */ - buffer_append_string_len(p->tmp_buf,CONST_STR_LEN("%")); - } else if (*(ptr+1) == '_' ) { - /* %_ == full hostname */ - char *colon = strchr(con->uri.authority->ptr, ':'); - - if(colon == NULL) { - buffer_append_string_buffer(p->tmp_buf, con->uri.authority); /* adds fqdn */ - } else { - /* strip the port out of the authority-part of the URI scheme */ - buffer_append_string_len(p->tmp_buf, con->uri.authority->ptr, colon - con->uri.authority->ptr); /* adds fqdn */ - } - } else if (NULL != (ds = (data_string *)array_get_element(parsed_host, CONST_BUF_LEN(p->conf.path_pieces[i])))) { - if (ds->value->used) { - buffer_append_string_buffer(p->tmp_buf,ds->value); - } - } else { - /* unhandled %-sequence */ - } - } else { - buffer_append_string_buffer(p->tmp_buf,p->conf.path_pieces[i]); - } - } - - PATHNAME_APPEND_SLASH(p->tmp_buf); - - array_free(parsed_host); - - 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); - not_good = 1; - } else if(!S_ISDIR(sce->st.st_mode)) { - log_error_write(srv, __FILE__, __LINE__, "sb", "not a directory:", p->tmp_buf); - not_good = 1; - } - - if (!not_good) { - buffer_copy_string_buffer(con->physical.doc_root, p->tmp_buf); - } - - return HANDLER_GO_ON; -} - -LI_EXPORT int mod_evhost_plugin_init(plugin *p); -LI_EXPORT 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/src/mod_expire.c b/src/mod_expire.c deleted file mode 100644 index c844deeb..00000000 --- a/src/mod_expire.c +++ /dev/null @@ -1,375 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" -#include "response.h" - -#include "plugin.h" -#include "stat_cache.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; -} 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; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->expire_tstmp = buffer_init(); - - buffer_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 (!s) continue; - - array_free(s->expire_url); - 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 (expire->used == 0) { - 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 */ - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - if (!p) return HANDLER_ERROR; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - s->expire_url = array_init(); - - cv[0].destination = s->expire_url; - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - 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; - } - } - } - - - return HANDLER_GO_ON; -} - -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_OPTION(expire_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("expire.url"))) { - PATCH_OPTION(expire_url); - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_expire_path_handler) { - plugin_data *p = p_d; - int s_len; - size_t k; - - if (con->uri.path->used == 0) return HANDLER_GO_ON; - - mod_expire_patch_connection(srv, con, p); - - s_len = con->uri.path->used - 1; - - for (k = 0; k < p->conf.expire_url->used; k++) { - data_string *ds = (data_string *)p->conf.expire_url->data[k]; - int ct_len = ds->key->used - 1; - - if (ct_len > s_len) continue; - if (ds->key->used == 0) continue; - - if (0 == strncmp(con->uri.path->ptr, ds->key->ptr, ct_len)) { - time_t ts, expires; - size_t len; - - switch(mod_expire_get_offset(srv, p, ds->value, &ts)) { - case 0: - /* access */ - expires = (ts + srv->cur_ts); - break; - case 1: { - /* modification */ - stat_cache_entry *sce = NULL; - - if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; - - if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { - /* it failed for some reason, just skip it */ - return HANDLER_GO_ON; - } - - expires = (ts + sce->st.st_mtime); - - break; } - default: - /* -1 is handled at parse-time */ - break; - } - - /* expires should be at least srv->cur_ts */ - if (expires < srv->cur_ts) expires = srv->cur_ts; - - if (0 == (len = strftime(p->expire_tstmp->ptr, p->expire_tstmp->size - 1, - "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(expires))))) { - /* could not set expire header, out of mem */ - - return HANDLER_GO_ON; - } - - p->expire_tstmp->used = len + 1; - - /* HTTP/1.0 */ - response_header_overwrite(srv, con, 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_long(p->expire_tstmp, expires - srv->cur_ts); /* as expires >= srv->cur_ts the difference is >= 0 */ - - response_header_append(srv, con, 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 */ - -LI_EXPORT int mod_expire_plugin_init(plugin *p); -LI_EXPORT 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_header = mod_expire_path_handler; - p->set_defaults = mod_expire_set_defaults; - p->cleanup = mod_expire_free; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_flv_streaming.c b/src/mod_flv_streaming.c deleted file mode 100644 index b527ad58..00000000 --- a/src/mod_flv_streaming.c +++ /dev/null @@ -1,322 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" -#include "response.h" -#include "stat_cache.h" - -#include "plugin.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -/* plugin config for all request/connections */ - -typedef struct { - array *extensions; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - buffer *query_str; - array *get_params; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -/* init the plugin data */ -INIT_FUNC(mod_flv_streaming_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->query_str = buffer_init(); - p->get_params = array_init(); - - return p; -} - -/* detroy the plugin data */ -FREE_FUNC(mod_flv_streaming_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->extensions); - - free(s); - } - free(p->config_storage); - } - - buffer_free(p->query_str); - array_free(p->get_params); - - free(p); - - 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; 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, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -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_OPTION(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_OPTION(extensions); - } - } - } - - return 0; -} - -static int split_get_params(array *get_params, buffer *qrystr) { - size_t is_key = 1; - size_t i; - char *key = NULL, *val = NULL; - - key = qrystr->ptr; - - /* we need the \0 */ - for (i = 0; i < qrystr->used; i++) { - switch(qrystr->ptr[i]) { - case '=': - if (is_key) { - val = qrystr->ptr + i + 1; - - qrystr->ptr[i] = '\0'; - - is_key = 0; - } - - break; - case '&': - case '\0': /* fin symbol */ - if (!is_key) { - data_string *ds; - /* we need at least a = since the last & */ - - /* terminate the value */ - qrystr->ptr[i] = '\0'; - - if (NULL == (ds = (data_string *)array_get_unused_element(get_params, TYPE_STRING))) { - ds = data_string_init(); - } - buffer_copy_string_len(ds->key, key, strlen(key)); - buffer_copy_string_len(ds->value, val, strlen(val)); - - array_insert_unique(get_params, (data_unset *)ds); - } - - key = qrystr->ptr + i + 1; - val = NULL; - is_key = 1; - break; - } - } - - return 0; -} - -/** - * - * @param - */ -URIHANDLER_FUNC(mod_flv_streaming_path_handler) { - plugin_data *p = p_d; - int s_len; - size_t k; - - UNUSED(srv); - - if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; - - if (con->conf.log_request_handling) { - TRACE("-- handling %s in mod_flv_streaming", SAFE_BUF_STR(con->physical.path)); - } - - mod_flv_streaming_patch_connection(srv, con, p); - - s_len = con->physical.path->used - 1; - - for (k = 0; k < p->conf.extensions->used; k++) { - data_string *ds = (data_string *)p->conf.extensions->data[k]; - int ct_len = ds->value->used - 1; - - if (ct_len > s_len) continue; - if (ds->value->used == 0) continue; - - if (0 == strncmp(con->physical.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { - data_string *get_param; - stat_cache_entry *sce = NULL; - buffer *b; - long start; - char *err = NULL; - /* if there is a start=[0-9]+ in the header use it as start, - * otherwise send the full file */ - - array_reset(p->get_params); - buffer_copy_string_buffer(p->query_str, con->uri.query); - split_get_params(p->get_params, p->query_str); - - if (NULL == (get_param = (data_string *)array_get_element(p->get_params, CONST_STR_LEN("start")))) { - if (con->conf.log_request_handling) { - TRACE("start=... not found, skipping %s", SAFE_BUF_STR(con->physical.path)); - } - - return HANDLER_GO_ON; - } - - /* too short */ - if (get_param->value->used < 2) { - if (con->conf.log_request_handling) { - TRACE("start=... found, but empty, skipping %s", SAFE_BUF_STR(con->physical.path)); - } - - return HANDLER_GO_ON; - } - - /* check if it is a number */ - start = strtol(get_param->value->ptr, &err, 10); - if (*err != '\0') { - if (con->conf.log_request_handling) { - TRACE("parsing start '%s' as number failed, skipping %s", - SAFE_BUF_STR(get_param->value), SAFE_BUF_STR(con->physical.path)); - } - - return HANDLER_GO_ON; - } - - if (start <= 0) { - if (con->conf.log_request_handling) { - TRACE("start is <= 0, skipping %s", SAFE_BUF_STR(con->physical.path)); - } - - return HANDLER_GO_ON; - } - - /* check if start is > filesize */ - if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { - if (con->conf.log_request_handling) { - TRACE("stat() for %s failed", SAFE_BUF_STR(con->physical.path)); - } - - return HANDLER_GO_ON; - } - - if (start > sce->st.st_size) { - if (con->conf.log_request_handling) { - TRACE("start > file-size, skipping %s", SAFE_BUF_STR(con->physical.path)); - } - - return HANDLER_GO_ON; - } - - /* we are safe now, let's build a flv header */ - b = chunkqueue_get_append_buffer(con->send); - buffer_copy_string_len(b, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9")); - - chunkqueue_append_file(con->send, con->physical.path, start, sce->st.st_size - start); - - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv")); - - con->send->is_closed = 1; - - if (con->conf.log_request_handling) { - TRACE("sending %s from position %ld", SAFE_BUF_STR(con->physical.path), start); - } - - return HANDLER_FINISHED; - } - } - - if (con->conf.log_request_handling) { - TRACE("none of the extensions matched %s, leaving", SAFE_BUF_STR(con->physical.path)); - } - - /* not found */ - return HANDLER_GO_ON; -} - -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_flv_streaming_plugin_init(plugin *p); -LI_EXPORT 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/src/mod_indexfile.c b/src/mod_indexfile.c deleted file mode 100644 index d48ee03f..00000000 --- a/src/mod_indexfile.c +++ /dev/null @@ -1,254 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#include "configfile.h" - -#include "stat_cache.h" - -#include "sys-strings.h" -#include "sys-files.h" - -#include "configfile.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; - - UNUSED(srv); - - 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 (!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(specific_config *)); - - for (i = 0; i < srv->config_context->used; 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, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -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_OPTION(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_OPTION(indexfiles); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("index-file.names"))) { - PATCH_OPTION(indexfiles); - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_indexfile_subrequest) { - plugin_data *p = p_d; - size_t k; - stat_cache_entry *sce = NULL; - - if (con->uri.path->used == 0) return HANDLER_GO_ON; - if (con->uri.path->ptr[con->uri.path->used - 2] != '/') return HANDLER_GO_ON; - - mod_indexfile_patch_connection(srv, con, p); - - /* is the physical-path really a dir ? */ - switch(stat_cache_get_entry(srv, con, con->physical.path, &sce)) { - case HANDLER_WAIT_FOR_EVENT: - return HANDLER_WAIT_FOR_EVENT; - case HANDLER_ERROR: - return HANDLER_GO_ON; - default: - break; - } - - if (!S_ISDIR(sce->st.st_mode)) { - return HANDLER_GO_ON; - } - - if (con->conf.log_request_handling) { - TRACE("-- %s", "handling the request as Indexfile"); - TRACE("URI : %s", SAFE_BUF_STR(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_string_buffer(p->tmp_buf, con->physical.doc_root); - } else { - buffer_copy_string_buffer(p->tmp_buf, con->physical.path); - PATHNAME_APPEND_SLASH(p->tmp_buf); - } - buffer_append_string_buffer(p->tmp_buf, ds->value); - - switch(stat_cache_get_entry(srv, con, p->tmp_buf, &sce)) { - case HANDLER_WAIT_FOR_EVENT: - return HANDLER_WAIT_FOR_EVENT; - case HANDLER_ERROR: - 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; - default: - break; - } - - /* rewrite uri.path to the real path (/ -> /index.php) */ - buffer_append_string_buffer(con->uri.path, ds->value); - buffer_copy_string_buffer(con->physical.path, p->tmp_buf); - - if (con->conf.log_request_handling) { - TRACE("rewrite path to %s (%s)", - SAFE_BUF_STR(con->physical.path), - SAFE_BUF_STR(con->uri.path)); - } - - /* need to reset condition cache since uri.path changed. */ - config_cond_cache_reset_item(srv, con, COMP_PHYSICAL_PATH); - config_cond_cache_reset_item(srv, con, COMP_PHYSICAL_PATH_EXISTS); - config_cond_cache_reset_item(srv, con, COMP_HTTP_URL); - - return HANDLER_GO_ON; - } - - /* not found */ - return HANDLER_GO_ON; -} - -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_indexfile_plugin_init(plugin *p); -LI_EXPORT 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_start_backend = mod_indexfile_subrequest; - p->set_defaults = mod_indexfile_set_defaults; - p->cleanup = mod_indexfile_free; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_magnet.c b/src/mod_magnet.c deleted file mode 100644 index d1260396..00000000 --- a/src/mod_magnet.c +++ /dev/null @@ -1,984 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <setjmp.h> - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#include "mod_magnet_cache.h" -#include "response.h" -#include "stat_cache.h" -#include "status_counter.h" -#include "etag.h" -#include "configfile.h" - -#ifdef HAVE_LUA_H -#include <lua.h> -#include <lauxlib.h> - -#define PLUGIN_NAME "magnet" - -#define MAGNET_CONFIG_RAW_URL PLUGIN_NAME ".attract-raw-url-to" -#define MAGNET_CONFIG_PHYSICAL_PATH PLUGIN_NAME ".attract-physical-path-to" -#define MAGNET_CONFIG_FILTER_CONTENT PLUGIN_NAME ".attract-response-content-to" -#define MAGNET_CONFIG_FILTER_HEADER PLUGIN_NAME ".attract-response-header-to" -#define MAGNET_RESTART_REQUEST 99 - -/* plugin config for all request/connections */ - -static jmp_buf exceptionjmp; - -typedef struct { - array *url_raw; - array *physical_path; - array *filter_header; - array *filter_content; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - script_cache *cache; - - buffer *encode_buf; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -/* init the plugin data */ -INIT_FUNC(mod_magnet_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->cache = script_cache_init(); - p->encode_buf = buffer_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 (!s) continue; - - array_free(s->url_raw); - array_free(s->physical_path); - array_free(s->filter_header); - array_free(s->filter_content); - - free(s); - } - free(p->config_storage); - } - - script_cache_free(p->cache); - buffer_free(p->encode_buf); - - 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 */ - { MAGNET_CONFIG_FILTER_CONTENT, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { MAGNET_CONFIG_FILTER_HEADER, 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - s->url_raw = array_init(); - s->physical_path = array_init(); - s->filter_content = array_init(); - s->filter_header = array_init(); - - cv[0].destination = s->url_raw; - cv[1].destination = s->physical_path; - cv[2].destination = s->filter_content; - cv[3].destination = s->filter_header; - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - 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); - PATCH(filter_header); - PATCH(filter_content); - - /* 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); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(MAGNET_CONFIG_FILTER_CONTENT))) { - PATCH(filter_content); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(MAGNET_CONFIG_FILTER_HEADER))) { - PATCH(filter_header); - } - } - } - - return 0; -} -#undef PATCH - -static int magnet_print(lua_State *L) { - const char *s = luaL_checkstring(L, 1); - server *srv; - - lua_pushstring(L, "lighty.srv"); - lua_gettable(L, LUA_REGISTRYINDEX); - srv = lua_touserdata(L, -1); - lua_pop(L, 1); - - log_error_write(srv, __FILE__, __LINE__, "ss", - "(lua-print)", s); - - return 0; -} - -static int magnet_stat(lua_State *L) { - const char *s = luaL_checkstring(L, 1); - server *srv; - connection *con; - buffer sb; - stat_cache_entry *sce = NULL; - - lua_pushstring(L, "lighty.srv"); - lua_gettable(L, LUA_REGISTRYINDEX); - srv = lua_touserdata(L, -1); - lua_pop(L, 1); - - lua_pushstring(L, "lighty.con"); - lua_gettable(L, LUA_REGISTRYINDEX); - con = lua_touserdata(L, -1); - lua_pop(L, 1); - - sb.ptr = (char *)s; - sb.used = sb.size = strlen(s) + 1; - - if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, &sb, &sce)) { - lua_pushnil(L); - - return 1; - } - - lua_newtable(L); - - 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_is_empty(sce->etag)) { - /* we have to mutate the etag */ - buffer *b = buffer_init(); - etag_mutate(b, sce->etag); - - lua_pushlstring(L, b->ptr, b->used - 1); - buffer_free(b); - } else { - lua_pushnil(L); - } - lua_setfield(L, -2, "etag"); - - if (!buffer_is_empty(sce->content_type)) { - lua_pushlstring(L, sce->content_type->ptr, sce->content_type->used - 1); - } else { - lua_pushnil(L); - } - lua_setfield(L, -2, "content-type"); - - return 1; -} - - -static int magnet_atpanic(lua_State *L) { - const char *s = luaL_checkstring(L, 1); - server *srv; - - lua_pushstring(L, "lighty.srv"); - lua_gettable(L, LUA_REGISTRYINDEX); - srv = lua_touserdata(L, -1); - lua_pop(L, 1); - - log_error_write(srv, __FILE__, __LINE__, "ss", - "(lua-atpanic)", s); - - longjmp(exceptionjmp, 1); -} - -static int magnet_reqhdr_get(lua_State *L) { - server *srv; - connection *con; - data_string *ds; - - const char *key = luaL_checkstring(L, 2); - - lua_pushstring(L, "lighty.srv"); - lua_gettable(L, LUA_REGISTRYINDEX); - srv = lua_touserdata(L, -1); - lua_pop(L, 1); - - lua_pushstring(L, "lighty.con"); - lua_gettable(L, LUA_REGISTRYINDEX); - con = lua_touserdata(L, -1); - lua_pop(L, 1); - - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, key, strlen(key)))) { - if (ds->value->used) { - lua_pushlstring(L, ds->value->ptr, ds->value->used - 1); - } else { - lua_pushnil(L); - } - } else { - lua_pushnil(L); - } - return 1; -} - -static int magnet_status_get(lua_State *L) { - data_integer *di; - server *srv; - size_t key_len = 0; - - const char *key = luaL_checklstring(L, 2, &key_len); - - lua_pushstring(L, "lighty.srv"); - lua_gettable(L, LUA_REGISTRYINDEX); - srv = lua_touserdata(L, -1); - lua_pop(L, 1); - - di = status_counter_get_counter(key, key_len); - - lua_pushnumber(L, (double)di->value); - - return 1; -} - -static int magnet_status_set(lua_State *L) { - size_t key_len = 0; - server *srv; - - const char *key = luaL_checklstring(L, 2, &key_len); - int counter = luaL_checkint(L, 3); - - lua_pushstring(L, "lighty.srv"); - lua_gettable(L, LUA_REGISTRYINDEX); - srv = lua_touserdata(L, -1); - lua_pop(L, 1); - - status_counter_set(key, key_len, counter); - - return 0; -} - -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_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_PROTOCOL - } type; -} magnet_env_t; - -static buffer *magnet_env_get_buffer(server *srv, connection *con, const char *key) { - buffer *dest = NULL; - size_t i; - - const magnet_env_t env[] = { - { "physical.path", MAGNET_ENV_PHYICAL_PATH }, - { "physical.rel-path", MAGNET_ENV_PHYICAL_REL_PATH }, - { "physical.doc-root", MAGNET_ENV_PHYICAL_DOC_ROOT }, - - { "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.protocol", MAGNET_ENV_REQUEST_PROTOCOL }, - - { NULL, MAGNET_ENV_UNSET } - }; - - UNUSED(srv); - - /** - * map all internal variables to lua - * - */ - - for (i = 0; env[i].name; i++) { - if (0 == strcmp(key, env[i].name)) break; - } - - switch (env[i].type) { - 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_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_copy_string(srv->tmp_buf, get_http_method_name(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_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 int magnet_env_get(lua_State *L) { - server *srv; - connection *con; - - const char *key = luaL_checkstring(L, 2); - buffer *dest = NULL; - - lua_pushstring(L, "lighty.srv"); - lua_gettable(L, LUA_REGISTRYINDEX); - srv = lua_touserdata(L, -1); - lua_pop(L, 1); - - lua_pushstring(L, "lighty.con"); - lua_gettable(L, LUA_REGISTRYINDEX); - con = lua_touserdata(L, -1); - lua_pop(L, 1); - - dest = magnet_env_get_buffer(srv, con, key); - - if (dest && dest->used) { - lua_pushlstring(L, dest->ptr, dest->used - 1); - } else { - lua_pushnil(L); - } - - return 1; -} - -static int magnet_env_set(lua_State *L) { - server *srv; - connection *con; - - const char *key = luaL_checkstring(L, 2); - const char *val = luaL_checkstring(L, 3); - buffer *dest = NULL; - - lua_pushstring(L, "lighty.srv"); - lua_gettable(L, LUA_REGISTRYINDEX); - srv = lua_touserdata(L, -1); - lua_pop(L, 1); - - lua_pushstring(L, "lighty.con"); - lua_gettable(L, LUA_REGISTRYINDEX); - con = lua_touserdata(L, -1); - lua_pop(L, 1); - - if (NULL != (dest = magnet_env_get_buffer(srv, con, key))) { - buffer_copy_string(dest, val); - } else { - /* couldn't save */ - - return luaL_error(L, "couldn't store '%s' in lighty.env[]", key); - } - - return 0; -} - - -static int magnet_cgi_get(lua_State *L) { - connection *con; - data_string *ds; - - const char *key = luaL_checkstring(L, 2); - - lua_pushstring(L, "lighty.con"); - lua_gettable(L, LUA_REGISTRYINDEX); - con = lua_touserdata(L, -1); - lua_pop(L, 1); - - if (NULL != (ds = (data_string *)array_get_element(con->environment, key, strlen(key))) && ds->value->used) - lua_pushlstring(L, CONST_BUF_LEN(ds->value)); - else - lua_pushnil(L); - - return 1; -} - -static int magnet_cgi_set(lua_State *L) { - connection *con; - - const char *key = luaL_checkstring(L, 2); - const char *val = luaL_checkstring(L, 3); - - lua_pushstring(L, "lighty.con"); - lua_gettable(L, LUA_REGISTRYINDEX); - con = lua_touserdata(L, -1); - lua_pop(L, 1); - - array_set_key_value(con->environment, key, strlen(key), val, strlen(val)); - - return 0; -} - - -static int magnet_copy_response_header(server *srv, connection *con, plugin_data *p, lua_State *L) { - UNUSED(p); - /** - * get the environment of the function - */ - - lua_getfenv(L, -1); /* -1 is the function */ - - /* lighty.header */ - - lua_getfield(L, -1, "lighty"); /* lighty.* from the env */ - assert(lua_istable(L, -1)); - - lua_getfield(L, -1, "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 char *key, *val; - size_t key_len, val_len; - - key = lua_tolstring(L, -2, &key_len); - val = lua_tolstring(L, -1, &val_len); - - response_header_overwrite(srv, con, key, key_len, val, val_len); - } - - lua_pop(L, 1); - } - } - - lua_pop(L, 1); /* pop the header-table */ - lua_pop(L, 1); /* pop the lighty-env */ - lua_pop(L, 1); /* pop the function env */ - - 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, plugin_data *p, lua_State *L) { - UNUSED(p); - /** - * get the environment of the function - */ - - assert(lua_isfunction(L, -1)); - lua_getfenv(L, -1); /* -1 is the function */ - - lua_getfield(L, -1, "lighty"); /* lighty.* from the env */ - assert(lua_istable(L, -1)); - - lua_getfield(L, -1, "content"); /* lighty.content */ - if (lua_istable(L, -1)) { - int i; - /* header 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)) { - size_t s_len = 0; - const char *s = lua_tolstring(L, -1, &s_len); - - chunkqueue_append_mem(con->send, s, s_len); - } else if (lua_istable(L, -1)) { - lua_getfield(L, -1, "filename"); - lua_getfield(L, -2, "length"); - lua_getfield(L, -3, "offset"); - - if (lua_isstring(L, -3)) { /* filename has to be a string */ - buffer *fn = buffer_init(); - stat_cache_entry *sce; - - buffer_copy_string(fn, lua_tostring(L, -3)); - - if (HANDLER_GO_ON == stat_cache_get_entry(srv, con, fn, &sce)) { - off_t off = 0; - off_t len = 0; - - if (lua_isnumber(L, -1)) { - off = lua_tonumber(L, -1); - } - - if (lua_isnumber(L, -2)) { - len = lua_tonumber(L, -2); - } else { - len = sce->st.st_size; - } - - if (off < 0) { - return luaL_error(L, "offset for '%s' is negative", fn->ptr); - } - - if (len < off) { - return luaL_error(L, "offset > length for '%s'", fn->ptr); - } - - chunkqueue_append_file(con->send, fn, off, len - off); - } - - buffer_free(fn); - } else { - lua_pop(L, 3 + 2); /* correct the stack */ - - 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)) { - /* oops, end of list */ - - lua_pop(L, 1); - - break; - } else { - lua_pop(L, 4); - - return luaL_error(L, "content[%d] is neither a string nor a table: ", i); - } - - lua_pop(L, 1); /* pop the content[...] table */ - } - } else { - return luaL_error(L, "lighty.content has to be a table"); - } - lua_pop(L, 1); /* pop the header-table */ - lua_pop(L, 1); /* pop the lighty-table */ - lua_pop(L, 1); /* php the function env */ - - return 0; -} - -static int traceback (lua_State *L) { - if (!lua_isstring(L, 1)) /* 'message' not a string? */ - return 1; /* keep it intact */ - lua_getfield(L, LUA_GLOBALSINDEX, "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; -} - -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 = -1; - int errfunc; - /* 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); - - assert(lua_gettop(L) == 0); /* only the function should be on the stack */ - - con->http_status = 500; - - return HANDLER_FINISHED; - } - - lua_pushstring(L, "lighty.srv"); - lua_pushlightuserdata(L, srv); - lua_settable(L, LUA_REGISTRYINDEX); /* registery[<id>] = srv */ - - lua_pushstring(L, "lighty.con"); - lua_pushlightuserdata(L, con); - lua_settable(L, LUA_REGISTRYINDEX); /* registery[<id>] = con */ - - 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[] has the HTTP-request headers - * lighty.content[] is a table of string/file - * lighty.header[] is an 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_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 request-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_setmetatable(L, -2); /* tie the metatable to request (sp -= 1) */ - lua_setfield(L, -2, "env"); /* content = {} (sp -= 1) */ - - lua_newtable(L); /* {} (sp += 1) */ - lua_newtable(L); /* the meta-table for the request-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_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 request-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_setmetatable(L, -2); /* tie the metatable to request (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) */ - - lua_setfield(L, -2, "lighty"); /* lighty.* (sp -= 1) */ - - lua_newtable(L); /* the meta-table for the new env (sp += 1) */ - lua_pushvalue(L, LUA_GLOBALSINDEX); /* (sp += 1) */ - lua_setfield(L, -2, "__index"); /* { __index = _G } (sp -= 1) */ - lua_setmetatable(L, -2); /* setmetatable({}, {__index = _G}) (sp -= 1) */ - - - lua_setfenv(L, -2); /* on the stack should be a modified env (sp -= 1) */ - - errfunc = push_traceback(L, 0); - if (lua_pcall(L, 0, 1, errfunc)) { - lua_remove(L, errfunc); - log_error_write(srv, __FILE__, __LINE__, - "ss", - "lua_pcall():", - lua_tostring(L, -1)); - lua_pop(L, 1); /* remove the error-msg and the function copy from the stack */ - - assert(lua_gettop(L) == 1); /* only the function should be on the stack */ - - con->http_status = 500; - - return HANDLER_FINISHED; - } - lua_remove(L, errfunc); - - /* we should have the function-copy and the return value on the stack */ - assert(lua_gettop(L) == 2); - - if (lua_isnumber(L, -1)) { - /* if the ret-value is a number, take it */ - lua_return_value = lua_tointeger(L, -1); - } - lua_pop(L, 1); /* pop the ret-value */ - - magnet_copy_response_header(srv, con, p, L); - - if (lua_return_value > 99) { - con->http_status = lua_return_value; - con->send->is_closed = 1; - - /* try { ...*/ - if (0 == setjmp(exceptionjmp)) { - magnet_attach_content(srv, con, p, L); - } else { - /* } catch () { */ - con->http_status = 500; - } - - if (chunkqueue_is_empty(con->send)) { - con->mode = DIRECT; - } else { - con->mode = p->id; - } - - assert(lua_gettop(L) == 1); /* only the function should be on the stack */ - - /* we are finished */ - return HANDLER_FINISHED; - } else if (MAGNET_RESTART_REQUEST == lua_return_value) { - assert(lua_gettop(L) == 1); /* only the function should be on the stack */ - buffer_reset(con->physical.path); - - return HANDLER_COMEBACK; - } else { - assert(lua_gettop(L) == 1); /* only the function should be on the stack */ - - return HANDLER_GO_ON; - } -} - -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; - - /** - * execute all files and jump out on the first !HANDLER_GO_ON - */ - for (i = 0; i < files->used; i++) { - data_string *ds = (data_string *)files->data[i]; - - if (buffer_is_empty(ds->value)) continue; - - ret = magnet_attract(srv, con, p, ds->value); - - if (ret != HANDLER_GO_ON) break; - } - - /* reset conditional cache. */ - config_cond_cache_reset_all_items(srv, con); - - 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); -} - -URIHANDLER_FUNC(mod_magnet_handle_response_header) { - plugin_data *p = p_d; - - mod_magnet_patch_connection(srv, con, p); - - return magnet_attract_array(srv, con, p, p->conf.filter_header); -} - -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_magnet_plugin_init(plugin *p); -LI_EXPORT int mod_magnet_plugin_init(plugin *p) { - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string(PLUGIN_NAME); - - p->init = mod_magnet_init; - p->set_defaults = mod_magnet_set_defaults; - p->cleanup = mod_magnet_free; - - p->handle_uri_clean = mod_magnet_uri_handler; /* match against the uri */ - p->handle_physical = mod_magnet_physical; /* match against the filename */ - - p->handle_response_header = mod_magnet_handle_response_header; -#if 0 - p->handle_filter_response_content = mod_magnet_handle_filter_response_content; -#endif - - p->data = NULL; - - return 0; -} - -#else -LI_EXPORT int mod_magnet_plugin_init(plugin *p); -LI_EXPORT int mod_magnet_plugin_init(plugin *p) { - UNUSED(p); - return -1; -} -#endif diff --git a/src/mod_magnet_cache.c b/src/mod_magnet_cache.c deleted file mode 100644 index 0cd1bcc3..00000000 --- a/src/mod_magnet_cache.c +++ /dev/null @@ -1,137 +0,0 @@ -#include <stdlib.h> -#include <time.h> -#include <assert.h> - -#include "stat_cache.h" -#include "mod_magnet_cache.h" - -#ifdef HAVE_LUA_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; - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, sc->name, &sce)) { - lua_pop(sc->L, 1); /* pop the old function */ - break; - } - - if (!buffer_is_equal(sce->etag, sc->etag)) { - /* the etag is outdated, reload the function */ - lua_pop(sc->L, 1); - break; - } - - assert(lua_isfunction(sc->L, -1)); - lua_pushvalue(sc->L, -1); /* copy the function-reference */ - - 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_string_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_string_buffer(sc->etag, sce->etag); - } - - /** - * pcall() needs the function on the stack - * - * as pcall() will pop the script from the stack when done, we have to - * duplicate it here - */ - assert(lua_isfunction(sc->L, -1)); - lua_pushvalue(sc->L, -1); /* copy the function-reference */ - - return sc->L; -} - -#endif diff --git a/src/mod_magnet_cache.h b/src/mod_magnet_cache.h deleted file mode 100644 index 50c9e44a..00000000 --- a/src/mod_magnet_cache.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef _MOD_MAGNET_CACHE_H_ -#define _MOD_MAGNET_CACHE_H_ - -#include "buffer.h" -#include "base.h" - -#ifdef HAVE_LUA_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 -#endif diff --git a/src/mod_mysql_vhost.c b/src/mod_mysql_vhost.c deleted file mode 100644 index 68b65d52..00000000 --- a/src/mod_mysql_vhost.c +++ /dev/null @@ -1,309 +0,0 @@ -#include <stdio.h> -#include <errno.h> -#include <fcntl.h> -#include <string.h> - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_MYSQL_H -# ifdef HAVE_LIBMYSQL -# define HAVE_MYSQL -# endif -#endif - -#ifdef HAVE_MYSQL -#include <mysql.h> -#endif - -#include "plugin.h" -#include "log.h" - -#include "stat_cache.h" -#include "sys-files.h" - -#include "mod_sql_vhost_core.h" - -#ifdef HAVE_MYSQL - -#define CORE_PLUGIN "mod_sql_vhost_core" - -typedef struct { - MYSQL *mysql; - - buffer *mysql_pre; - buffer *mysql_post; - - mod_sql_vhost_core_plugin_config *core; -} plugin_config; - -/* global plugin data */ -typedef struct { - PLUGIN_DATA; - - buffer *tmp_buf; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -SQLVHOST_BACKEND_GETVHOST(mod_mysql_vhost_get_vhost); - -/* init the plugin data */ -INIT_FUNC(mod_mysql_vhost_init) { - plugin_data *p; - - UNUSED(srv); - - 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_pre); - buffer_free(s->mysql_post); - - free(s); - } - free(p->config_storage); - } - buffer_free(p->tmp_buf); - - free(p); - - return HANDLER_GO_ON; -} - -/* set configuration values */ -SERVER_FUNC(mod_mysql_vhost_set_defaults) { - plugin_data *p = p_d; - mod_sql_vhost_core_plugin_data *core_config; - - size_t i = 0; - - /* our very own plugin storage, one entry for each conditional - * - * srv->config_context->used is the number of conditionals - * */ - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - /* get the config of the core-plugin */ - core_config = plugin_get_config(srv, CORE_PLUGIN); - - /* walk through all conditionals and check for assignments */ - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - buffer *sel; - char *qmark; - - /* get the config from the core plugin for this conditional-context */ - s = calloc(1, sizeof(plugin_config)); - - s->core = core_config->config_storage[i]; - - s->mysql = NULL; - - s->mysql_pre = buffer_init(); - s->mysql_post = buffer_init(); - - p->config_storage[i] = s; - - /* check if we are the plugin for this backend */ - if (!buffer_is_equal_string(s->core->backend, CONST_STR_LEN("mysql"))) continue; - - /* attach us to the core-plugin */ - s->core->backend_data = p; - s->core->get_vhost = mod_mysql_vhost_get_vhost; - - sel = buffer_init(); - buffer_copy_string_buffer(sel, s->core->select_vhost); - - if (sel->used && (qmark = strchr(sel->ptr, '?'))) { - *qmark = '\0'; - buffer_copy_string(s->mysql_pre, sel->ptr); - buffer_copy_string(s->mysql_post, qmark+1); - } else { - buffer_copy_string_buffer(s->mysql_pre, sel); - } - buffer_free(sel); - - /* 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_is_empty(s->core->user) || - buffer_is_empty(s->core->db))) { - - int fd; - - 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 >= 50003 - /* in mysql versions above 5.0.3 the reconnect flag is off by default */ - { - my_bool reconnect = 1; - mysql_options(s->mysql, MYSQL_OPT_RECONNECT, &reconnect); - } -#endif - -#define FOO(x) (s->core->x->used ? s->core->x->ptr : NULL) - - s->mysql->free_me = 1; - - if (!mysql_real_connect(s->mysql, FOO(hostname), FOO(user), FOO(pass), - FOO(db), s->core->port, FOO(sock), 0)) { - log_error_write(srv, __FILE__, __LINE__, "s", mysql_error(s->mysql)); - - return HANDLER_ERROR; - } -#undef FOO - /* set close_on_exec for mysql the hard way */ - /* Note: this only works as it is done during startup, */ - /* otherwise we cannot be sure that mysql is fd i-1 */ - if (-1 == (fd = open("/dev/null", 0))) { - close(fd); - fcntl(fd-1, F_SETFD, FD_CLOEXEC); - } - } - } - - - - return HANDLER_GO_ON; -} - -static int mod_mysql_vhost_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i; - plugin_config *s = p->config_storage[0]; - - PATCH_OPTION(mysql_pre); - PATCH_OPTION(mysql_post); - PATCH_OPTION(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; - - if (s->mysql) { - PATCH_OPTION(mysql); - PATCH_OPTION(mysql_pre); - PATCH_OPTION(mysql_post); - } - } - - return 0; -} - -/** - * get the vhost info from the database - */ -SQLVHOST_BACKEND_GETVHOST(mod_mysql_vhost_get_vhost) { - plugin_data *p = p_d; - unsigned cols; - MYSQL_ROW row; - MYSQL_RES *result = NULL; - - UNUSED(host); - - /* no host specified? */ - if (!con->uri.authority->used) return HANDLER_ERROR; - - mod_mysql_vhost_patch_connection(srv, con, p); - - if (!p->conf.mysql) return HANDLER_ERROR; - - /* build and run SQL query */ - buffer_copy_string_buffer(p->tmp_buf, p->conf.mysql_pre); - if (p->conf.mysql_post->used) { - buffer_append_string_buffer(p->tmp_buf, con->uri.authority); - buffer_append_string_buffer(p->tmp_buf, p->conf.mysql_post); - } - if (mysql_query(p->conf.mysql, BUF_STR(p->tmp_buf))) { - ERROR("mysql_query(%s) failed: %s", SAFE_BUF_STR(p->tmp_buf), mysql_error(p->conf.mysql)); - - mysql_free_result(result); - return HANDLER_ERROR; - } - 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); - return HANDLER_ERROR; - } - - buffer_copy_string(docroot, row[0]); - - mysql_free_result(result); - - return HANDLER_GO_ON; -} - -/* this function is called at dlopen() time and inits the callbacks */ -LI_EXPORT int mod_mysql_vhost_plugin_init(plugin *p); -LI_EXPORT int mod_mysql_vhost_plugin_init(plugin *p) { - data_string *ds; - - 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->set_defaults = mod_mysql_vhost_set_defaults; - - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN(CORE_PLUGIN)); - array_insert_unique(p->required_plugins, (data_unset *)ds); - - return 0; -} -#else -/* we don't have mysql support, this plugin does nothing */ -LI_EXPORT int mod_mysql_vhost_plugin_init(plugin *p); -LI_EXPORT int mod_mysql_vhost_plugin_init(plugin *p) { - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("mysql_vhost"); - - return 0; -} -#endif diff --git a/src/mod_postgresql_vhost.c b/src/mod_postgresql_vhost.c deleted file mode 100644 index 2e0f4a7f..00000000 --- a/src/mod_postgresql_vhost.c +++ /dev/null @@ -1,371 +0,0 @@ -#include <stdio.h> -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdbool.h> -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_LIBPQ_FE_H -# ifdef HAVE_LIBPQ -# define HAVE_POSTGRESQL -# endif -#endif - -#ifdef HAVE_POSTGRESQL -#include <glib.h> -#include <libpq-fe.h> -#include <time.h> -#endif - -#include "plugin.h" -#include "log.h" - -#include "stat_cache.h" -#include "sys-files.h" - -#include "mod_sql_vhost_core.h" - -#ifdef HAVE_POSTGRESQL -// Define a couple constants so the cache expires and we -// do a countdown for when to do some cleanup. - -typedef struct { - PGconn **pconn; - PGconn *conn; - - buffer *postgresql_pre; - buffer *postgresql_post; - - buffer *conninfo; - - mod_sql_vhost_core_plugin_config *core; -} plugin_config; - -/* global plugin data */ -typedef struct { - PLUGIN_DATA; - - buffer *tmp_buf; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -#define CORE_PLUGIN "mod_sql_vhost_core" - -SQLVHOST_BACKEND_GETVHOST(mod_postgresql_vhost_get_vhost); - -/* init the plugin data */ -INIT_FUNC(mod_postgresql_vhost_init) { - plugin_data *p; - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->tmp_buf = buffer_init(); - - return p; -} - -/* cleanup the plugin data */ -SERVER_FUNC(mod_postgresql_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; - - PQfinish(s->conn); - buffer_free(s->postgresql_pre); - buffer_free(s->postgresql_post); - buffer_free(s->conninfo); - - free(s); - } - free(p->config_storage); - } - buffer_free(p->tmp_buf); - - free(p); - - return HANDLER_GO_ON; -} - - -/* set configuration values */ -SERVER_FUNC(mod_postgresql_vhost_set_defaults) { - plugin_data *p = p_d; - mod_sql_vhost_core_plugin_data *core_config; - - size_t i = 0; - - /* our very own plugin storage, one entry for each conditional - * - * srv->config_context->used is the number of conditionals - * */ - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - /* get the config of the core-plugin */ - core_config = plugin_get_config(srv, CORE_PLUGIN); - - /* walk through all conditionals and check for assignments */ - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - buffer *sel; - char *qmark; - - /* get the config from the core plugin for this conditional-context */ - s = calloc(1, sizeof(plugin_config)); - - s->core = core_config->config_storage[i]; - s->conn = NULL; - s->pconn = &(s->conn); - - s->postgresql_pre = buffer_init(); - s->postgresql_post = buffer_init(); - s->conninfo = buffer_init(); - - p->config_storage[i] = s; - - /* check if we are the plugin for this backend */ - if (!buffer_is_equal_string(s->core->backend, CONST_STR_LEN("postgresql"))) continue; - - /* attach us to the core-plugin */ - s->core->backend_data = p; - s->core->get_vhost = mod_postgresql_vhost_get_vhost; - - sel = buffer_init(); - buffer_copy_string_buffer(sel, s->core->select_vhost); - - if (sel->used && (qmark = strchr(sel->ptr, '?'))) { - *qmark = '\0'; - buffer_copy_string(s->postgresql_pre, sel->ptr); - buffer_copy_string(s->postgresql_post, qmark+1); - } else { - buffer_copy_string_buffer(s->postgresql_pre, sel); - } - buffer_free(sel); - - /* see if we can build our connection string based on the parts we know from the config - * - * "host=/tmp dbname=lighttpd user=lighttpd" - * */ - - if (!buffer_is_empty(s->core->hostname)) { - buffer_append_string_len(s->conninfo, CONST_STR_LEN("host=")); - buffer_append_string_buffer(s->conninfo, s->core->hostname); - if (s->core->port) { - buffer_append_string_len(s->conninfo, CONST_STR_LEN(" ")); - buffer_append_string_len(s->conninfo, CONST_STR_LEN("port=")); - buffer_append_long(s->conninfo, s->core->port); - } - } else if (!buffer_is_empty(s->core->sock)) { - buffer_append_string_len(s->conninfo, CONST_STR_LEN("host=")); - buffer_append_string_buffer(s->conninfo, s->core->sock); - } - - if (!buffer_is_empty(s->core->db)) { - if (!buffer_is_empty(s->conninfo)) buffer_append_string_len(s->conninfo, CONST_STR_LEN(" ")); - buffer_append_string_len(s->conninfo, CONST_STR_LEN("dbname=")); - buffer_append_string_buffer(s->conninfo, s->core->db); - } - - if (!buffer_is_empty(s->core->user)) { - if (!buffer_is_empty(s->conninfo)) buffer_append_string_len(s->conninfo, CONST_STR_LEN(" ")); - buffer_append_string_len(s->conninfo, CONST_STR_LEN("user=")); - buffer_append_string_buffer(s->conninfo, s->core->user); - } - - if (!buffer_is_empty(s->core->pass)) { - if (!buffer_is_empty(s->conninfo)) buffer_append_string_len(s->conninfo, CONST_STR_LEN(" ")); - buffer_append_string_len(s->conninfo, CONST_STR_LEN("password=")); - buffer_append_string_buffer(s->conninfo, s->core->pass); - } - - } - - return HANDLER_GO_ON; -} - - -static int mod_postgresql_vhost_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i; - plugin_config *s = p->config_storage[0]; - - PATCH_OPTION(postgresql_pre); - PATCH_OPTION(postgresql_post); - PATCH_OPTION(conn); - PATCH_OPTION(pconn); - PATCH_OPTION(conninfo); - PATCH_OPTION(core); - - /* 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; - - if (!buffer_is_equal_string(s->core->backend, CONST_STR_LEN("postgresql"))) continue; - - PATCH_OPTION(pconn); - PATCH_OPTION(conn); - PATCH_OPTION(conninfo); - PATCH_OPTION(postgresql_pre); - PATCH_OPTION(postgresql_post); - PATCH_OPTION(core); - } - - return 0; -} - -/* - * get the vhost info from the database - */ -SQLVHOST_BACKEND_GETVHOST(mod_postgresql_vhost_get_vhost) { - plugin_data *p = p_d; - int nFields; - PGresult *result; - gchar *field; - - UNUSED(host); - - /* no host specified? */ - if (buffer_is_empty(con->uri.authority)) return HANDLER_ERROR; - - mod_postgresql_vhost_patch_connection(srv, con, p); - - if (buffer_is_empty(p->conf.conninfo)) return HANDLER_ERROR; - - /** - * try to connect the pg-server - */ - if (p->conf.conn == NULL) { - if (p->conf.core->debug) TRACE("connecting to postgres: %s", SAFE_BUF_STR(p->conf.conninfo)); - - if (NULL == (p->conf.conn = PQconnectdb(BUF_STR(p->conf.conninfo)))) { - ERROR("%s", "postgresql malloc failure"); - - return HANDLER_ERROR; - } - - /* we have to update the pointer in the conditional cache too */ - *(p->conf.pconn) = p->conf.conn; - - /* For when we move to Async requests - * PQsetnonblocking(s->conn,1); - */ - - if (CONNECTION_BAD == PQstatus(p->conf.conn)) { - /* Even if bad connection. maybe next time it can be fixed - */ - ERROR("Bad connection for '%s': %s", SAFE_BUF_STR(p->conf.conninfo), PQerrorMessage(p->conf.conn)); - - PQfinish(p->conf.conn); - - p->conf.conn = NULL; - - return HANDLER_ERROR; - } - - if (PQstatus(p->conf.conn) != CONNECTION_OK){ - ERROR("PQconnectdb() failed: %i", PQstatus(p->conf.conn)); - - PQfinish(p->conf.conn); - - p->conf.conn = NULL; - - return HANDLER_ERROR; - } - } - - /** - * TODO: Change to a stored proc to simiplify this - * build and run SQL query - */ - if (PQstatus(p->conf.conn) != CONNECTION_OK) { - PQreset(p->conf.conn); - } - - buffer_copy_string_buffer(p->tmp_buf, p->conf.postgresql_pre); - if (p->conf.postgresql_post->used) { - buffer_append_string_buffer(p->tmp_buf, con->uri.authority); - buffer_append_string_buffer(p->tmp_buf, p->conf.postgresql_post); - } - - result = PQexec(p->conf.conn, p->tmp_buf->ptr); - - /** - * For Async requests. Database will block/slow down your daemon - * result = PQsendQuery(p->conf.conn, p->tmp_buf->ptr); - */ - if (result == NULL) { - ERROR("PQexec(%s) failed: %s", SAFE_BUF_STR(p->tmp_buf), PQerrorMessage(p->conf.conn)); - return HANDLER_ERROR; - } - - if (PQresultStatus(result) != PGRES_TUPLES_OK) { - ERROR("PQresultStatus(%s): %s", SAFE_BUF_STR(p->tmp_buf), PQerrorMessage(p->conf.conn)); - - PQclear(result); - - return HANDLER_ERROR; - } - /** - * FIXME: - * We checked for tupels already, but will add more sanity - */ - nFields = PQnfields(result); - if (PQntuples(result) < 1 || nFields < 1 || - (field = PQgetvalue(result, 0, 0)) == NULL || !*field) { - /* no such virtual host */ - PQclear(result); - - return HANDLER_ERROR; - } - - buffer_copy_string(docroot, field); - - PQclear(result); - - return HANDLER_GO_ON; -} - -/* this function is called at dlopen() time and inits the callbacks */ -LI_EXPORT int mod_postgresql_vhost_plugin_init(plugin *p); -LI_EXPORT int mod_postgresql_vhost_plugin_init(plugin *p) { - data_string *ds; - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("postgresql_vhost"); - p->init = mod_postgresql_vhost_init; - p->cleanup = mod_postgresql_vhost_cleanup; - p->set_defaults = mod_postgresql_vhost_set_defaults; - - ds = data_string_init(); - - buffer_copy_string_len(ds->value, CONST_STR_LEN(CORE_PLUGIN)); - array_insert_unique(p->required_plugins, (data_unset *)ds); - - return 0; -} -#else -/* we don't have postgresql support, this plugin does nothing */ -LI_EXPORT int mod_postgresql_vhost_plugin_init(plugin *p); -LI_EXPORT int mod_postgresql_vhost_plugin_init(plugin *p) { - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("postgresql_vhost"); - - return 0; -} -#endif diff --git a/src/mod_proxy_backend_ajp13.c b/src/mod_proxy_backend_ajp13.c deleted file mode 100644 index 1c674f8f..00000000 --- a/src/mod_proxy_backend_ajp13.c +++ /dev/null @@ -1,836 +0,0 @@ -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <ctype.h> -#include <stdio.h> - -#include "inet_ntop_cache.h" -#include "mod_proxy_core.h" -#include "mod_proxy_core_protocol.h" -#include "buffer.h" -#include "log.h" -#include "array.h" -#include "keyvalue.h" -#include "ajp13.h" - -#define CORE_PLUGIN "mod_proxy_core" - -typedef struct { - http_method_t http; - ajp13_method_t ajp13; -} http_method_map; - -static http_method_map http_methods[] = { - { HTTP_METHOD_GET, AJP13_METHOD_GET }, - { HTTP_METHOD_POST, AJP13_METHOD_POST }, - { HTTP_METHOD_HEAD, AJP13_METHOD_HEAD }, - { HTTP_METHOD_OPTIONS, AJP13_METHOD_OPTIONS }, - { HTTP_METHOD_PROPFIND, AJP13_METHOD_PROPFIND }, - { HTTP_METHOD_MKCOL, AJP13_METHOD_MKCOL }, - { HTTP_METHOD_PUT, AJP13_METHOD_PUT }, - { HTTP_METHOD_DELETE, AJP13_METHOD_DELETE }, - { HTTP_METHOD_COPY, AJP13_METHOD_COPY }, - { HTTP_METHOD_MOVE, AJP13_METHOD_MOVE }, - { HTTP_METHOD_PROPPATCH, AJP13_METHOD_PROPPATCH }, - { HTTP_METHOD_REPORT, AJP13_METHOD_REPORT }, - { HTTP_METHOD_CHECKOUT, AJP13_METHOD_CHECKOUT }, - { HTTP_METHOD_CHECKIN, AJP13_METHOD_CHECKIN }, - { HTTP_METHOD_VERSION_CONTROL, AJP13_METHOD_VERSION_CONTROL }, - { HTTP_METHOD_UNCHECKOUT, AJP13_METHOD_UNCHECKOUT }, - { HTTP_METHOD_MKACTIVITY, AJP13_METHOD_MKACTIVITY }, - { HTTP_METHOD_MERGE, AJP13_METHOD_MERGE }, - { HTTP_METHOD_LOCK, AJP13_METHOD_LOCK }, - { HTTP_METHOD_UNLOCK, AJP13_METHOD_UNLOCK }, - { HTTP_METHOD_LABEL, AJP13_METHOD_LABEL }, - { HTTP_METHOD_CONNECT, AJP13_METHOD_UNKNOWN }, - { HTTP_METHOD_UNSET, AJP13_METHOD_UNKNOWN } -}; - -static ajp13_method_t ajp13_convert_http_method(http_method_t http_method) { - int i; - /* try fast lookup. if http_methods[] is sorted, we will find it this way. */ - if(http_method != HTTP_METHOD_UNSET) { - if(http_methods[http_method].http == http_method) { - return http_methods[http_method].ajp13; - } - } - /* walk the list to find ajp13 method. */ - for(i = 0; http_methods[i].http != HTTP_METHOD_UNSET; i++) { - if(http_methods[i].http == http_method) return http_methods[i].http; - } - return AJP13_METHOD_UNKNOWN; -} - -/* - * request header keyvalue map. - * - * Note: The strings have to be uppercase - */ -static keyvalue request_headers[] = { - { AJP13_REQ_ACCEPT, "ACCEPT" }, - { AJP13_REQ_ACCEPT_CHARSET, "ACCEPT-CHARSET" }, - { AJP13_REQ_ACCEPT_ENCODING, "ACCEPT-ENCODING" }, - { AJP13_REQ_ACCEPT_LANGUAGE, "ACCEPT-LANGUAGE" }, - { AJP13_REQ_AUTHORIZATION, "AUTHORIZATION" }, - { AJP13_REQ_CONNECTION, "CONNECTION" }, - { AJP13_REQ_CONTENT_TYPE, "CONTENT-TYPE" }, - { AJP13_REQ_CONTENT_LENGTH, "CONTENT-LENGTH" }, - { AJP13_REQ_COOKIE, "COOKIE" }, - { AJP13_REQ_COOKIE2, "COOKIE2" }, - { AJP13_REQ_HOST, "HOST" }, - { AJP13_REQ_PRAGMA, "PRAGMA" }, - { AJP13_REQ_REFERER, "REFERER" }, - { AJP13_REQ_USER_AGENT, "USER-AGENT" }, - - { -1, NULL } -}; - -/* - * response header keyvalue map - * - * Note: The strings don't have to be all uppercase here - */ -static keyvalue response_headers[] = { - { AJP13_RESP_CONTENT_TYPE, "Content-Type" }, - { AJP13_RESP_CONTENT_LANGUAGE, "Content-Language" }, - { AJP13_RESP_CONTENT_LENGTH, "Content-Length" }, - { AJP13_RESP_DATE, "Date" }, - { AJP13_RESP_LAST_MODIFIED, "Last-Modified" }, - { AJP13_RESP_LOCATION, "Location" }, - { AJP13_RESP_SET_COOKIE, "Set-Cookie" }, - { AJP13_RESP_SET_COOKIE2, "Set-Cookie2" }, - { AJP13_RESP_SERVLET_ENGINE, "Servlet-Engine" }, - { AJP13_RESP_STATUS, "Status" }, - { AJP13_RESP_WWW_AUTHENTICATE, "WWW-Authenticate" }, - { -1, NULL } -}; - - -typedef struct { - PLUGIN_DATA; - - proxy_protocol *protocol; -} protocol_plugin_data; - -typedef struct { - size_t len; - off_t offset; - int type; -} ajp13_packet; - -typedef struct { - unsigned char magicB0; - unsigned char magicB1; - unsigned char lengthB0; - unsigned char lengthB1; - unsigned char type; -} AJP13_Header; - -/** - * init the AJP13_header - */ -static int ajp13_header(char *ptr, int length) { - AJP13_Header * header = (AJP13_Header *)ptr; - header->magicB0 = (AJP13_SERVER_MAGIC >> 8) & 0xff; - header->magicB1 = (AJP13_SERVER_MAGIC ) & 0xff; - header->lengthB0 = (length >> 8) & 0xff; - header->lengthB1 = (length ) & 0xff; - return AJP13_HEADER_LEN; -} - -/** - * The ajp13 protocol decoder will use this struct for storing state variables - * used in decoding the stream - */ -typedef struct { - buffer *buf; /* holds raw header bytes or used to buffer STDERR */ - off_t offset; /* parse offset into buffer. */ - ajp13_packet packet; /* parsed info about current packet. */ - size_t chunk_len; /* chunk length */ - size_t requested_bytes; /* backend requested we send this many bytes of the request content. */ -} ajp13_state_data; - -static ajp13_state_data *ajp13_state_data_init(void) { - ajp13_state_data *data; - - data = calloc(1, sizeof(*data)); - data->buf = buffer_init(); - - return data; -} - -static void ajp13_state_data_free(ajp13_state_data *data) { - buffer_free(data->buf); - free(data); -} - -static void ajp13_state_data_reset(ajp13_state_data *data) { - buffer_reset(data->buf); - data->packet.len = 0; - data->packet.offset = 0; - data->packet.type = 0; - data->offset = 0; - data->chunk_len = 0; -} - -/** - * encode ajp13 byte/boolean - */ -static int ajp13_encode_byte(buffer *buf, int val) { - - /* format : 2 bytes integer */ - buffer_prepare_append(buf, 1); - - buf->ptr[buf->used++] = val; - - return 1; -} - -/** - * encode ajp13 integer (2 bytes) - */ -static int ajp13_encode_int(buffer *buf, int val) { - - /* format : 2 bytes integer */ - buffer_prepare_append(buf, 2); - - buf->ptr[buf->used++] = (val >> 8) & 0xff; - buf->ptr[buf->used++] = (val >> 0) & 0xff; - - return 2; -} - -/** - * encode ajp13 string. - */ -static int ajp13_encode_string(buffer *buf, const char *str, size_t str_len) { - size_t len = 0; - - if (!str || str_len == 0) { - return ajp13_encode_int(buf,0xFFFF); - } - - /* format : 2 byte string length : string : null */ - len = 2 + str_len + 1; - - buffer_prepare_append(buf, len); - - ajp13_encode_int(buf,str_len); - /* include the NUL */ - buffer_append_memory(buf, str, str_len + 1); - - return len; -} - -/** - * add a key-value pair to the ajp13-buffer - */ -#define MAX_KEY_LEN 16 -static int ajp13_env_add(buffer *buf, const char *key, size_t key_len, const char *val, size_t val_len) { - char uppercase_key[MAX_KEY_LEN]; - size_t len = 0; - int code = -1; - size_t i; - - if (!key || !val) return -1; - - if(key_len < MAX_KEY_LEN) { - /* convert key to uppercase */ - for(i=0;i <key_len;i++) { - uppercase_key[i] = toupper(key[i]); - } - uppercase_key[key_len] = '\0'; - code = keyvalue_get_key(request_headers, uppercase_key); - } - if(code >= 0) { - len += ajp13_encode_int(buf, AJP13_COMMON_HEADER_CODE + code); - } else { - len += ajp13_encode_string(buf, key, key_len); - } - len += ajp13_encode_string(buf, val, val_len); - - return len; -} - -/** - * decode ajp13 integer (2 bytes) - */ -static int ajp13_decode_int(ajp13_state_data *data) { - int val = 0; - - if ((data->buf->used - data->offset) <= 2) return -1; - - val = (unsigned char)data->buf->ptr[data->offset++]; - val <<= 8; - val |= (unsigned char)data->buf->ptr[data->offset++]; - - return val; -} - -/** - * decode ajp13 string. - */ -static int ajp13_decode_string(buffer *str, ajp13_state_data *data, int is_header) { - size_t len = 0; - const char *p = NULL; - - if (!str) { - return len; - } - - /* string length */ - len = ajp13_decode_int(data); - if ((ssize_t)len == -1) { - ERROR("ajp13_decode_int() returned invalid len: %zu", len); - return len; - } -#ifdef AJP13_DEBUG - TRACE("ajp13_decode_string() string-len: %zu (is_header: %d, common-header: %zd)", len, is_header, (len & AJP13_COMMON_HEADER_CODE)); -#endif - - /* if string is header, check for common header code. */ - if (is_header && (len & AJP13_COMMON_HEADER_CODE)) { - p = keyvalue_get_value(response_headers, len & ~AJP13_COMMON_HEADER_CODE); - if (p) { - len = strlen(p); - } else { - ERROR("ajp13_decode_string() can't resolve common-header: %zd", len & ~AJP13_COMMON_HEADER_CODE); - - return -1; - } - } - /* copy string from buffer. */ - if (p == NULL) { - if ((data->buf->used - data->offset) <= (len + 1)) { - ERROR("we have %jd bytes, but a partial-string wants %zu. no way", (intmax_t) (data->buf->used - data->offset), len); - return -1; - } - p = data->buf->ptr + data->offset; - data->offset += len + 1; - } - buffer_copy_string_len(str, p, len); - - return len; -} - -/** - * decode ajp13 response headers - */ -static int ajp13_decode_response_headers(http_resp *resp, ajp13_state_data *data) { - buffer *key, *value; - int key_len, value_len; - int i,num; - - resp->protocol = HTTP_VERSION_UNSET; - resp->status = ajp13_decode_int(data); - - if (resp->status == -1) { - ERROR("parsing AJP13 response-status failed, got %d", resp->status); - return -1; - } - - if (ajp13_decode_string(resp->reason, data, 0) == -1) { - ERROR("parsing AJP13 response-reason failed: %s", "..."); - return -1; - } - -#ifdef AJP13_DEBUG - TRACE("ajp13: header-status: %d", resp->status); - TRACE("ajp13: header-reason: %s", resp->reason->ptr); -#endif - - /* leave if we have no headers to decode */ - num = ajp13_decode_int(data); -#ifdef AJP13_DEBUG - TRACE("ajp13: header-count: %d", num); -#endif - - if (0 == num) return 0; - - key = buffer_init(); - value = buffer_init(); - for(i = 0; i < num; i++) { - key_len = ajp13_decode_string(key, data, 1); - value_len = ajp13_decode_string(value, data, 1); -#ifdef AJP13_DEBUG - TRACE("ajp13: header[%d]: key-len: %d, value-len: %d", i, key_len, value_len); -#endif - - if (key_len > 0 && value_len >= 0) { - data_string *HDR; - if (NULL == (HDR = (data_string *)array_get_unused_element(resp->headers, TYPE_STRING))) { - HDR = data_response_init(); - } - - buffer_copy_string_len(HDR->key, key->ptr, key_len); - buffer_copy_string_len(HDR->value, value->ptr, value_len); - - array_insert_unique(resp->headers, (data_unset *)HDR); -#ifdef AJP13_DEBUG - TRACE("ajp13: header[%d]: %s = %s", i, key->ptr, value->ptr); -#endif - } else { - ERROR("ajp13: response-headers skipped: key-len = %d, val-len = %d", key_len, value_len); - } - } - buffer_free(key); - buffer_free(value); - - return 0; -} - -PROXY_CONNECTION_FUNC(proxy_ajp13_init) { - UNUSED(srv); - - if(!proxy_con->protocol_data) { - proxy_con->protocol_data = ajp13_state_data_init(); - } - return 1; -} - -PROXY_CONNECTION_FUNC(proxy_ajp13_cleanup) { - UNUSED(srv); - - if(proxy_con->protocol_data) { - ajp13_state_data_free((ajp13_state_data *)proxy_con->protocol_data); - proxy_con->protocol_data = NULL; - } - return 1; -} - -static int proxy_ajp13_forward_request(server *srv, connection *con, proxy_session *sess, buffer *packet) { - char buf[32]; - const char *str; - server_socket *srv_sock = con->srv_socket; -#ifdef HAVE_IPV6 - char b2[INET6_ADDRSTRLEN + 1]; -#endif - int len = 0,port = 0; - size_t i; - - /* prefix_code */ - len += ajp13_encode_byte(packet, AJP13_TYPE_FORWARD_REQUEST); - - /* http method */ - len += ajp13_encode_byte(packet, ajp13_convert_http_method(con->request.http_method)); - - /* protocol */ - str = get_http_version_name(con->request.http_version); - len += ajp13_encode_string(packet, str, strlen(str)); - - /* request uri - * - * the docs where unspecific here, but it looks like tomcat wants to get - * the uri without the query-string here */ - len += ajp13_encode_string(packet, CONST_BUF_LEN(con->uri.path)); - - /* remote address */ - str = inet_ntop_cache_get_ip(srv, &(con->dst_addr)); - len += ajp13_encode_string(packet, str, strlen(str)); - - /* remote host */ - len += ajp13_encode_string(packet, CONST_STR_LEN("")); - - /* server name */ - if (con->server_name->used) { - len += ajp13_encode_string(packet, CONST_BUF_LEN(con->server_name)); - } else { -#ifdef HAVE_IPV6 - str = inet_ntop(srv_sock->addr.plain.sa_family, - srv_sock->addr.plain.sa_family == AF_INET6 ? - (const void *) &(srv_sock->addr.ipv6.sin6_addr) : - (const void *) &(srv_sock->addr.ipv4.sin_addr), - b2, sizeof(b2)-1); -#else - str = inet_ntoa(srv_sock->addr.ipv4.sin_addr); -#endif - len += ajp13_encode_string(packet, str, strlen(str)); - } - - /* server port */ -#ifdef HAVE_IPV6 - port = ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port); -#else - port = ntohs(srv_sock->addr.ipv4.sin_port); -#endif - len += ajp13_encode_int(packet, port); - - /* is_ssl */ -#ifdef USE_OPENSSL - len += ajp13_encode_byte(packet, srv_sock->is_ssl); -#else - len += ajp13_encode_byte(packet, 0); -#endif - - /* make sure we have content-length header */ - if(con->request.content_length > 0) { - LI_ltostr(buf, con->request.content_length); - array_set_key_value(sess->request_headers, CONST_STR_LEN("Content-Length"), buf, strlen(buf)); - } else { - array_set_key_value(sess->request_headers, CONST_STR_LEN("Content-Length"), CONST_STR_LEN("0")); - } - - /* request headers count */ - len += ajp13_encode_int(packet, sess->request_headers->used); - - /* request headers */ - for (i = 0; i < sess->request_headers->used; i++) { - data_string *ds; - ds = (data_string *)sess->request_headers->data[i]; - len += ajp13_env_add(packet, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); - } - - /* remote user */ - if (!buffer_is_empty(con->authed_user)) { - len += ajp13_encode_byte(packet, AJP13_ATTRIBUTE_REMOTE_USER); - len += ajp13_encode_string(packet, CONST_BUF_LEN(con->authed_user)); - } - - /* query string */ - if (!buffer_is_empty(con->uri.query)) { - len += ajp13_encode_byte(packet, AJP13_ATTRIBUTE_QUERY_STRING); - len += ajp13_encode_string(packet, CONST_BUF_LEN(con->uri.query)); - } - - /* jvm_route */ - if (!buffer_is_empty(sess->proxy_con->address->name)) { - len += ajp13_encode_byte(packet, AJP13_ATTRIBUTE_JVM_ROUTE); - len += ajp13_encode_string(packet, CONST_BUF_LEN(sess->proxy_con->address->name)); - } - - /* request terminator */ - len += ajp13_encode_byte(packet, AJP13_ATTRIBUTE_ARE_DONE); - - return len; -} - -PROXY_STREAM_ENCODER_FUNC(proxy_ajp13_encode_request_headers) { - proxy_connection *proxy_con = sess->proxy_con; - ajp13_state_data *data = (ajp13_state_data *)proxy_con->protocol_data; - chunkqueue *out = proxy_con->send; - connection *con = sess->remote_con; - buffer *packet; - size_t len; - - UNUSED(srv); - UNUSED(in); - - packet = chunkqueue_get_append_buffer(out); - buffer_prepare_copy(packet, 1024); - - /* reserve bytes for header. Will over right header when we know packet length. */ - packet->used += AJP13_HEADER_LEN; - - /* send AJP13_TYPE_FORWARD_REQUEST */ - len = proxy_ajp13_forward_request(srv, con, sess, packet); - packet->used++; /* this is needed because the network will only write "used - 1" bytes */ - out->bytes_in += packet->used - 1; - - /* rewrite packet header with correct length. */ - ajp13_header(packet->ptr, len); - - if(con->request.content_length > AJP13_MAX_BODY_PACKET_SIZE) { - data->requested_bytes = AJP13_MAX_BODY_PACKET_SIZE; - } else if(con->request.content_length > 0) { - data->requested_bytes = con->request.content_length; - } else { - data->requested_bytes = 0; - } - - return HANDLER_FINISHED; -} - -/* - * copy len bytes from chunk-chain into buffer - */ -static int proxy_ajp13_fill_buffer(ajp13_state_data *data, chunkqueue *in, size_t len) { - off_t we_have = 0, we_need = len; - chunk *c; - - buffer_prepare_append(data->buf, we_need); - for (c = in->first; c && we_need > 0; c = c->next) { - if(c->mem->used == 0) continue; - - we_have = c->mem->used - c->offset - 1; - if (we_have == 0) continue; - if (we_have > we_need) we_have = we_need; - - buffer_append_string_len(data->buf, c->mem->ptr + c->offset, we_have); - data->packet.offset += we_have; - c->offset += we_have; - in->bytes_out += we_have; - we_need -= we_have; - } - return we_need; -} - -PROXY_STREAM_DECODER_FUNC(proxy_ajp13_stream_decoder_internal) { - proxy_connection *proxy_con = sess->proxy_con; - ajp13_state_data *data = (ajp13_state_data *)proxy_con->protocol_data; - chunkqueue *in = proxy_con->recv; - AJP13_Header *header; - size_t we_parsed = 0, we_need = 0; - handler_t rc = HANDLER_GO_ON; - int magic = 0; - int reuse = 0; - - UNUSED(srv); - - /* no data ? */ - if (!in->first) return HANDLER_WAIT_FOR_EVENT; - - /* parse the packet header. */ - if(data->packet.offset < AJP13_FULL_HEADER_LEN) { - we_need = (AJP13_FULL_HEADER_LEN - data->packet.offset); - /* copy ajp13 header to buffer */ - we_need = proxy_ajp13_fill_buffer(data, in, we_need); - /* make sure we have the full ajp13 header. */ - if(we_need > 0) { - /* we need more data to parse the header. */ - return HANDLER_WAIT_FOR_EVENT; - } - /* parse raw header. */ - header = (AJP13_Header *)(data->buf->ptr); - - data->packet.len = ((header->lengthB0 << 8) | header->lengthB1); - data->packet.len--; /* packet type byte already parsed. */ - data->packet.type = header->type; - magic = ((header->magicB0 << 8) | header->magicB1); - if (magic != AJP13_CONTAINER_MAGIC) { - ERROR("%s", "bad ajp13 magic code, invalid protocl stream"); - return HANDLER_ERROR; - } - - /* Finished parsing raw header bytes. */ - buffer_reset(data->buf); - } - - /* for most packet types copy the content into the data buffer */ - if (data->packet.type != AJP13_TYPE_SEND_BODY_CHUNK) { - we_need = data->packet.len - (data->packet.offset - AJP13_FULL_HEADER_LEN); - if(we_need > 0) { - /* copy ajp13 packet contents to buffer */ - we_need = proxy_ajp13_fill_buffer(data, in, we_need); - /* make sure we have the full ajp13 packet content. */ - if(we_need > 0) { - /* we need more data to parse the content. */ - return HANDLER_WAIT_FOR_EVENT; - } - } - } - - switch (data->packet.type) { - case AJP13_TYPE_GET_BODY_CHUNK: - data->requested_bytes = ajp13_decode_int(data); - break; - case AJP13_TYPE_SEND_HEADERS: - if (ajp13_decode_response_headers(sess->resp, data) == -1) { - ERROR("%s", "Error parsing response_headers"); - rc = HANDLER_ERROR; - } - sess->have_response_headers = 1; - break; - case AJP13_TYPE_SEND_BODY_CHUNK: - /* parse chunk length */ - we_parsed = (data->packet.offset - AJP13_FULL_HEADER_LEN); - if (we_parsed < 2) { - we_need = 2 - we_parsed; - /* copy chunk length bytes to buffer */ - we_need = proxy_ajp13_fill_buffer(data, in, we_need); - if(we_need > 0) { - /* we need more data to parse the chunk length. */ - return HANDLER_WAIT_FOR_EVENT; - } - /* parse chunk length */ - data->chunk_len = ajp13_decode_int(data); - } - /* parse chunk data. */ - we_parsed = (data->packet.offset - 2 - AJP13_FULL_HEADER_LEN); - if(we_parsed < data->chunk_len) { - we_need = data->chunk_len - we_parsed; - /* copy chunk data */ - we_parsed = chunkqueue_steal_chunks_len(out, in->first, we_need); - data->packet.offset += we_parsed; - we_need -= we_parsed; - in->bytes_out += we_parsed; - out->bytes_in += we_parsed; - } - we_need = data->packet.len - (data->packet.offset - AJP13_FULL_HEADER_LEN); - /* ignore padding */ - if(we_need > 0) { - we_parsed = chunkqueue_skip(in, we_need); - data->packet.offset += we_parsed; - we_need -= we_parsed; - in->bytes_out += we_parsed; - } - rc = HANDLER_GO_ON; - break; - case AJP13_TYPE_END_RESPONSE: - if(data->buf->used >= 1) { - reuse = data->buf->ptr[0]; - } - if(reuse != 1) { - sess->is_closing = 1; - } - sess->is_request_finished = 1; - /* close the queues. no more data to decode. */ - in->is_closed = 1; - out->is_closed = 1; - /* make sure the send queue is closed, since we can't send any more request content. */ - proxy_con->send->is_closed = 1; - rc = HANDLER_FINISHED; - break; - default: - TRACE("unknown packet.type: %d", data->packet.type); - rc = HANDLER_ERROR; - break; - } - - if(we_need == 0) { - /* packet finished, reset state for next packet */ - ajp13_state_data_reset(data); - } - - chunkqueue_remove_finished_chunks(in); - - return rc; -} - -PROXY_STREAM_DECODER_FUNC(proxy_ajp13_stream_decoder) { - proxy_connection *proxy_con = sess->proxy_con; - chunkqueue *in = proxy_con->recv; - int res; - - if(out->is_closed) return 1; - /* decode the whole packet stream */ - do { - /* decode the packet */ - res = proxy_ajp13_stream_decoder_internal(srv, sess, out); - } while (in->first && res == HANDLER_GO_ON); - - if (res == HANDLER_WAIT_FOR_EVENT) { - if (in->is_closed) return HANDLER_ERROR; - return HANDLER_GO_ON; - } - - return res; -} - -/** - * transform the content-stream into a valid FastCGI STDIN content-stream - * - * as we don't apply chunked-encoding here, pass it on AS IS - */ -PROXY_STREAM_ENCODER_FUNC(proxy_ajp13_stream_encoder) { - proxy_connection *proxy_con = sess->proxy_con; - ajp13_state_data *data = (ajp13_state_data *)proxy_con->protocol_data; - chunkqueue *out = proxy_con->send; - size_t we_need = 0, we_have = 0; - buffer *b; - - UNUSED(srv); - - /* output queue closed, can't encode any more data. */ - if(out->is_closed) return HANDLER_FINISHED; - - if (data->requested_bytes == 0) { - /* backend needs to send a request for more content before we can encode more. */ - return HANDLER_GO_ON; - } - - /* calculate how many bytes we can encode. */ - if (in->bytes_in > in->bytes_out) { - we_need = in->bytes_in - in->bytes_out; - if (we_need > AJP13_MAX_BODY_PACKET_SIZE) we_need = AJP13_MAX_BODY_PACKET_SIZE; - if (we_need > data->requested_bytes) we_need = data->requested_bytes; - - data->requested_bytes = 0; - } - /* - * write ajp13 header - */ - b = chunkqueue_get_append_buffer(out); - buffer_prepare_copy(b, AJP13_HEADER_LEN); - b->used += AJP13_HEADER_LEN; - if (we_need > 0) { - ajp13_header(b->ptr, we_need + 2); - ajp13_encode_int(b, we_need); - } else { - /* empty body packet to mark EOF, if the backend requested data beyond - * the end of the request content. - */ - ajp13_header(b->ptr, we_need); - } - out->bytes_in += b->used; - b->used++; - - we_have = chunkqueue_steal_chunks_len(out, in->first, we_need); - in->bytes_out += we_have; - out->bytes_in += we_have; - - if (in->bytes_in == in->bytes_out && in->is_closed) { - /* We are finished encoding the request content, - * but we can't close the send queue here since the backend might - * try to request more request content then is available and - * we will have to response with a 0 length body packet. - */ - return HANDLER_FINISHED; - } - - return HANDLER_GO_ON; -} - -INIT_FUNC(mod_proxy_backend_ajp13_init) { - mod_proxy_core_plugin_data *core_data; - protocol_plugin_data *p; - - /* get the plugin_data of the core-plugin */ - core_data = plugin_get_config(srv, CORE_PLUGIN); - if(!core_data) return NULL; - - p = calloc(1, sizeof(*p)); - - /* define protocol handler callbacks */ - p->protocol = core_data->proxy_register_protocol("ajp13"); - - p->protocol->proxy_stream_init = proxy_ajp13_init; - p->protocol->proxy_stream_cleanup = proxy_ajp13_cleanup; - p->protocol->proxy_stream_decoder = proxy_ajp13_stream_decoder; - p->protocol->proxy_stream_encoder = proxy_ajp13_stream_encoder; - p->protocol->proxy_encode_request_headers = proxy_ajp13_encode_request_headers; - - return p; -} - -FREE_FUNC(mod_proxy_backend_ajp13_free) { - protocol_plugin_data *p = p_d; - - UNUSED(srv); - - if (!p) return HANDLER_GO_ON; - - free(p); - - return HANDLER_GO_ON; -} - -LI_EXPORT int mod_proxy_backend_ajp13_plugin_init(plugin *p); -LI_EXPORT int mod_proxy_backend_ajp13_plugin_init(plugin *p) { - data_string *ds; - - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("mod_proxy_backend_ajp13"); - - p->init = mod_proxy_backend_ajp13_init; - p->cleanup = mod_proxy_backend_ajp13_free; - - p->data = NULL; - - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN(CORE_PLUGIN)); - array_insert_unique(p->required_plugins, (data_unset *)ds); - - return 0; -} - - diff --git a/src/mod_proxy_backend_fastcgi.c b/src/mod_proxy_backend_fastcgi.c deleted file mode 100644 index 3ba7c5e6..00000000 --- a/src/mod_proxy_backend_fastcgi.c +++ /dev/null @@ -1,819 +0,0 @@ -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -#include "sys-strings.h" -#include "inet_ntop_cache.h" -#include "mod_proxy_core.h" -#include "mod_proxy_core_protocol.h" -#include "buffer.h" -#include "log.h" -#include "fastcgi.h" -#include "array.h" - -#define CORE_PLUGIN "mod_proxy_core" - -typedef struct { - PLUGIN_DATA; - - proxy_protocol *protocol; -} protocol_plugin_data; - -/** - * we aren't supporting multiplexing - * - * use always the same request-id - */ -#define PROXY_FASTCGI_REQUEST_ID 1 -#if 1 -#define PROXY_FASTCGI_USE_KEEP_ALIVE 1 -#endif - -typedef struct { - size_t len; - off_t offset; - int type; - int padding; - size_t request_id; -} fastcgi_packet; - -/** - * The fastcgi protocol decoder will use this struct for storing state variables - * used in decoding the stream - */ -typedef struct { - buffer *buf; /* holds raw header bytes or used to buffer STDERR */ - fastcgi_packet packet; /* parsed info about current packet. */ - int is_complete; -} fcgi_state_data; - -static fcgi_state_data *fcgi_state_data_init(void) { - fcgi_state_data *data; - - data = calloc(1, sizeof(*data)); - data->buf = buffer_init(); - - return data; -} - -static void fcgi_state_data_free(fcgi_state_data *data) { - buffer_free(data->buf); - free(data); -} - -static void fcgi_state_data_reset(fcgi_state_data *data) { - buffer_reset(data->buf); - data->packet.len = 0; - data->packet.offset = 0; - data->packet.type = 0; - data->packet.padding = 0; - data->packet.request_id = 0; - data->is_complete = 0; -} - -PROXY_CONNECTION_FUNC(proxy_fastcgi_init) { - UNUSED(srv); - - if(!proxy_con->protocol_data) { - proxy_con->protocol_data = fcgi_state_data_init(); - } - return 1; -} - -PROXY_CONNECTION_FUNC(proxy_fastcgi_cleanup) { - UNUSED(srv); - - if(proxy_con->protocol_data) { - fcgi_state_data_free((fcgi_state_data *)proxy_con->protocol_data); - proxy_con->protocol_data = NULL; - } - return 1; -} - -static int proxy_fastcgi_get_env_fastcgi(server *srv, connection *con, plugin_data *p, proxy_session *sess) { - char buf[32]; - const char *s; - server_socket *srv_sock = con->srv_socket; -#ifdef HAVE_IPV6 - char b2[INET6_ADDRSTRLEN + 1]; -#endif - - sock_addr our_addr; - socklen_t our_addr_len; - - array_set_key_value(sess->env_headers, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_NAME"/"PACKAGE_VERSION)); - - if (con->server_name->used) { - size_t len = con->server_name->used - 1; - char *colon = strchr(con->server_name->ptr, ':'); - if (colon) len = colon - con->server_name->ptr; - - array_set_key_value(sess->env_headers, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len); - } else { -#ifdef HAVE_IPV6 - s = inet_ntop(srv_sock->addr.plain.sa_family, - srv_sock->addr.plain.sa_family == AF_INET6 ? - (const void *) &(srv_sock->addr.ipv6.sin6_addr) : - (const void *) &(srv_sock->addr.ipv4.sin_addr), - b2, sizeof(b2)-1); -#else - s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); -#endif - array_set_key_value(sess->env_headers, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); - } - - array_set_key_value(sess->env_headers, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); - - LI_ltostr(buf, -#ifdef HAVE_IPV6 - ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) -#else - ntohs(srv_sock->addr.ipv4.sin_port) -#endif - ); - - array_set_key_value(sess->env_headers, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf)); - - /* get the server-side of the connection to the client */ - our_addr_len = sizeof(our_addr); - - if (-1 == getsockname(con->sock->fd, &(our_addr.plain), &our_addr_len)) { - s = inet_ntop_cache_get_ip(srv, &(srv_sock->addr)); - } else { - s = inet_ntop_cache_get_ip(srv, &(our_addr)); - } - array_set_key_value(sess->env_headers, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); - - LI_ltostr(buf, -#ifdef HAVE_IPV6 - ntohs(con->dst_addr.plain.sa_family ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port) -#else - ntohs(con->dst_addr.ipv4.sin_port) -#endif - ); - - array_set_key_value(sess->env_headers, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf)); - - s = inet_ntop_cache_get_ip(srv, &(con->dst_addr)); - array_set_key_value(sess->env_headers, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); - - if (!buffer_is_empty(con->authed_user)) { - array_set_key_value(sess->env_headers, CONST_STR_LEN("REMOTE_USER"), - CONST_BUF_LEN(con->authed_user)); - } - - if (con->request.content_length > 0) { - /* CGI-SPEC 6.1.2 and FastCGI spec 6.3 */ - - /* request.content_length < SSIZE_MAX, see request.c */ - LI_ltostr(buf, con->request.content_length); - array_set_key_value(sess->env_headers, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf)); - } - - - /* - * 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) - * For AUTHORIZER mode these headers should be omitted. - */ - - array_set_key_value(sess->env_headers, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); - - if (!buffer_is_empty(con->request.pathinfo)) { - array_set_key_value(sess->env_headers, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); - - /* PATH_TRANSLATED is only defined if PATH_INFO is set */ - - buffer_copy_string_buffer(p->tmp_buf, con->physical.doc_root); - buffer_append_string_buffer(p->tmp_buf, con->request.pathinfo); - array_set_key_value(sess->env_headers, CONST_STR_LEN("PATH_TRANSLATED"), CONST_BUF_LEN(p->tmp_buf)); - } else { - array_set_key_value(sess->env_headers, CONST_STR_LEN("PATH_INFO"), CONST_STR_LEN("")); - } - - /* - * 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. - * TODO: this code should be checked against cgi.fix_pathinfo php - * parameter. - */ - - if (1) { - array_set_key_value(sess->env_headers, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); - array_set_key_value(sess->env_headers, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)); - } - - array_set_key_value(sess->env_headers, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); - - if (!buffer_is_equal(sess->request_uri, con->request.orig_uri)) { - array_set_key_value(sess->env_headers, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(sess->request_uri)); - } - if (!buffer_is_empty(con->uri.query)) { - array_set_key_value(sess->env_headers, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); - } else { - array_set_key_value(sess->env_headers, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN("")); - } - - s = get_http_method_name(con->request.http_method); - array_set_key_value(sess->env_headers, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); - array_set_key_value(sess->env_headers, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); /* if php is compiled with --force-redirect */ - s = get_http_version_name(con->request.http_version); - array_set_key_value(sess->env_headers, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); - -#ifdef USE_OPENSSL - if (srv_sock->is_ssl) { - array_set_key_value(sess->env_headers, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); - } -#endif - - return 0; -} - -/** - * transform the HTTP-Request headers into CGI notation - */ -static int proxy_fastcgi_get_env_request(server *srv, connection *con, plugin_data *p, proxy_session *sess) { - size_t i; - - UNUSED(srv); - UNUSED(con); - - /* the request header got already copied into the sess->request_headers for us - * no extra filter is needed - * - * prepend a HTTP_ and uppercase the keys - */ - for (i = 0; i < sess->request_headers->used; i++) { - data_string *ds; - size_t j; - - ds = (data_string *)sess->request_headers->data[i]; - - buffer_reset(p->tmp_buf); - - if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { - buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_")); - p->tmp_buf->used--; - } - - buffer_prepare_append(p->tmp_buf, ds->key->used + 2); - for (j = 0; j < ds->key->used - 1; j++) { - char c = '_'; - if (light_isalpha(ds->key->ptr[j])) { - /* upper-case */ - c = ds->key->ptr[j] & ~32; - } else if (light_isdigit(ds->key->ptr[j])) { - /* copy */ - c = ds->key->ptr[j]; - } - p->tmp_buf->ptr[p->tmp_buf->used++] = c; - } - p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; - - array_set_key_value(sess->env_headers, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); - } - - for (i = 0; i < con->environment->used; i++) { - data_string *ds; - size_t j; - - ds = (data_string *)con->environment->data[i]; - - buffer_reset(p->tmp_buf); - - buffer_prepare_append(p->tmp_buf, ds->key->used + 2); - for (j = 0; j < ds->key->used - 1; j++) { - char c = '_'; - if (light_isalpha(ds->key->ptr[j])) { - /* upper-case */ - c = ds->key->ptr[j] & ~32; - } else if (light_isdigit(ds->key->ptr[j])) { - /* copy */ - c = ds->key->ptr[j]; - } - p->tmp_buf->ptr[p->tmp_buf->used++] = c; - } - p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; - - array_set_key_value(sess->env_headers, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); - } - - return 0; -} - - -/** - * add a key-value pair to the fastcgi-buffer - */ - -static int fcgi_env_add(buffer *env, const char *key, size_t key_len, const char *val, size_t val_len) { - size_t len; - - if (!key || !val) return -1; - - len = key_len + val_len; - - len += key_len > 127 ? 4 : 1; - len += val_len > 127 ? 4 : 1; - - /** - * ensure we don't create a longer packet than fastcgi can handle - */ - if (env->used + len >= FCGI_MAX_LENGTH) { - return -1; - } - - buffer_prepare_append(env, len); - - if (key_len > 127) { - env->ptr[env->used++] = ((key_len >> 24) & 0xff) | 0x80; - env->ptr[env->used++] = (key_len >> 16) & 0xff; - env->ptr[env->used++] = (key_len >> 8) & 0xff; - env->ptr[env->used++] = (key_len >> 0) & 0xff; - } else { - env->ptr[env->used++] = (key_len >> 0) & 0xff; - } - - if (val_len > 127) { - env->ptr[env->used++] = ((val_len >> 24) & 0xff) | 0x80; - env->ptr[env->used++] = (val_len >> 16) & 0xff; - env->ptr[env->used++] = (val_len >> 8) & 0xff; - env->ptr[env->used++] = (val_len >> 0) & 0xff; - } else { - env->ptr[env->used++] = (val_len >> 0) & 0xff; - } - - memcpy(env->ptr + env->used, key, key_len); - env->used += key_len; - memcpy(env->ptr + env->used, val, val_len); - env->used += val_len; - - return 0; -} - -/** - * init the FCGI_header - */ -static int fcgi_header(FCGI_Header * header, unsigned char type, size_t request_id, int contentLength, unsigned char paddingLength) { - 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; - - return 0; -} - - -PROXY_STREAM_ENCODER_FUNC(proxy_fastcgi_encode_request_headers) { - proxy_connection *proxy_con = sess->proxy_con; - chunkqueue *out = proxy_con->send; - connection *con = sess->remote_con; - buffer *b, *packet; - size_t i; - FCGI_BeginRequestRecord beginRecord; - FCGI_Header header; - plugin_data *p = sess->p; - - UNUSED(srv); - UNUSED(in); - - b = chunkqueue_get_append_buffer(out); - /* send FCGI_BEGIN_REQUEST */ - - fcgi_header(&(beginRecord.header), FCGI_BEGIN_REQUEST, PROXY_FASTCGI_REQUEST_ID, sizeof(beginRecord.body), 0); - beginRecord.body.roleB0 = FCGI_RESPONDER; - beginRecord.body.roleB1 = 0; -#ifdef PROXY_FASTCGI_USE_KEEP_ALIVE - if (p->conf.max_keep_alive_requests && p->conf.max_pool_size) { - /** - * only announce keep-alive if we really can handle it - */ - beginRecord.body.flags = FCGI_KEEP_CONN; - } else { - beginRecord.body.flags = 0; - } -#else - beginRecord.body.flags = 0; -#endif - memset(beginRecord.body.reserved, 0, sizeof(beginRecord.body.reserved)); - - buffer_copy_string_len(b, (const char *)&beginRecord, sizeof(beginRecord)); - out->bytes_in += sizeof(beginRecord); - - /* send FCGI_PARAMS */ - b = chunkqueue_get_append_buffer(out); - buffer_prepare_copy(b, 1024); - - /* fill the sess->env_headers */ - array_reset(sess->env_headers); - proxy_fastcgi_get_env_request(srv, con, p, sess); - proxy_fastcgi_get_env_fastcgi(srv, con, p, sess); - - packet = buffer_init(); - - for (i = 0; i < sess->env_headers->used; i++) { - data_string *ds; - - ds = (data_string *)sess->env_headers->data[i]; - fcgi_env_add(packet, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); - } - - fcgi_header(&(header), FCGI_PARAMS, PROXY_FASTCGI_REQUEST_ID, packet->used, 0); - buffer_append_memory(b, (const char *)&header, sizeof(header)); - buffer_append_memory(b, (const char *)packet->ptr, packet->used); - out->bytes_in += sizeof(header); - out->bytes_in += packet->used - 1; - - buffer_free(packet); - - fcgi_header(&(header), FCGI_PARAMS, PROXY_FASTCGI_REQUEST_ID, 0, 0); - buffer_append_memory(b, (const char *)&header, sizeof(header)); - b->used++; - out->bytes_in += sizeof(header) + 1; - - return HANDLER_FINISHED; -} - -/** - * parse the HTTP response header - */ -static handler_t proxy_fastcgi_http_response_headers(proxy_session *sess, chunkqueue *in) { - http_response_reset(sess->resp); - - /* http response parser. */ - switch(http_response_parse_cq(in, sess->resp)) { - case PARSE_ERROR: - /* bad gateway */ - http_response_reset(sess->resp); - sess->have_response_headers = 1; - sess->resp->status = 502; - return HANDLER_ERROR; - case PARSE_NEED_MORE: - return HANDLER_GO_ON; - case PARSE_SUCCESS: - default: - /* finished parsing response headers. */ - sess->have_response_headers = 1; - return HANDLER_FINISHED; - } -} - -PROXY_STREAM_DECODER_FUNC(proxy_fastcgi_stream_decoder_internal) { - proxy_connection *proxy_con = sess->proxy_con; - fcgi_state_data *data = (fcgi_state_data *)proxy_con->protocol_data; - chunkqueue *in = proxy_con->recv; - FCGI_Header *header; - off_t we_have = 0, we_need = 0; - handler_t rc = HANDLER_GO_ON; - chunk *c; - - UNUSED(srv); - - if ((in->bytes_in == in->bytes_out) && in->is_closed) { - /* everything got passed through, - * - * as we usually have a FIN packet we should expect to get a is_closed within the - * fcgi-stream. Looks like the remote side crashed - * */ - out->is_closed = 1; - - TRACE("%jd / %jd -> %d", - (intmax_t) in->bytes_in, (intmax_t) in->bytes_out, - in->is_closed); - - - ERROR("looks like the fastcgi-backend (%s) terminated before it sent a FIN packet", SAFE_BUF_STR(sess->request_uri)); - - return HANDLER_FINISHED; - } - - /* no data ? */ - if (!in->first) return HANDLER_GO_ON; - - /* a single network packet might contain multiple fcgi packets */ - if(!data->is_complete) { - we_need = (FCGI_HEADER_LEN - data->packet.offset); - - /** - * a the fcgi header might spread over multiple network packets - */ - for (c = in->first; c && we_need > 0; c = c->next) { - if (c->mem->used == 0) continue; - - we_have = c->mem->used - c->offset - 1; - if (we_have == 0) continue; - if (we_have > we_need) we_have = we_need; - - buffer_append_string_len(data->buf, c->mem->ptr + c->offset, we_have); - data->packet.offset += we_have; - c->offset += we_have; - in->bytes_out += we_have; - we_need -= we_have; - } - /* make sure we have the full fastcgi header. */ - if(we_need > 0) { - chunkqueue_remove_finished_chunks(in); - /* we need more data to parse the header. */ - return HANDLER_GO_ON; - } - - /* parse raw header. */ - header = (FCGI_Header *)(data->buf->ptr); - - data->packet.len = (header->contentLengthB0 | (header->contentLengthB1 << 8)); - data->packet.request_id = (header->requestIdB0 | (header->requestIdB1 << 8)); - data->packet.type = header->type; - data->packet.padding = header->paddingLength; - data->is_complete = 1; - - /* Finished parsing raw header bytes. */ - buffer_reset(data->buf); - } - - /* proccess the packet's contents. */ - we_need = data->packet.len - (data->packet.offset - FCGI_HEADER_LEN); - - switch (data->packet.type) { - case FCGI_STDOUT: - if (we_need > 0) { - /* copy packet contents */ - we_have = chunkqueue_steal_chunks_len(out, in->first, we_need); - data->packet.offset += we_have; - we_need -= we_have; - in->bytes_out += we_have; - out->bytes_in += we_have; - } else { - /** - * we might come here again if the padding is part of the next packet - * - * read(15, "\1\6\0\1\1D\4\0", 16384) = 8 - * read(15, "Status: 200 Ok\r\nCache-contro... - * read(15, "\0\0\0\0\1\6\0\1\37\370\0\0", 16384) = 12 - * - */ -#if 0 - /* FIXME: for which case did we set this to 1 ? */ - out->is_closed = 1; -#endif - } - - /* parse response headers. */ - if (!sess->have_response_headers) { - /* check if we have all the response headers */ - switch (proxy_fastcgi_http_response_headers(sess, out)) { - case HANDLER_FINISHED: - /* the headers are complete */ - - rc = HANDLER_GO_ON; - break; - case HANDLER_GO_ON: - /* no finished yet */ - rc = HANDLER_GO_ON; - break; - default: - /* something failed */ - rc = HANDLER_ERROR; - break; - } - } else { - rc = HANDLER_GO_ON; - } - break; - case FCGI_STDERR: - if(we_need > 0) { - buffer *b = buffer_init(); - buffer_prepare_append(b, we_need); - for (c = in->first; c && we_need > 0; c = c->next) { - if (c->mem->used == 0) continue; - - we_have = c->mem->used - c->offset - 1; - if (we_have == 0) continue; - if (we_have > we_need) we_have = we_need; - - buffer_append_string_len(b, c->mem->ptr + c->offset, we_have); - data->packet.offset += we_have; - c->offset += we_have; - in->bytes_out += we_have; - we_need -= we_have; - } -/** -==18752== Conditional jump or move depends on uninitialised value(s) -==18752== at 0x401D255: strlen (mc_replace_strmem.c:246) -==18752== by 0x42B7A1B: vfprintf (in /lib/tls/i686/cmov/libc-2.3.6.so) -==18752== by 0x42D6280: vsnprintf (in /lib/tls/i686/cmov/libc-2.3.6.so) -==18752== by 0x805EAAD: log_trace (log.c:325) -==18752== by 0x4513C5B: proxy_fastcgi_stream_decoder (mod_proxy_backend_fastcgi.c:594) -==18752== by 0x4565130: proxy_stream_decoder (mod_proxy_core.c:587) -==18752== by 0x4565D46: proxy_stream_encode_decode (mod_proxy_core.c:855) -==18752== by 0x4567D69: proxy_state_engine (mod_proxy_core.c:1567) -==18752== by 0x45686C7: mod_proxy_core_start_backend (mod_proxy_core.c:2397) -==18752== by 0x4568CBC: mod_proxy_send_request_content (mod_proxy_core.c:2441) -==18752== by 0x80634EC: plugins_call_handle_send_request_content (plugin.c:387) -==18752== by 0x8054CE7: connection_state_machine (connections.c:1192) -==18752== -==18752== Syscall param write(buf) points to uninitialised byte(s) -==18752== at 0x4000792: (within /lib/ld-2.3.6.so) -==18752== by 0x4513C5B: proxy_fastcgi_stream_decoder (mod_proxy_backend_fastcgi.c:594) -==18752== by 0x4565130: proxy_stream_decoder (mod_proxy_core.c:587) -==18752== by 0x4565D46: proxy_stream_encode_decode (mod_proxy_core.c:855) -==18752== by 0x4567D69: proxy_state_engine (mod_proxy_core.c:1567) -==18752== by 0x45686C7: mod_proxy_core_start_backend (mod_proxy_core.c:2397) -==18752== by 0x4568CBC: mod_proxy_send_request_content (mod_proxy_core.c:2441) -==18752== by 0x80634EC: plugins_call_handle_send_request_content (plugin.c:387) -==18752== by 0x8054CE7: connection_state_machine (connections.c:1192) -==18752== by 0x804F5D8: lighty_mainloop (server.c:1001) -==18752== by 0x80517AE: main (server.c:1705) -==18752== Address 0x4A7C899 is 81 bytes inside a block of size 4,160 alloc'd -==18752== at 0x401C4B0: malloc (vg_replace_malloc.c:149) -==18752== by 0x805DCFA: buffer_prepare_copy (buffer.c:85) -==18752== by 0x805EA7E: log_trace (log.c:321) -==18752== by 0x4513C5B: proxy_fastcgi_stream_decoder (mod_proxy_backend_fastcgi.c:594) -==18752== by 0x4565130: proxy_stream_decoder (mod_proxy_core.c:587) -==18752== by 0x4565D46: proxy_stream_encode_decode (mod_proxy_core.c:855) -==18752== by 0x4567D69: proxy_state_engine (mod_proxy_core.c:1567) -==18752== by 0x45686C7: mod_proxy_core_start_backend (mod_proxy_core.c:2397) -==18752== by 0x4568CBC: mod_proxy_send_request_content (mod_proxy_core.c:2441) -==18752== by 0x80634EC: plugins_call_handle_send_request_content (plugin.c:387) -==18752== by 0x8054CE7: connection_state_machine (connections.c:1192) -==18752== by 0x804F5D8: lighty_mainloop (server.c:1001) -mod_proxy_backend_fastcgi.c.597: (trace) (stderr from 127.0.0.1:9090 for /trac/) SERVER_ADDR -*/ - TRACE("(stderr from %s for %s) %s", - SAFE_BUF_STR(proxy_con->address->name), - SAFE_BUF_STR(sess->remote_con->uri.path), - SAFE_BUF_STR(b)); - buffer_free(b); - } - rc = HANDLER_GO_ON; - break; - case FCGI_END_REQUEST: - /* ignore packet content. */ - if(we_need > 0) { - we_have = chunkqueue_skip(in, we_need); - data->packet.offset += we_have; - we_need -= we_have; - in->bytes_out += we_have; - } - if(we_need == 0) { -#ifndef PROXY_FASTCGI_USE_KEEP_ALIVE - sess->is_closing = 1; -#endif - sess->have_response_headers = 1; - sess->is_request_finished = 1; - in->is_closed = 1; - out->is_closed = 1; - rc = HANDLER_FINISHED; - } - break; - default: - TRACE("unknown packet.type: %d", data->packet.type); - rc = HANDLER_ERROR; - break; - } - - /* skip packet padding, once content has been processed. */ - if(we_need == 0 && data->packet.padding > 0) { - we_have = chunkqueue_skip(in, data->packet.padding); - data->packet.padding -= we_have; - in->bytes_out += we_have; - } - - if(we_need == 0 && data->packet.padding == 0) { - /* packet finished, reset state for next packet */ - fcgi_state_data_reset(data); - } - - chunkqueue_remove_finished_chunks(in); - - return rc; -} - -PROXY_STREAM_DECODER_FUNC(proxy_fastcgi_stream_decoder) { - proxy_connection *proxy_con = sess->proxy_con; - chunkqueue *in = proxy_con->recv; - int res; - - if(out->is_closed) return HANDLER_FINISHED; - /* decode the whole packet stream */ - do { - /* decode the packet */ - res = proxy_fastcgi_stream_decoder_internal(srv, sess, out); - } while (in->first && res == HANDLER_GO_ON); - - return res; -} - -/** - * transform the content-stream into a valid FastCGI STDIN content-stream - * - * as we don't apply chunked-encoding here, pass it on AS IS - */ -PROXY_STREAM_ENCODER_FUNC(proxy_fastcgi_stream_encoder) { - proxy_connection *proxy_con = sess->proxy_con; - chunkqueue *out = proxy_con->send; - chunk *c; - buffer *b; - FCGI_Header header; - off_t we_need = 0, we_have = 0; - - UNUSED(srv); - - /* output queue closed, can't encode any more data. */ - if(out->is_closed) return HANDLER_FINISHED; - - /* encode data into output queue. */ - for (c = in->first; in->bytes_out < in->bytes_in; ) { - /* - * write fcgi header - */ - if(we_need == 0) { - we_need = in->bytes_in - in->bytes_out; - if(we_need > FCGI_MAX_LENGTH) we_need = FCGI_MAX_LENGTH; - - b = chunkqueue_get_append_buffer(out); - fcgi_header(&(header), FCGI_STDIN, PROXY_FASTCGI_REQUEST_ID, we_need, 0); - buffer_copy_memory(b, (const char *)&header, sizeof(header) + 1); - out->bytes_in += sizeof(header); - } - - we_have = chunkqueue_steal_chunks_len(out, c, we_need); - in->bytes_out += we_have; - out->bytes_in += we_have; - we_need -= we_have; - } - - if (in->bytes_in == in->bytes_out && in->is_closed && !out->is_closed) { - /* send the closing packet */ - b = chunkqueue_get_append_buffer(out); - /* terminate STDIN */ - fcgi_header(&(header), FCGI_STDIN, PROXY_FASTCGI_REQUEST_ID, 0, 0); - buffer_copy_memory(b, (const char *)&header, sizeof(header) + 1); - - out->bytes_in += sizeof(header); - out->is_closed = 1; - return HANDLER_FINISHED; - } - - return HANDLER_GO_ON; -} - -INIT_FUNC(mod_proxy_backend_fastcgi_init) { - mod_proxy_core_plugin_data *core_data; - protocol_plugin_data *p; - - /* get the plugin_data of the core-plugin */ - core_data = plugin_get_config(srv, CORE_PLUGIN); - if(!core_data) return NULL; - - p = calloc(1, sizeof(*p)); - - /* define protocol handler callbacks */ - p->protocol = core_data->proxy_register_protocol("fastcgi"); - - p->protocol->proxy_stream_init = proxy_fastcgi_init; - p->protocol->proxy_stream_cleanup = proxy_fastcgi_cleanup; - p->protocol->proxy_stream_decoder = proxy_fastcgi_stream_decoder; - p->protocol->proxy_stream_encoder = proxy_fastcgi_stream_encoder; - p->protocol->proxy_encode_request_headers = proxy_fastcgi_encode_request_headers; - - return p; -} - -FREE_FUNC(mod_proxy_backend_fastcgi_free) { - protocol_plugin_data *p = p_d; - - UNUSED(srv); - - if (!p) return HANDLER_GO_ON; - - free(p); - - return HANDLER_GO_ON; -} - -LI_EXPORT int mod_proxy_backend_fastcgi_plugin_init(plugin *p); -LI_EXPORT int mod_proxy_backend_fastcgi_plugin_init(plugin *p) { - data_string *ds; - - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("mod_proxy_backend_fastcgi"); - - p->init = mod_proxy_backend_fastcgi_init; - p->cleanup = mod_proxy_backend_fastcgi_free; - - p->data = NULL; - - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN(CORE_PLUGIN)); - array_insert_unique(p->required_plugins, (data_unset *)ds); - - return 0; -} - - diff --git a/src/mod_proxy_backend_http.c b/src/mod_proxy_backend_http.c deleted file mode 100644 index a56bd602..00000000 --- a/src/mod_proxy_backend_http.c +++ /dev/null @@ -1,423 +0,0 @@ -#include <stdlib.h> -#include <string.h> - -#include "mod_proxy_core.h" -#include "mod_proxy_core_protocol.h" -#include "configfile.h" -#include "buffer.h" -#include "log.h" -#include "sys-strings.h" - -#define CORE_PLUGIN "mod_proxy_core" - -typedef struct { - PLUGIN_DATA; - - proxy_protocol *protocol; -} protocol_plugin_data; - -typedef enum { - HTTP_CHUNK_LEN, - HTTP_CHUNK_EXTENSION, - HTTP_CHUNK_DATA, - HTTP_CHUNK_END -} http_chunk_state_t; - -/** - * The protocol will use this struct for storing state variables - * used in decoding the stream - */ -typedef struct { - http_chunk_state_t chunk_parse_state; - off_t chunk_len; - off_t chunk_offset; - buffer *buf; -} protocol_state_data; - -static protocol_state_data *protocol_state_data_init(void) { - protocol_state_data *data; - - data = calloc(1, sizeof(*data)); - data->chunk_parse_state = HTTP_CHUNK_LEN; - data->buf = buffer_init(); - - return data; -} - -static void protocol_state_data_free(protocol_state_data *data) { - buffer_free(data->buf); - free(data); -} - -static void protocol_state_data_reset(protocol_state_data *data) { - buffer_reset(data->buf); - data->chunk_parse_state = HTTP_CHUNK_LEN; -} - -PROXY_CONNECTION_FUNC(proxy_http_init) { - - UNUSED(srv); - - if(!proxy_con->protocol_data) { - proxy_con->protocol_data = protocol_state_data_init(); - } - return 1; -} - -PROXY_CONNECTION_FUNC(proxy_http_cleanup) { - - UNUSED(srv); - - if(proxy_con->protocol_data) { - protocol_state_data_free((protocol_state_data *)proxy_con->protocol_data); - proxy_con->protocol_data = NULL; - } - return 1; -} - -/** - * parse the HTTP response header - */ -static handler_t proxy_http_parse_response_headers(proxy_session *sess, chunkqueue *in) { - data_string *ds; - - http_response_reset(sess->resp); - - /* http response parser. */ - switch(http_response_parse_cq(in, sess->resp)) { - case PARSE_ERROR: - /* bad gateway */ - http_response_reset(sess->resp); - sess->have_response_headers = 1; - sess->resp->status = 502; - return HANDLER_ERROR; - case PARSE_NEED_MORE: - return HANDLER_GO_ON; - case PARSE_SUCCESS: - default: - break; - } - /* check for Transfer-Encoding header. */ - if (NULL != (ds = (data_string *)array_get_element(sess->resp->headers, CONST_STR_LEN("Transfer-Encoding")))) { - if (strstr(ds->value->ptr, "chunked")) { - sess->is_chunked = 1; - } - } - /* finished parsing response headers. */ - sess->have_response_headers = 1; - - switch (sess->resp->status) { - case 205: /* class: header only */ - case 304: - sess->is_request_finished = 1; - } - return HANDLER_FINISHED; -} - -static handler_t proxy_http_parse_chunked_stream(server *srv, proxy_session *sess, chunkqueue *in, chunkqueue *out) { - protocol_state_data *data = (protocol_state_data *)sess->proxy_con->protocol_data; - char *err = NULL; - off_t we_have = 0, we_want = 0; - off_t chunk_len = 0; - off_t offset = 0; - buffer *b; - chunk *c; - char ch = '\0'; - int finished = 0; - - UNUSED(srv); - - for (c = in->first; c && !finished;) { - if(c->mem->used == 0) { - c = c->next; - continue; - } - switch(data->chunk_parse_state) { - case HTTP_CHUNK_LEN: - /* parse chunk len. */ - for(offset = c->offset; (size_t)(offset) < (c->mem->used - 1) ; offset++) { - ch = c->mem->ptr[offset]; - if(!light_isxdigit(ch)) break; - } - if(offset > c->offset) { - buffer_append_string_len(data->buf, (c->mem->ptr + c->offset), offset - c->offset); - in->bytes_out += (offset - c->offset); - c->offset = offset; - } - if ( (size_t)offset == c->mem->used - 1) { - break; /* get next chunk from queue */ - } - /* now ch is the last character, and it wasn't an xdigit */ - if (!(ch == ' ' || ch == '\r' || ch == ';')) { - /* protocol error. bad http-chunk len */ - return HANDLER_ERROR; - } - data->chunk_len = strtol(BUF_STR(data->buf), &err, 16); - data->chunk_offset = 0; - buffer_reset(data->buf); - data->chunk_parse_state = HTTP_CHUNK_EXTENSION; - case HTTP_CHUNK_EXTENSION: - /* find CRLF. discard chunk-extension */ - for(ch = 0; (size_t)(c->offset) < (c->mem->used - 1) && ch != '\n' ;) { - ch = c->mem->ptr[c->offset]; - c->offset++; - in->bytes_out++; - } - if(ch != '\n') { - /* get next chunk from queue */ - break; - } - if(data->chunk_len > 0) { - data->chunk_parse_state = HTTP_CHUNK_DATA; - } else { - data->chunk_parse_state = HTTP_CHUNK_END; - } - case HTTP_CHUNK_DATA: - chunk_len = data->chunk_len - data->chunk_offset; - /* copy chunk_len bytes from in queue to out queue. */ - we_have = c->mem->used - c->offset - 1; - we_want = chunk_len > we_have ? we_have : chunk_len; - - if (c->offset == 0 && we_want == we_have) { - /* we are copying the whole buffer, just steal it */ - chunkqueue_steal_chunk(out, c); - /* c is an empty chunk now */ - } else { - b = chunkqueue_get_append_buffer(out); - buffer_copy_string_len(b, c->mem->ptr + c->offset, we_want); - c->offset += we_want; - } - - chunk_len -= we_want; - out->bytes_in += we_want; - in->bytes_out += we_want; - data->chunk_offset += we_want; - if(chunk_len > 0) { - /* get next chunk from queue */ - break; - } - data->chunk_offset = 0; - data->chunk_parse_state = HTTP_CHUNK_END; - case HTTP_CHUNK_END: - /* discard CRLF.*/ - for(ch = 0; c->mem->used > 0 && (size_t)(c->offset) < (c->mem->used - 1) && ch != '\n' ;) { - ch = c->mem->ptr[c->offset]; - c->offset++; - in->bytes_out++; - } - if(ch != '\n') { - /* get next chunk from queue */ - break; - } - /* final chunk */ - if(data->chunk_len == 0) { - finished = 1; - } - /* finished http-chunk. reset and parse next chunk. */ - protocol_state_data_reset(data); - break; - } - if((size_t)(c->offset) == c->mem->used - 1) { - c = c->next; - } - } - chunkqueue_remove_finished_chunks(in); - if (finished) { - sess->is_request_finished = 1; - return HANDLER_FINISHED; - } - /* ran out of data. */ - return HANDLER_GO_ON; -} - -PROXY_STREAM_DECODER_FUNC(proxy_http_stream_decoder) { - proxy_connection *proxy_con = sess->proxy_con; - chunkqueue *in = proxy_con->recv; - chunk *c; - - if (in->first == NULL) { - if ((sess->content_length >= 0 && sess->bytes_read == sess->content_length) || in->is_closed) { - sess->is_request_finished = 1; - return HANDLER_FINISHED; - } - - return HANDLER_GO_ON; - } - - /* parse response headers. */ - if (!sess->have_response_headers) { - handler_t rc = proxy_http_parse_response_headers(sess, in); - if (rc != HANDLER_FINISHED) return rc; - } - - if (sess->is_request_finished) return HANDLER_FINISHED; - - if (sess->is_chunked) { - return proxy_http_parse_chunked_stream(srv, sess, in, out); - } else { - /* no chunked encoding, ok, perhaps a content-length ? */ - - chunkqueue_remove_finished_chunks(in); - for (c = in->first; c; c = c->next) { - buffer *b; - - if (c->mem->used == 0) continue; - - out->bytes_in += c->mem->used - c->offset - 1; - in->bytes_out += c->mem->used - c->offset - 1; - - sess->bytes_read += c->mem->used - c->offset - 1; - - if (c->offset == 0) { - /* we are copying the whole buffer, just steal it */ - - chunkqueue_steal_chunk(out, c); - } else { - b = chunkqueue_get_append_buffer(out); - buffer_copy_string_len(b, c->mem->ptr + c->offset, c->mem->used - c->offset - 1); - c->offset = c->mem->used - 1; /* marks is read */ - } - - if (sess->bytes_read == sess->content_length) { - break; - } - } - - if (in->is_closed || sess->bytes_read == sess->content_length) { - sess->is_request_finished = 1; - return HANDLER_FINISHED; /* finished */ - } - } - - return HANDLER_GO_ON; -} - -/** - * transform the content-stream into a valid HTTP-content-stream - * - * as we don't apply chunked-encoding here, pass it on AS IS - */ -PROXY_STREAM_ENCODER_FUNC(proxy_http_stream_encoder) { - proxy_connection *proxy_con = sess->proxy_con; - chunkqueue *out = proxy_con->send; - int we_have = 0; - - UNUSED(srv); - - /* output queue closed, can't encode any more data. */ - if(out->is_closed) return HANDLER_FINISHED; - - /* encode all request content data into output queue. */ - we_have = chunkqueue_steal_all_chunks(out, in); - in->bytes_out += we_have; - out->bytes_in += we_have; - - if (in->bytes_in == in->bytes_out && in->is_closed) { - out->is_closed = 1; - return HANDLER_FINISHED; - } - - return HANDLER_GO_ON; -} - -/** - * generate a HTTP/1.1 proxy request from the set of request-headers - * - */ -PROXY_STREAM_ENCODER_FUNC(proxy_http_encode_request_headers) { - proxy_connection *proxy_con = sess->proxy_con; - chunkqueue *out = proxy_con->send; - connection *con = sess->remote_con; - buffer *b; - size_t i; - - UNUSED(srv); - UNUSED(in); - - b = chunkqueue_get_append_buffer(out); - - /* request line */ - buffer_copy_string(b, get_http_method_name(con->request.http_method)); - buffer_append_string_len(b, CONST_STR_LEN(" ")); - - /* request uri */ - buffer_append_string_buffer(b, sess->request_uri); - - if (con->request.http_version == HTTP_VERSION_1_1) { - buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.1\r\n")); - } else { - buffer_append_string_len(b, CONST_STR_LEN(" HTTP/1.0\r\n")); - } - - for (i = 0; i < sess->request_headers->used; i++) { - data_string *ds; - - ds = (data_string *)sess->request_headers->data[i]; - - buffer_append_string_buffer(b, ds->key); - buffer_append_string_len(b, CONST_STR_LEN(": ")); - buffer_append_string_buffer(b, ds->value); - buffer_append_string_len(b, CONST_STR_LEN("\r\n")); - } - - buffer_append_string_len(b, CONST_STR_LEN("\r\n")); - - out->bytes_in += b->used - 1; - - return HANDLER_FINISHED; -} - -INIT_FUNC(mod_proxy_backend_http_init) { - mod_proxy_core_plugin_data *core_data; - protocol_plugin_data *p; - - /* get the plugin_data of the core-plugin */ - core_data = plugin_get_config(srv, CORE_PLUGIN); - if(!core_data) return NULL; - - p = calloc(1, sizeof(*p)); - - /* define protocol handler callbacks */ - p->protocol = (core_data->proxy_register_protocol)("http"); - - p->protocol->proxy_stream_init = proxy_http_init; - p->protocol->proxy_stream_cleanup = proxy_http_cleanup; - p->protocol->proxy_stream_decoder = proxy_http_stream_decoder; - p->protocol->proxy_stream_encoder = proxy_http_stream_encoder; - p->protocol->proxy_encode_request_headers = proxy_http_encode_request_headers; - - return p; -} - -FREE_FUNC(mod_proxy_backend_http_free) { - protocol_plugin_data *p = p_d; - - UNUSED(srv); - - if (!p) return HANDLER_GO_ON; - - free(p); - - return HANDLER_GO_ON; -} - -LI_EXPORT int mod_proxy_backend_http_plugin_init(plugin *p); -LI_EXPORT int mod_proxy_backend_http_plugin_init(plugin *p) { - data_string *ds; - - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("mod_proxy_backend_http"); - - p->init = mod_proxy_backend_http_init; - p->cleanup = mod_proxy_backend_http_free; - - p->data = NULL; - - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN(CORE_PLUGIN)); - array_insert_unique(p->required_plugins, (data_unset *)ds); - - return 0; -} - - diff --git a/src/mod_proxy_backend_scgi.c b/src/mod_proxy_backend_scgi.c deleted file mode 100644 index 60e1de83..00000000 --- a/src/mod_proxy_backend_scgi.c +++ /dev/null @@ -1,475 +0,0 @@ -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <ctype.h> - -#include "sys-strings.h" -#include "inet_ntop_cache.h" -#include "mod_proxy_core.h" -#include "mod_proxy_core_protocol.h" -#include "buffer.h" -#include "log.h" -#include "array.h" - -#define CORE_PLUGIN "mod_proxy_core" - -typedef struct { - PLUGIN_DATA; - - proxy_protocol *protocol; -} protocol_plugin_data; - -/* -PROXY_CONNECTION_FUNC(proxy_scgi_init) { - return 1; -} -*/ - -/* -PROXY_CONNECTION_FUNC(proxy_scgi_cleanup) { - return 1; -} -*/ - -/** - * add a key-value pair to the scgi-buffer - */ -static int scgi_env_add(buffer *env, const char *key, size_t key_len, const char *val, size_t val_len) { - size_t len; - - if (!key || !val) return -1; - - len = key_len + val_len + 2; - - buffer_prepare_append(env, len); - - buffer_append_memory(env, key, key_len); - env->ptr[env->used++] = '\0'; - buffer_append_memory(env, val, val_len); - env->ptr[env->used++] = '\0'; - - return 0; -} - -static int proxy_scgi_get_env_scgi(server *srv, proxy_session *sess, buffer *env_headers) { - connection *con = sess->remote_con; - server_socket *srv_sock = con->srv_socket; - plugin_data *p = sess->p; - socklen_t our_addr_len; - sock_addr our_addr; - const char *s; - char buf[32]; - int len; -#ifdef HAVE_IPV6 - char b2[INET6_ADDRSTRLEN + 1]; -#endif - - /* CGI-SPEC 6.1.2 and FastCGI spec 6.3 */ - - /* request.content_length < SSIZE_MAX, see request.c */ - if(con->request.content_length > 0) { - len = LI_ltostr(buf, con->request.content_length); - } else { - buf[0] = '0'; - buf[1] = '\0'; - len = 1; - } - scgi_env_add(env_headers, CONST_STR_LEN("CONTENT_LENGTH"), buf, len); - scgi_env_add(env_headers, CONST_STR_LEN("SCGI"), CONST_STR_LEN("1")); - - scgi_env_add(env_headers, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_NAME"/"PACKAGE_VERSION)); - - if (con->server_name->used) { - size_t _len = con->server_name->used - 1; - char *colon = strchr(con->server_name->ptr, ':'); - if (colon) _len = colon - con->server_name->ptr; - - scgi_env_add(env_headers, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, _len); - } else { -#ifdef HAVE_IPV6 - s = inet_ntop(srv_sock->addr.plain.sa_family, - srv_sock->addr.plain.sa_family == AF_INET6 ? - (const void *) &(srv_sock->addr.ipv6.sin6_addr) : - (const void *) &(srv_sock->addr.ipv4.sin_addr), - b2, sizeof(b2)-1); -#else - s = inet_ntoa(srv_sock->addr.ipv4.sin_addr); -#endif - scgi_env_add(env_headers, CONST_STR_LEN("SERVER_NAME"), s, strlen(s)); - } - - scgi_env_add(env_headers, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1")); - - len = LI_ltostr(buf, -#ifdef HAVE_IPV6 - ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) -#else - ntohs(srv_sock->addr.ipv4.sin_port) -#endif - ); - - scgi_env_add(env_headers, CONST_STR_LEN("SERVER_PORT"), buf, len); - - /* get the server-side of the connection to the client */ - our_addr_len = sizeof(our_addr); - - if (-1 == getsockname(con->sock->fd, &(our_addr.plain), &our_addr_len)) { - s = inet_ntop_cache_get_ip(srv, &(srv_sock->addr)); - } else { - s = inet_ntop_cache_get_ip(srv, &(our_addr)); - } - scgi_env_add(env_headers, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s)); - - len = LI_ltostr(buf, -#ifdef HAVE_IPV6 - ntohs(con->dst_addr.plain.sa_family ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port) -#else - ntohs(con->dst_addr.ipv4.sin_port) -#endif - ); - - scgi_env_add(env_headers, CONST_STR_LEN("REMOTE_PORT"), buf, len); - - s = inet_ntop_cache_get_ip(srv, &(con->dst_addr)); - scgi_env_add(env_headers, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s)); - - if (!buffer_is_empty(con->authed_user)) { - scgi_env_add(env_headers, CONST_STR_LEN("REMOTE_USER"), - CONST_BUF_LEN(con->authed_user)); - } - - /* - * 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) - * For AUTHORIZER mode these headers should be omitted. - */ - - scgi_env_add(env_headers, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path)); - - if (!buffer_is_empty(con->request.pathinfo)) { - scgi_env_add(env_headers, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo)); - - /* PATH_TRANSLATED is only defined if PATH_INFO is set */ - - buffer_copy_string_buffer(p->tmp_buf, con->physical.doc_root); - buffer_append_string_buffer(p->tmp_buf, con->request.pathinfo); - scgi_env_add(env_headers, CONST_STR_LEN("PATH_TRANSLATED"), CONST_BUF_LEN(p->tmp_buf)); - } else { - scgi_env_add(env_headers, CONST_STR_LEN("PATH_INFO"), CONST_STR_LEN("")); - } - - /* - * 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. - * TODO: this code should be checked against cgi.fix_pathinfo php - * parameter. - */ - - if (1) { - scgi_env_add(env_headers, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path)); - scgi_env_add(env_headers, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.doc_root)); - } - - scgi_env_add(env_headers, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri)); - - if (!buffer_is_equal(sess->request_uri, con->request.orig_uri)) { - scgi_env_add(env_headers, CONST_STR_LEN("REDIRECT_URI"), CONST_BUF_LEN(sess->request_uri)); - } - if (!buffer_is_empty(con->uri.query)) { - scgi_env_add(env_headers, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query)); - } else { - scgi_env_add(env_headers, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN("")); - } - - s = get_http_method_name(con->request.http_method); - scgi_env_add(env_headers, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s)); - scgi_env_add(env_headers, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200")); /* if php is compiled with --force-redirect */ - s = get_http_version_name(con->request.http_version); - scgi_env_add(env_headers, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s)); - -#ifdef USE_OPENSSL - if (srv_sock->is_ssl) { - scgi_env_add(env_headers, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on")); - } -#endif - - return 0; -} - -/** - * transform the HTTP-Request headers into CGI notation - */ -static int proxy_scgi_get_env_request(server *srv, proxy_session *sess, buffer *env_headers) { - connection *con = sess->remote_con; - plugin_data *p = sess->p; - size_t i; - - UNUSED(srv); - - /* the request header got already copied into the sess->request_headers for us - * no extra filter is needed - * - * prepend a HTTP_ and uppercase the keys - */ - for (i = 0; i < sess->request_headers->used; i++) { - data_string *ds; - size_t j; - - ds = (data_string *)sess->request_headers->data[i]; - if (ds->value->used == 0 || ds->key->used == 0) continue; - - buffer_reset(p->tmp_buf); - - if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { - buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_")); - p->tmp_buf->used--; - } - - buffer_prepare_append(p->tmp_buf, ds->key->used + 2); - for (j = 0; j < ds->key->used - 1; j++) { - char c = ds->key->ptr[j]; - if (light_isalpha(c)) { - /* upper-case */ - c = toupper(c); - } else if (light_isdigit(c)) { - /* copy */ - c = c; - } else { - c = '_'; - } - p->tmp_buf->ptr[p->tmp_buf->used++] = c; - } - p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; - - scgi_env_add(env_headers, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); - } - - for (i = 0; i < con->environment->used; i++) { - data_string *ds; - size_t j; - - ds = (data_string *)con->environment->data[i]; - if (ds->value->used == 0 || ds->key->used == 0) continue; - - buffer_reset(p->tmp_buf); - - buffer_prepare_append(p->tmp_buf, ds->key->used + 2); - for (j = 0; j < ds->key->used - 1; j++) { - char c = ds->key->ptr[j]; - if (light_isalpha(c)) { - /* upper-case */ - c = toupper(c); - } else { - c = '_'; - } - p->tmp_buf->ptr[p->tmp_buf->used++] = c; - } - p->tmp_buf->ptr[p->tmp_buf->used++] = '\0'; - - scgi_env_add(env_headers, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value)); - } - - return 0; -} - -PROXY_STREAM_ENCODER_FUNC(proxy_scgi_encode_request_headers) { - proxy_connection *proxy_con = sess->proxy_con; - chunkqueue *out = proxy_con->send; - size_t headers_len = 0; - buffer *len_buf; - buffer *env_headers; - - UNUSED(in); - - /* get buffer to write headers length into */ - len_buf = chunkqueue_get_append_buffer(out); - - /* write SCGI request headers */ - env_headers = chunkqueue_get_append_buffer(out); - buffer_prepare_copy(env_headers, 1024); - proxy_scgi_get_env_scgi(srv, sess, env_headers); - proxy_scgi_get_env_request(srv, sess, env_headers); - headers_len = env_headers->used; - out->bytes_in += headers_len; - - /* append "," after headers */ - buffer_append_memory(env_headers, CONST_STR_LEN(",")); - env_headers->used++; /* this is needed because the network will only write "used - 1" bytes */ - out->bytes_in++; - - /* prepend [headers_len]: */ - buffer_append_long(len_buf, headers_len); - buffer_append_string_len(len_buf, CONST_STR_LEN(":")); - out->bytes_in += len_buf->used - 1; - - return HANDLER_FINISHED; -} - -/** - * parse the HTTP response header - */ -static handler_t proxy_scgi_parse_response_headers(proxy_session *sess, chunkqueue *in) { - http_response_reset(sess->resp); - - /* http response parser. */ - switch(http_response_parse_cq(in, sess->resp)) { - case PARSE_ERROR: - /* bad gateway */ - http_response_reset(sess->resp); - sess->have_response_headers = 1; - sess->resp->status = 502; - return HANDLER_ERROR; - case PARSE_NEED_MORE: - return HANDLER_GO_ON; - case PARSE_SUCCESS: - default: - /* finished parsing response headers. */ - sess->have_response_headers = 1; - return HANDLER_FINISHED; - } -} - -PROXY_STREAM_DECODER_FUNC(proxy_scgi_stream_decoder) { - proxy_connection *proxy_con = sess->proxy_con; - chunkqueue *in = proxy_con->recv; - chunk *c; - - UNUSED(srv); - - if (in->first == NULL) { - if ((sess->content_length >= 0 && sess->bytes_read == sess->content_length) || in->is_closed) { - sess->is_request_finished = 1; - return HANDLER_FINISHED; - } - - return HANDLER_GO_ON; - } - - /* parse response headers. */ - if (!sess->have_response_headers) { - handler_t rc = proxy_scgi_parse_response_headers(sess, in); - if (rc != HANDLER_FINISHED) return rc; - } - - /* no chunked encoding, ok, perhaps a content-length ? */ - - chunkqueue_remove_finished_chunks(in); - for (c = in->first; c; c = c->next) { - buffer *b; - - if (c->mem->used == 0) continue; - - out->bytes_in += c->mem->used - c->offset - 1; - in->bytes_out += c->mem->used - c->offset - 1; - - sess->bytes_read += c->mem->used - c->offset - 1; - - if (c->offset == 0) { - /* we are copying the whole buffer, just steal it */ - - chunkqueue_steal_chunk(out, c); - } else { - b = chunkqueue_get_append_buffer(out); - buffer_copy_string_len(b, c->mem->ptr + c->offset, c->mem->used - c->offset - 1); - c->offset = c->mem->used - 1; /* marks is read */ - } - - if (sess->bytes_read == sess->content_length) { - break; - } - } - - if (in->is_closed || sess->bytes_read == sess->content_length) { - sess->is_request_finished = 1; - return HANDLER_FINISHED; /* finished */ - } - - return HANDLER_GO_ON; -} - -/** - * transform the content-stream into a valid HTTP-content-stream - * - * as we don't apply chunked-encoding here, pass it on AS IS - */ -PROXY_STREAM_ENCODER_FUNC(proxy_scgi_stream_encoder) { - proxy_connection *proxy_con = sess->proxy_con; - chunkqueue *out = proxy_con->send; - int we_have = 0; - - UNUSED(srv); - - /* output queue closed, can't encode any more data. */ - if(out->is_closed) return HANDLER_FINISHED; - - /* encode all request content data into output queue. */ - we_have = chunkqueue_steal_all_chunks(out, in); - in->bytes_out += we_have; - out->bytes_in += we_have; - - if (in->bytes_in == in->bytes_out && in->is_closed) { - out->is_closed = 1; - return HANDLER_FINISHED; - } - - return HANDLER_GO_ON; -} - -INIT_FUNC(mod_proxy_backend_scgi_init) { - mod_proxy_core_plugin_data *core_data; - protocol_plugin_data *p; - - /* get the plugin_data of the core-plugin */ - core_data = plugin_get_config(srv, CORE_PLUGIN); - if(!core_data) return NULL; - - p = calloc(1, sizeof(*p)); - - /* define protocol handler callbacks */ - p->protocol = core_data->proxy_register_protocol("scgi"); - - /* - p->protocol->proxy_stream_init = proxy_scgi_init; - p->protocol->proxy_stream_cleanup = proxy_scgi_cleanup; - */ - p->protocol->proxy_stream_decoder = proxy_scgi_stream_decoder; - p->protocol->proxy_stream_encoder = proxy_scgi_stream_encoder; - p->protocol->proxy_encode_request_headers = proxy_scgi_encode_request_headers; - - return p; -} - -FREE_FUNC(mod_proxy_backend_scgi_free) { - protocol_plugin_data *p = p_d; - - UNUSED(srv); - - if (!p) return HANDLER_GO_ON; - - free(p); - - return HANDLER_GO_ON; -} - -LI_EXPORT int mod_proxy_backend_scgi_plugin_init(plugin *p); -LI_EXPORT int mod_proxy_backend_scgi_plugin_init(plugin *p) { - data_string *ds; - - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("mod_proxy_backend_scgi"); - - p->init = mod_proxy_backend_scgi_init; - p->cleanup = mod_proxy_backend_scgi_free; - - p->data = NULL; - - ds = data_string_init(); - buffer_copy_string_len(ds->value, CONST_STR_LEN(CORE_PLUGIN)); - array_insert_unique(p->required_plugins, (data_unset *)ds); - - return 0; -} - - diff --git a/src/mod_proxy_core.c b/src/mod_proxy_core.c deleted file mode 100644 index 31f884a7..00000000 --- a/src/mod_proxy_core.c +++ /dev/null @@ -1,2624 +0,0 @@ -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> -#include <errno.h> -#include <ctype.h> -#include <assert.h> -#include <fcntl.h> -#include "base.h" -#include "sys-strings.h" - -#include "buffer.h" -#include "array.h" -#include "log.h" - -#include "plugin.h" -#include "joblist.h" -#include "sys-files.h" -#include "inet_ntop_cache.h" -#include "crc32.h" -#include "configfile.h" -#include "stat_cache.h" -#include "buffer.h" -#include "array.h" -#include "log.h" -#include "status_counter.h" - -#include "mod_proxy_core.h" -#include "mod_proxy_core_protocol.h" - -#define PROXY_CORE "proxy-core" -#define CONFIG_PROXY_CORE_BALANCER PROXY_CORE ".balancer" -#define CONFIG_PROXY_CORE_PROTOCOL PROXY_CORE ".protocol" -#define CONFIG_PROXY_CORE_DEBUG PROXY_CORE ".debug" -#define CONFIG_PROXY_CORE_MAX_KEEP_ALIVE PROXY_CORE ".max-keep-alive-requests" -#define CONFIG_PROXY_CORE_BACKENDS PROXY_CORE ".backends" -#define CONFIG_PROXY_CORE_REWRITE_REQUEST PROXY_CORE ".rewrite-request" -#define CONFIG_PROXY_CORE_REWRITE_RESPONSE PROXY_CORE ".rewrite-response" -#define CONFIG_PROXY_CORE_ALLOW_X_SENDFILE PROXY_CORE ".allow-x-sendfile" -#define CONFIG_PROXY_CORE_ALLOW_X_REWRITE PROXY_CORE ".allow-x-rewrite" -#define CONFIG_PROXY_CORE_MAX_POOL_SIZE PROXY_CORE ".max-pool-size" -#define CONFIG_PROXY_CORE_CHECK_LOCAL PROXY_CORE ".check-local" -#define CONFIG_PROXY_CORE_SPLIT_HOSTNAMES PROXY_CORE ".split-hostnames" -#define CONFIG_PROXY_CORE_DISABLE_TIME PROXY_CORE ".disable-time" -#define CONFIG_PROXY_CORE_MAX_BACKLOG_SIZE PROXY_CORE ".max-backlog-size" - -static int mod_proxy_wakeup_connections(server *srv, plugin_data *p, plugin_config *p_conf); - -static int array_insert_int(array *a, const char *key, int val) { - data_integer *di; - - if (NULL == (di = (data_integer *)array_get_unused_element(a, TYPE_INTEGER))) { - di = data_integer_init(); - } - - buffer_copy_string(di->key, key); - di->value = val; - array_insert_unique(a, (data_unset *)di); - - return 0; -} - -static proxy_protocol *mod_proxy_core_register_protocol(const char *name) { - proxy_protocol *protocol = proxy_protocol_init(); - - protocol->name = buffer_init_string(name); - - proxy_protocols_register(protocol); - return protocol; -} - -INIT_FUNC(mod_proxy_core_init) { - plugin_data *p; - - UNUSED(srv); - - proxy_protocols_init(); - - p = calloc(1, sizeof(*p)); - - /* create some backends as long as we don't have the config-parser */ - - p->possible_balancers = array_init(); - array_insert_int(p->possible_balancers, "sqf", PROXY_BALANCE_SQF); - array_insert_int(p->possible_balancers, "carp", PROXY_BALANCE_CARP); - array_insert_int(p->possible_balancers, "round-robin", PROXY_BALANCE_RR); - array_insert_int(p->possible_balancers, "static", PROXY_BALANCE_STATIC); - - p->proxy_register_protocol = mod_proxy_core_register_protocol; - - /* statistics counters. */ - p->request_count = status_counter_get_counter(CONST_STR_LEN(PROXY_CORE ".requests")); - - p->balance_buf = buffer_init(); - p->protocol_buf = buffer_init(); - p->replace_buf = buffer_init(); - p->backends_arr = array_init(); - - p->tmp_buf = buffer_init(); - -#if 0 - /** - * create a small pool of session objects - * - * instead of creating new one each time, - * cleanup old ones and put them into the pool - * - * 8 clean items should be enough at a time, destroy the other ones - */ - p->session_pool = proxy_session_pool_init(); -#endif - - return p; -} - -FREE_FUNC(mod_proxy_core_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 (!s) continue; - - proxy_backends_free(s->backends); - proxy_backlog_free(s->backlog); - - proxy_rewrites_free(s->request_rewrites); - proxy_rewrites_free(s->response_rewrites); - - free(s); - } - free(p->config_storage); - } - - array_free(p->possible_balancers); - array_free(p->backends_arr); - - buffer_free(p->balance_buf); - buffer_free(p->protocol_buf); - buffer_free(p->replace_buf); - buffer_free(p->tmp_buf); - -#if 0 - proxy_session_pool_free(p->session_pool); -#endif - - free(p); - - proxy_protocols_free(); - - return HANDLER_GO_ON; -} - -static handler_t mod_proxy_core_config_parse_rewrites(proxy_rewrites *dest, array *src, const char *config_key) { - data_unset *du; - size_t j; - - if (NULL != (du = array_get_element(src, config_key, strlen(config_key)))) { - data_array *keys = (data_array *)du; - - if (keys->type != TYPE_ARRAY) { - ERROR("%s = <...>", - config_key); - - return HANDLER_ERROR; - } - - /* - * proxy-core.rewrite-request = ( - * "_uri" => ( ... ) - * ) - */ - - for (j = 0; j < keys->value->used; j++) { - size_t k; - data_array *headers = (data_array *)keys->value->data[j]; - - /* keys->key should be "_uri" and the value a array of rewrite */ - if (headers->type != TYPE_ARRAY) { - ERROR("%s = ( %s => <...> ) has to a array", - config_key, - SAFE_BUF_STR(headers->key)); - - return HANDLER_ERROR; - } - - if (headers->value->used > 1) { - ERROR("%s = ( %s => <...> ) has to a array with only one element", - config_key, - SAFE_BUF_STR(headers->key)); - - return HANDLER_ERROR; - - } - - for (k = 0; k < headers->value->used; k++) { - data_string *rewrites = (data_string *)headers->value->data[k]; - proxy_rewrite *rw; - - /* keys->key should be "_uri" and the value a array of rewrite */ - if (rewrites->type != TYPE_STRING) { - ERROR("%s = ( \"%s\" => ( \"%s\" => <value> ) ) has to a string", - config_key, - SAFE_BUF_STR(headers->key), - SAFE_BUF_STR(rewrites->key)); - - return HANDLER_ERROR; - } - - rw = proxy_rewrite_init(); - - if (0 != proxy_rewrite_set_regex(rw, rewrites->key)) { - return HANDLER_ERROR; - } - buffer_copy_string_buffer(rw->replace, rewrites->value); - buffer_copy_string_buffer(rw->match, rewrites->key); - buffer_copy_string_buffer(rw->header, headers->key); - - proxy_rewrites_add(dest, rw); - } - } - } - - return HANDLER_GO_ON; -} - -static void mod_proxy_core_create_backend_stats(plugin_data *p, buffer *stat_basename, proxy_backend *backend) { -#define COUNTER_NAME(b, x) \ - buffer_copy_string_buffer(b, stat_basename); \ - buffer_append_string_len(b, CONST_STR_LEN("\"")); \ - buffer_append_string_buffer(b, backend->name); \ - buffer_append_string_len(b, CONST_STR_LEN("\"." x)); - - /* request count stat. */ - COUNTER_NAME(p->tmp_buf, "requests"); - backend->request_count = status_counter_get_counter(CONST_BUF_LEN(p->tmp_buf)); - - /* load */ - COUNTER_NAME(p->tmp_buf, "load"); - backend->load = status_counter_get_counter(CONST_BUF_LEN(p->tmp_buf)); - - /* pool size */ - COUNTER_NAME(p->tmp_buf, "pool_size"); - backend->pool_size = status_counter_get_counter(CONST_BUF_LEN(p->tmp_buf)); - - COUNTER_NAME(p->tmp_buf, "requests_failed"); - backend->requests_failed = status_counter_get_counter(CONST_BUF_LEN(p->tmp_buf)); -#undef COUNTER_NAME -} - -SETDEFAULTS_FUNC(mod_proxy_core_set_defaults) { - plugin_data *p = p_d; - buffer *stat_basename; - size_t i, j; - int proxy_counter = 0; - - config_values_t cv[] = { - { CONFIG_PROXY_CORE_BACKENDS, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { CONFIG_PROXY_CORE_DEBUG, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { CONFIG_PROXY_CORE_BALANCER, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - { CONFIG_PROXY_CORE_PROTOCOL, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ - { CONFIG_PROXY_CORE_REWRITE_REQUEST, NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ - { CONFIG_PROXY_CORE_REWRITE_RESPONSE, NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ - { CONFIG_PROXY_CORE_ALLOW_X_SENDFILE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ - { CONFIG_PROXY_CORE_ALLOW_X_REWRITE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ - { CONFIG_PROXY_CORE_MAX_POOL_SIZE, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ - { CONFIG_PROXY_CORE_CHECK_LOCAL, "use $PHYSICAL[\"existing-path\"] =~ ... { ... } instead", - T_CONFIG_DEPRECATED, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ - { CONFIG_PROXY_CORE_MAX_KEEP_ALIVE, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ - { CONFIG_PROXY_CORE_SPLIT_HOSTNAMES, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ - { CONFIG_PROXY_CORE_DISABLE_TIME, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ - { CONFIG_PROXY_CORE_MAX_BACKLOG_SIZE, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - stat_basename = buffer_init(); - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - array *ca; - proxy_backend *backend; - - array_reset(p->backends_arr); - buffer_reset(p->balance_buf); - buffer_reset(p->protocol_buf); - - s = calloc(1, sizeof(plugin_config)); - s->debug = 0; - s->balancer = PROXY_BALANCE_UNSET; - s->protocol = NULL; - s->backends = proxy_backends_init(); - s->backlog = proxy_backlog_init(); - s->response_rewrites = proxy_rewrites_init(); - s->request_rewrites = proxy_rewrites_init(); - s->check_local = 0; - s->split_hostnames = 1; - s->max_keep_alive_requests = 0; - s->disable_time = 1; - s->max_backlog_size = 4; - - cv[0].destination = p->backends_arr; - cv[1].destination = &(s->debug); - cv[2].destination = p->balance_buf; /* parse into a constant */ - cv[3].destination = p->protocol_buf; /* parse into a constant */ - cv[6].destination = &(s->allow_x_sendfile); - cv[7].destination = &(s->allow_x_rewrite); - cv[8].destination = &(s->max_pool_size); - cv[10].destination = &(s->max_keep_alive_requests); - cv[11].destination = &(s->split_hostnames); - cv[12].destination = &(s->disable_time); - cv[13].destination = &(s->max_backlog_size); - - buffer_reset(p->balance_buf); - - p->config_storage[i] = s; - ca = ((data_config *)srv->config_context->data[i])->value; - - if (0 != config_insert_values_global(srv, ca, cv)) { - return HANDLER_ERROR; - } - - if (!buffer_is_empty(p->balance_buf)) { - data_integer *di; - - if (NULL != (di = (data_integer *)array_get_element(p->possible_balancers, CONST_BUF_LEN(p->balance_buf)))) { - s->balancer = di->value; - } else { - ERROR("proxy.balance has to be one of 'round-robin', 'carp', 'sqf', 'static': got %s", SAFE_BUF_STR(p->balance_buf)); - return HANDLER_ERROR; - } - } - - if (!buffer_is_empty(p->protocol_buf)) { - proxy_protocol *protocol = NULL; - if (NULL == (protocol = proxy_get_protocol(p->protocol_buf))) { - ERROR("proxy.protocol has to be one of { %s } got %s, you might have to load 'mod_proxy_backend_%s'", - proxy_available_protocols(), - SAFE_BUF_STR(p->protocol_buf), - SAFE_BUF_STR(p->protocol_buf) - ); - return HANDLER_ERROR; - } - s->protocol = protocol; - } - - if (p->backends_arr->used) { - /* statistics base name. */ - buffer_copy_string_len(stat_basename, CONST_STR_LEN(PROXY_CORE ".")); - buffer_append_long(stat_basename, proxy_counter); - - /* backlog size stats */ - buffer_copy_string_buffer(p->tmp_buf, stat_basename); - buffer_append_string_len(p->tmp_buf, CONST_STR_LEN(".backlogged")); - s->backlog_size = status_counter_get_counter(CONST_BUF_LEN(p->tmp_buf)); - - /* backends stats base name. */ - buffer_append_string_len(stat_basename, CONST_STR_LEN(".backends.")); - - /* check if the backends have a valid host-name */ - for (j = 0; j < p->backends_arr->used; j++) { - data_string *ds = (data_string *)p->backends_arr->data[j]; - backend = proxy_backend_init(); - - /* save name of backend for config. */ - buffer_copy_string_buffer(backend->name, ds->value); - /* the values should be ips or hostnames */ - if (0 != proxy_address_pool_add_string(backend->address_pool, ds->value)) { - return HANDLER_ERROR; - } - - if (s->max_pool_size) { - backend->pool->max_size = s->max_pool_size; - } - - /* append backend to list of backends */ - proxy_backends_add(s->backends, backend); - - /* if there is more then one address in the pool, split them out into seperate backends */ - if (s->split_hostnames && backend->address_pool->used > 1) { - proxy_address_pool *pool = backend->address_pool; - - /* change current backend's name to name of the first ip address in the pool. */ - buffer_copy_string_buffer(backend->name, pool->ptr[0]->name); - - /* setup stats */ - mod_proxy_core_create_backend_stats(p, stat_basename, backend); - - /* move all addresses from the pool into new backends, except for the first one */ - while (pool->used > 1) { - /* remove last address from pool */ - proxy_address *address = pool->ptr[--(pool->used)]; - - /* create new backend for address */ - backend = proxy_backend_init(); - - /* set backend name to name of address */ - buffer_copy_string_buffer(backend->name, address->name); - - /* setup stats */ - mod_proxy_core_create_backend_stats(p, stat_basename, backend); - - /* add address to pool */ - proxy_address_pool_add(backend->address_pool, address); - - if (s->max_pool_size) { - backend->pool->max_size = s->max_pool_size; - } - - /* append backend to list of backends */ - proxy_backends_add(s->backends, backend); - } - } else { - /* setup stats */ - mod_proxy_core_create_backend_stats(p, stat_basename, backend); - } - } - /* counter number of "proxy-core.backends" groups */ - proxy_counter++; - } - - if (HANDLER_GO_ON != mod_proxy_core_config_parse_rewrites(s->request_rewrites, ca, CONFIG_PROXY_CORE_REWRITE_REQUEST)) { - return HANDLER_ERROR; - } - - if (HANDLER_GO_ON != mod_proxy_core_config_parse_rewrites(s->response_rewrites, ca, CONFIG_PROXY_CORE_REWRITE_RESPONSE)) { - return HANDLER_ERROR; - } - } - - buffer_free(stat_basename); - - return HANDLER_GO_ON; -} - - -static proxy_session *proxy_session_init(void) { - proxy_session *sess; - - sess = calloc(1, sizeof(*sess)); - - sess->state = PROXY_STATE_UNSET; - sess->request_uri = buffer_init(); - sess->request_headers = array_init(); - sess->env_headers = array_init(); - - sess->resp = http_response_init(); - - sess->recv = chunkqueue_init(); - - sess->is_chunked = 0; - sess->content_length = -1; - sess->send_response_content = 1; - sess->do_new_session = 0; - sess->do_x_rewrite_backend = 0; - sess->sticky_session = NULL; - - return sess; -} - -static void proxy_session_reset(proxy_session *sess) { - if (!sess) return; - - buffer_reset(sess->request_uri); - array_reset(sess->request_headers); - array_reset(sess->env_headers); - - http_response_reset(sess->resp); - sess->p = NULL; - - chunkqueue_reset(sess->recv); - - sess->state = PROXY_STATE_UNSET; - - sess->is_chunked = 0; - sess->send_response_content = 1; - - sess->bytes_read = 0; - sess->connect_start_ts = 0; - sess->content_length = -1; - sess->internal_redirect_count = 0; - sess->do_internal_redirect = 0; - sess->is_closing = 0; - sess->is_closed = 0; - sess->is_request_finished = 0; - sess->have_response_headers = 0; - - sess->do_new_session = 0; - sess->do_x_rewrite_backend = 0; - buffer_free(sess->sticky_session); - sess->sticky_session = NULL; - - sess->remote_con = NULL; - sess->proxy_con = NULL; - sess->proxy_backend = NULL; -} - -static void proxy_session_free(proxy_session *sess) { - if (!sess) return; - - buffer_free(sess->request_uri); - array_free(sess->request_headers); - array_free(sess->env_headers); - - http_response_free(sess->resp); - sess->p = NULL; - - chunkqueue_free(sess->recv); - - buffer_free(sess->sticky_session); - free(sess); -} - -/** - * Copy decoded response content to client connection. - */ -static int proxy_copy_response(server *srv, connection *con, proxy_session *sess) { - chunk *c; - int we_have = 0; - - UNUSED(srv); - - chunkqueue_remove_finished_chunks(sess->recv); - /* copy the content to the next cq */ - for (c = sess->recv->first; c; c = c->next) { - if (c->mem->used == 0) continue; - - we_have = c->mem->used - c->offset - 1; - sess->recv->bytes_out += we_have; - if (sess->send_response_content) { - con->send->bytes_in += we_have; - /* X-Sendfile ignores the content-body */ - chunkqueue_steal_chunk(con->send, c); - } else { - /* discard the data */ - chunk_set_done(c); - } - } - chunkqueue_remove_finished_chunks(sess->recv); - - if(sess->recv->is_closed && sess->send_response_content) { - con->send->is_closed = 1; - } - return 0; -} - -/** - * Initialize protocol stream. - * - */ -static int proxy_stream_init(server *srv, proxy_session *sess) { - proxy_protocol *protocol = (sess->proxy_backend) ? sess->proxy_backend->protocol : NULL; - if(protocol && protocol->proxy_stream_init) { - return (protocol->proxy_stream_init)(srv, sess->proxy_con); - } - return 1; -} - -/** - * Cleanup protocol state data. - * - */ -static int proxy_stream_cleanup(server *srv, proxy_session *sess) { - proxy_protocol *protocol = (sess->proxy_backend) ? sess->proxy_backend->protocol : NULL; - if(protocol && protocol->proxy_stream_cleanup) { - return (protocol->proxy_stream_cleanup)(srv, sess->proxy_con); - } - return 1; -} - -/** - * decode the content for the protocol - * - * http might have chunk-encoding - * fastcgi has the fastcgi wrapper code - * - * @param out chunkqueue for the plain content - */ - -static handler_t proxy_stream_decoder(server *srv, proxy_session *sess, chunkqueue *out) { - proxy_protocol *protocol = (sess->proxy_backend) ? sess->proxy_backend->protocol : NULL; - if(protocol && protocol->proxy_stream_decoder) { - return (protocol->proxy_stream_decoder)(srv, sess, out); - } - ERROR("protocol '%s' is not supported yet", SAFE_BUF_STR(protocol->name)); - return HANDLER_ERROR; -} - -/** - * encode the content for the protocol - * - * @param in chunkqueue with the content to (no encoding) - */ -static handler_t proxy_stream_encoder(server *srv, proxy_session *sess, chunkqueue *in) { - proxy_protocol *protocol = (sess->proxy_backend) ? sess->proxy_backend->protocol : NULL; - if(protocol && protocol->proxy_stream_encoder) { - return (protocol->proxy_stream_encoder)(srv, sess, in); - } - ERROR("protocol '%s' is not supported yet", SAFE_BUF_STR(protocol->name)); - return HANDLER_ERROR; -} - -/** - * encode the request for the protocol - * - * @param in chunkqueue with the content to (no encoding) - * @param out chunkqueue for the encoded, protocol specific data - */ -static handler_t proxy_encode_request_headers(server *srv, proxy_session *sess, chunkqueue *in) { - proxy_protocol *protocol = (sess->proxy_backend) ? sess->proxy_backend->protocol : NULL; - if(protocol && protocol->proxy_encode_request_headers) { - /* reset proxy connection queues before we encode a new request. - */ - chunkqueue_reset(sess->proxy_con->send); - chunkqueue_reset(sess->proxy_con->recv); - return (protocol->proxy_encode_request_headers)(srv, sess, in); - } - - if (protocol == NULL) { - ERROR("protocol is not set: %s", ""); - } else { - ERROR("protocol '%s' is not supported yet", SAFE_BUF_STR(protocol->name)); - } - return HANDLER_ERROR; -} - -static handler_t proxy_handle_response_headers(server *srv, connection *con, plugin_data *p, - proxy_session *sess, chunkqueue *out) { - int have_content_length = 0; - int do_x_rewrite = 0; - size_t i; - - /* finished parsing http response headers from backend, now prepare http response headers - * for client response. - */ - sess->content_length = -1; - con->http_status = sess->resp->status; - - /* copy the http-headers */ - for (i = 0; i < sess->resp->headers->used; i++) { - const char *ign[] = { "Status", NULL }; - size_t j, k; - data_string *ds; - - data_string *header = (data_string *)sess->resp->headers->data[i]; - - /* some headers are ignored by default */ - for (j = 0; ign[j]; j++) { - if (0 == strcasecmp(ign[j], header->key->ptr)) break; - } - if (ign[j]) continue; - - if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("Location"))) { - /* CGI/1.1 rev 03 - 7.2.1.2 */ - if (con->http_status == 0) con->http_status = 302; - } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("Content-Length"))) { - have_content_length = 1; - - sess->content_length = strtol(header->value->ptr, NULL, 10); - - if (sess->content_length < 0) { - return HANDLER_ERROR; - } - con->response.content_length = sess->content_length; - /* don't save this header, other modules might change the content length. */ - continue; - } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("X-Sendfile")) || - 0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("X-LIGHTTPD-send-file"))) { - if (p->conf.allow_x_sendfile) { - sess->send_response_content = 0; - sess->do_internal_redirect = 1; - - /* don't try to rewrite this request through mod_proxy_core again */ - sess->internal_redirect_count = MAX_INTERNAL_REDIRECTS; - - buffer_copy_string_buffer(con->physical.path, header->value); - - /* as we want to support ETag and friends we set the physical path for the file - * and hope mod_staticfile catches up */ - } - - continue; - } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("X-LIGHTTPD-send-tempfile"))) { - if (p->conf.allow_x_sendfile && !buffer_is_empty(header->value)) { - stat_cache_entry *sce = NULL; - - if (HANDLER_ERROR != stat_cache_get_entry(srv, con, header->value, &sce)) { - chunk *c; - sess->send_response_content = 0; - - if(sce->st.st_size > 0) { - chunkqueue_append_file(con->send, header->value, 0, sce->st.st_size); - con->send->bytes_in += sce->st.st_size; - c = con->send->last; - c->file.is_temp = 1; - } else { - if(unlink(BUF_STR(header->value)) < 0) { - ERROR("Failed to delete empty tempfile: file=%s, error: %s", SAFE_BUF_STR(header->value), strerror(errno)); - } - } - con->response.content_length = sce->st.st_size; - have_content_length = 1; - con->send->is_closed = 1; - } else { - ERROR("Failed to send tempfile: file=%s, error: %s", SAFE_BUF_STR(header->value), strerror(errno)); - } - } - - continue; - } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("X-Rewrite-URI"))) { - if (p->conf.allow_x_rewrite) { - do_x_rewrite = 1; - buffer_copy_string_buffer(con->request.uri, header->value); - } - - continue; - } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("X-Rewrite-Host"))) { - if (p->conf.allow_x_rewrite) { - do_x_rewrite = 1; - buffer_copy_string_buffer(con->request.http_host, header->value); - /* replace Host request header */ - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Host")))) { - buffer_copy_string_buffer(ds->value, header->value); - } else { - /* insert Host request header */ - if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { - ds = data_response_init(); - } - buffer_copy_string_len(ds->key, CONST_STR_LEN("Host")); - buffer_copy_string_buffer(ds->value, header->value); - array_insert_unique(con->request.headers, (data_unset *)ds); - } - } - - continue; - } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("X-Rewrite-Backend"))) { - if (p->conf.allow_x_rewrite) { - do_x_rewrite = 1; - if (!sess->sticky_session) sess->sticky_session = buffer_init(); - buffer_copy_string_buffer(sess->sticky_session, header->value); - sess->do_x_rewrite_backend = 1; - } - - continue; - } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("Transfer-Encoding"))) { - if (strstr(header->value->ptr, "chunked")) { - sess->is_chunked = 1; - } - /* ignore the header */ - continue; - } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(header->key), CONST_STR_LEN("Connection"))) { - if (strstr(header->value->ptr, "close")) { - sess->is_closing = 1; - } - /* ignore the header */ - continue; - - } - - if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { - ds = data_response_init(); - } - - - buffer_copy_string_buffer(ds->key, header->key); - -#ifdef HAVE_PCRE_H - for (k = 0; k < p->conf.response_rewrites->used; k++) { - proxy_rewrite *rw = p->conf.response_rewrites->ptr[k]; - - if (buffer_is_equal(rw->header, header->key)) { - int ret; - - if ((ret = pcre_replace(rw->regex, rw->replace, header->value, p->replace_buf)) < 0) { - switch (ret) { - case PCRE_ERROR_NOMATCH: - /* hmm, ok. no problem */ - buffer_append_string_buffer(ds->value, header->value); - break; - default: - TRACE("oops, pcre_replace failed with: %d", ret); - break; - } - } else { - buffer_append_string_buffer(ds->value, p->replace_buf); - } - - break; - } - } - - if (k == p->conf.response_rewrites->used) { - buffer_copy_string_buffer(ds->value, header->value); - } -#else - buffer_copy_string_buffer(ds->value, header->value); -#endif - - array_insert_unique(con->response.headers, (data_unset *)ds); - } - - if (do_x_rewrite) { - sess->send_response_content = 0; - sess->do_internal_redirect = 1; - sess->do_new_session = 1; - sess->is_chunked = 0; - con->http_status = 0; - sess->content_length = -1; - con->response.content_length = -1; - - /* we are restarting the whole request, reset all the response headers */ - array_reset(con->response.headers); - - buffer_reset(con->physical.path); - - config_cond_cache_reset(srv, con); - } - - /* we are finished decoding the response headers. */ - if(!out->is_closed) { - /* We don't have all the response content try to enable chunked encoding. */ - /* does the client allow us to send chunked encoding ? */ - if (con->request.http_version == HTTP_VERSION_1_1 && - !have_content_length) { - con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; - } - } - - /* we might have part of the response content too */ - proxy_copy_response(srv, con, sess); - - return HANDLER_FINISHED; /* we have a full header */ -} - -/* - * if there is data in the send queue enable FDEVENT_OUT & FDEVENT_IN. - * else just enable FDEVENT_IN. - */ -static int proxy_connection_enable_events(server *srv, proxy_connection *proxy_con) { - int events = FDEVENT_IN; - if (proxy_con->send->bytes_out < proxy_con->send->bytes_in) events |= FDEVENT_OUT; - return fdevent_event_add(srv->ev, proxy_con->sock, events); -} - -/** - * encode/decode stream data from backend connection. - * - */ -static handler_t proxy_stream_encode_decode(server *srv, proxy_session *sess) { - //proxy_connection *proxy_con = sess->proxy_con; - connection *con = sess->remote_con; - - if (!sess->recv->is_closed) { - /* call stream-decoder (HTTP-chunked, FastCGI, ... ) */ - switch (proxy_stream_decoder(srv, sess, sess->recv)) { - case HANDLER_FINISHED: - /* finished decoding reponse. */ - /* we are done, close the response content queue */ - sess->recv->is_closed = 1; - break; - case HANDLER_GO_ON: - break; - case HANDLER_ERROR: - ERROR("%s", "stream decoder failed."); - /* error */ - return HANDLER_ERROR; - default: - TRACE("stream-decoder: %s", "foo"); - break; - } - } - - /* encode request content. */ - switch(proxy_stream_encoder(srv, sess, con->recv)) { - case HANDLER_FINISHED: - /* finished encoding request content. */ - break; - case HANDLER_GO_ON: - break; - case HANDLER_ERROR: - ERROR("%s", "stream encoder failed."); - /* error */ - return HANDLER_ERROR; - default: - TRACE("stream-encoder: %s", "foo"); - break; - } - chunkqueue_remove_finished_chunks(con->recv); - - if (!sess->is_closed) { - /* enable FDEVENT_OUT if there is data to send. */ - proxy_connection_enable_events(srv, sess->proxy_con); - } - - return HANDLER_GO_ON; -} - -static handler_t proxy_connection_connect(proxy_connection *con) { - int fd; -#ifdef _WIN32 - int io_ctl = 1; - int win_err = 0; -#endif - - if (-1 == (fd = socket(con->address->addr.plain.sa_family, SOCK_STREAM, 0))) { - switch (errno) { - case EMFILE: - return HANDLER_WAIT_FOR_FD; - default: - ERROR("socket failed: %s (%d)", strerror(errno), errno); - return HANDLER_ERROR; - } - } - -#ifdef O_NONBLOCK - fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR); -#elif defined _WIN32 - ioctlsocket(fd, FIONBIO, &io_ctl); -#endif - - con->sock->fd = fd; - con->sock->fde_ndx = -1; - con->sock->type = IOSOCKET_TYPE_SOCKET; - - if (-1 == connect(fd, &(con->address->addr.plain), con->address->addrlen)) { - switch(light_sock_errno()) { - case EINPROGRESS: - case EALREADY: - case EINTR: -#ifdef _WIN32 - case EWOULDBLOCK: -#endif - return HANDLER_WAIT_FOR_EVENT; - default: - closesocket(fd); - con->sock->fd = -1; - - ERROR("connect(%s) failed: %s (%d)", - SAFE_BUF_STR(con->address->name), - strerror(errno), errno); - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -/** - * event-handler for idling connections - * - * unused (idling) keep-alive connections are not bound to a session - * and need their own event-handler - * - * if the connection closes (we get a FDEVENT_IN), close our side too and - * let the trigger-func handle the cleanup - * - * @see proxy_trigger - */ - - -static handler_t proxy_handle_fdevent_idle(void *s, void *ctx, int revents) { - server *srv = (server *)s; - proxy_connection *proxy_con = ctx; - char buf[4096]; - - if (revents & FDEVENT_IN) { - switch (proxy_con->state) { - case PROXY_CONNECTION_STATE_IDLE: - proxy_con->state = PROXY_CONNECTION_STATE_CLOSED; - - /* close + unregister have to be in the same call, - * otherwise we get a events for a re-opened fd */ - - sockread(proxy_con->sock->fd, buf, sizeof(buf)); - - fdevent_event_del(srv->ev, proxy_con->sock); - - /* we have to notify the pool, that this connection is free now */ - - break; - case PROXY_CONNECTION_STATE_CLOSED: - /* poll() is state-driven, we will get events as long as it isn't disabled - * the close() above should disable the events too */ - ERROR("%s", "hurry up buddy, I got another event for a closed idle-connection"); - break; - default: - ERROR("invalid connection state: %d, should be idle", proxy_con->state); - break; - } - } - - return HANDLER_GO_ON; -} - - -/* don't call any proxy functions directly */ -static handler_t proxy_handle_fdevent(void *s, void *ctx, int revents) { - server *srv = (server *)s; - proxy_session *sess = ctx; - proxy_connection *proxy_con; - connection *con; - int call_append = 1; - - /** - * we might receive a event for a connection that should be closed already - */ - - if (sess == NULL) { - /** - * race - * the other fd of the connection might have been closed already - * mod_proxy_connection_close_callback() frees the - */ - - ERROR("the session ctx is NULL: %p, expect a crash", (void*) sess); - // FIXME: do not dereference an known NULL ??? - } - - proxy_con = sess->proxy_con; - con = sess->remote_con; - - if (revents & FDEVENT_IN) { - chunkqueue_remove_finished_chunks(proxy_con->recv); - switch (srv->network_backend_read(srv, con, proxy_con->sock, proxy_con->recv)) { - case NETWORK_STATUS_CONNECTION_CLOSE: - /* a close here means we can't read/write any more data. */ - sess->is_closed = 1; - proxy_con->send->is_closed = 1; - proxy_con->recv->is_closed = 1; - break; - case NETWORK_STATUS_SUCCESS: - case NETWORK_STATUS_WAIT_FOR_EVENT: - break; - default: - ERROR("%s", "oops, we failed to read"); - break; - } - } - - if (revents & FDEVENT_OUT) { - fdevent_event_add(srv->ev, proxy_con->sock, FDEVENT_IN); - - switch (srv->network_backend_write(srv, con, proxy_con->sock, proxy_con->send)) { - case NETWORK_STATUS_SUCCESS: - break; - case NETWORK_STATUS_WAIT_FOR_AIO_EVENT: - call_append = 0; /* let the joblist-queue-handler call the connection again */ - break; - case NETWORK_STATUS_WAIT_FOR_EVENT: - fdevent_event_add(srv->ev, proxy_con->sock, FDEVENT_IN | FDEVENT_OUT); - break; - case NETWORK_STATUS_CONNECTION_CLOSE: - /* done mark the connection closed here only the send queue, - * since there might be more data to read. - */ - proxy_con->send->is_closed = 1; - break; - default: - ERROR("%s", "oops, we failed to write"); - break; - } - chunkqueue_remove_finished_chunks(proxy_con->send); - } - - if (revents & FDEVENT_HUP) { - if (!(revents & FDEVENT_IN)) { - /* if we only received the FDEVENT_HUP event, then there is no more data to read. */ - sess->is_closed = 1; - proxy_con->recv->is_closed = 1; - } - /* can't write on a closed socket, so close the send queue. */ - proxy_con->send->is_closed = 1; - } - - if (sess->is_closed) { - sess->recv->is_closed = 1; - fdevent_event_del(srv->ev, sess->proxy_con->sock); - } - - /** - * on NETWORK_STATUS_WAIT_FOR_AIO_EVENT we are not allowed to call the state-engine - * the connection has to sleep until our disk-read is finished - * - * the joblist_append() will be called by the joblist_append_queue_handler in server.c - */ - if (call_append) joblist_append(srv, con); - - return HANDLER_GO_ON; -} - -/** - * Cleanup backend proxy connection. - */ -static int proxy_remove_backend_connection(server *srv, proxy_session *sess) { - - if(!sess->proxy_con) return -1; - - /* cleanup protocol stream */ - proxy_stream_cleanup(srv, sess); - - /* remove closed connection from pool. */ - proxy_connection_pool_remove_connection(sess->proxy_backend->pool, sess->proxy_con); - - /* update stats. */ - COUNTER_SET(sess->proxy_backend->pool_size, sess->proxy_backend->pool->used); - COUNTER_DEC(sess->proxy_backend->load); - - /* the backend might have been disabled by a full connection pool, re-enable - * if there is at least one active address. - */ - if (sess->proxy_backend->disabled_addresses <= sess->proxy_backend->address_pool->used) { - sess->proxy_backend->state = PROXY_BACKEND_STATE_ACTIVE; - } - - if (sess->proxy_con->sock->fd != -1) { - /* if we fail in connect() might not have a FD yet */ - fdevent_event_del(srv->ev, sess->proxy_con->sock); - fdevent_unregister(srv->ev, sess->proxy_con->sock); - } - - proxy_connection_free(sess->proxy_con); - sess->proxy_con = NULL; - - return 0; -} - -/** - * Recycle backend proxy connection. - * - * 1. close the connection if keep-alive is disable - * 2. or set the connection idling and wake up a backlogged request. - * - */ -static int proxy_recycle_backend_connection(server *srv, plugin_data *p, proxy_session *sess) { - proxy_request *req; - int reuse = 1; - - if (!sess) return HANDLER_GO_ON; - - if (sess->proxy_con) { - COUNTER_INC(p->request_count); - COUNTER_INC(sess->proxy_backend->request_count); - COUNTER_DEC(sess->proxy_backend->load); - switch (sess->proxy_con->state) { - case PROXY_CONNECTION_STATE_CONNECTED: - /* - * Set the connection to idling if: - * - * 1. keep-alive was not disabled (sess->is_closing) - * 2. backend connection is already closed (sess->is_closed) - * 3. backend protocol finished parsing all data for this request. (sess->recv->is_closed) - * 4. keep-alive request count hasn't reached max-keep-alive-requests - */ - if (sess->is_closing || sess->is_closed) { - reuse = 0; - } - - sess->proxy_con->request_count++; - if (p->conf.debug) TRACE("request_count=%d", sess->proxy_con->request_count); - if (sess->proxy_con->request_count >= p->conf.max_keep_alive_requests) { - reuse = 0; - } - if (reuse && sess->recv->is_closed) { - sess->proxy_con->state = PROXY_CONNECTION_STATE_IDLE; - - /* make sure backend is active since we have a free connection. */ - sess->proxy_backend->state = PROXY_BACKEND_STATE_ACTIVE; - - /* don't ignore events as the FD is idle - * we might get a HUP as the remote connection might close */ - fdevent_event_del(srv->ev, sess->proxy_con->sock); - fdevent_unregister(srv->ev, sess->proxy_con->sock); - - fdevent_register(srv->ev, sess->proxy_con->sock, proxy_handle_fdevent_idle, sess->proxy_con); - fdevent_event_add(srv->ev, sess->proxy_con->sock, FDEVENT_IN); - - break; - } - - /* fall-through for non-keep-alive or response parsing didn't finish */ - - case PROXY_CONNECTION_STATE_CLOSED: - /* user terminated connection and proxy connection is still in CONNECTING mode. - * this happens frequently when backends are slow - */ - case PROXY_CONNECTION_STATE_CONNECTING: - proxy_remove_backend_connection(srv, sess); - case PROXY_CONNECTION_STATE_IDLE: - default: - break; - } - sess->proxy_con = NULL; - } - - /* wake up a connection from the backlog */ - if ((req = proxy_backlog_shift(p->conf.backlog))) { - connection *next_con = req->con; - - if (p->conf.debug) TRACE("wakeup a connection from backlog: con=%d", next_con->sock->fd); - joblist_append(srv, next_con); - - COUNTER_DEC(p->conf.backlog_size); - proxy_request_free(req); - } - - return HANDLER_GO_ON; -} - -/** - * push the session into the backlog - * - * @returns HANDLER_ERROR in case we reach the max-connect-retry limit - */ -static handler_t mod_proxy_core_backlog_connection(server *srv, connection *con, plugin_data *p, proxy_session *sess) { - proxy_request *req; - - if (sess->sent_to_backlog >= p->conf.max_backlog_size) { - return HANDLER_ERROR; - } - - /* connection pool is full, queue the request for now */ - req = proxy_request_init(); - req->added_ts = srv->cur_ts; - req->con = con; - - proxy_backlog_push(p->conf.backlog, req); - - COUNTER_INC(p->conf.backlog_size); - sess->sent_to_backlog++; - - return HANDLER_GO_ON; -} - -/** - * build the request-header array and call the backend specific request formater - * to fill the chunkqueue - */ -static int proxy_get_request_header(server *srv, connection *con, plugin_data *p, proxy_session *sess) { - /* request line */ - const char *remote_ip; - size_t i; - - remote_ip = inet_ntop_cache_get_ip(srv, &(con->dst_addr)); - array_append_key_value(sess->request_headers, CONST_STR_LEN("X-Forwarded-For"), remote_ip, strlen(remote_ip)); - - /* http_host is NOT is just a pointer to a buffer - * which is NULL if it is not set */ - if (con->request.http_host && - !buffer_is_empty(con->request.http_host)) { - array_set_key_value(sess->request_headers, CONST_STR_LEN("X-Host"), CONST_BUF_LEN(con->request.http_host)); - } - if (con->conf.is_ssl) { - array_set_key_value(sess->request_headers, CONST_STR_LEN("X-Forwarded-Proto"), CONST_STR_LEN("https")); - } else { - array_set_key_value(sess->request_headers, CONST_STR_LEN("X-Forwarded-Proto"), CONST_STR_LEN("http")); - } - - /* request header */ - for (i = 0; i < con->request.headers->used; i++) { - data_string *ds; - size_t k; - - ds = (data_string *)con->request.headers->data[i]; - - if (buffer_is_empty(ds->value) || buffer_is_empty(ds->key)) continue; - - if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Connection"))) continue; - if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Keep-Alive"))) continue; - if (buffer_is_equal_string(ds->key, CONST_STR_LEN("Expect"))) continue; -#ifdef HAVE_PCRE_H - for (k = 0; k < p->conf.request_rewrites->used; k++) { - proxy_rewrite *rw = p->conf.request_rewrites->ptr[k]; - - if (buffer_is_equal(rw->header, ds->key)) { - int ret; - - if ((ret = pcre_replace(rw->regex, rw->replace, ds->value, p->replace_buf)) < 0) { - switch (ret) { - case PCRE_ERROR_NOMATCH: - /* hmm, ok. no problem */ - array_set_key_value(sess->request_headers, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); - break; - default: - TRACE("oops, pcre_replace failed with: %d", ret); - break; - } - } else { - array_set_key_value(sess->request_headers, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(p->replace_buf)); - } - - break; - } - } - - if (k == p->conf.request_rewrites->used) { - array_set_key_value(sess->request_headers, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); - } -#else - array_set_key_value(sess->request_headers, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); -#endif - } - - /* populate sess->request_uri with the actually requested path - * (con->request.uri). if we have pcre and there is a _uri request - * rewrite, it will be overwritten later - */ - buffer_copy_string_buffer(sess->request_uri, con->request.uri); - - /* check if we want to rewrite the uri */ -#ifdef HAVE_PCRE_H - for (i = 0; i < p->conf.request_rewrites->used; i++) { - proxy_rewrite *rw = p->conf.request_rewrites->ptr[i]; - - if (buffer_is_equal_string(rw->header, CONST_STR_LEN("_uri"))) { - int ret; - - if ((ret = pcre_replace(rw->regex, rw->replace, con->request.uri, p->replace_buf)) < 0) { - switch (ret) { - case PCRE_ERROR_NOMATCH: - /* hmm, ok. no problem */ - buffer_copy_string_buffer(sess->request_uri, con->request.uri); - break; - default: - TRACE("oops, pcre_replace failed with: %d", ret); - break; - } - } else { - buffer_copy_string_buffer(sess->request_uri, p->replace_buf); - } - } else if (buffer_is_equal_string(rw->header, CONST_STR_LEN("_docroot"))) { - int ret; - - if ((ret = pcre_replace(rw->regex, rw->replace, con->physical.doc_root, p->replace_buf)) < 0) { - switch (ret) { - case PCRE_ERROR_NOMATCH: - /* hmm, ok. no problem */ - break; - default: - TRACE("oops, pcre_replace failed with: %d", ret); - break; - } - } else { - /* adjust DOCUMENT_ROOT */ - buffer_copy_string_buffer(con->physical.doc_root, p->replace_buf); - - /* adjust SCRIPT_FILENAME */ - buffer_copy_string_buffer(con->physical.path, p->replace_buf); - buffer_append_string_buffer(con->physical.path, con->physical.rel_path); - } - } else if (buffer_is_equal_string(rw->header, CONST_STR_LEN("_pathinfo"))) { - int ret; - - if ((ret = pcre_replace(rw->regex, rw->replace, con->uri.path, p->replace_buf)) < 0) { - switch (ret) { - case PCRE_ERROR_NOMATCH: - /* hmm, ok. no problem */ - break; - default: - TRACE("oops, pcre_replace failed with: %d", ret); - break; - } - } else { - /* we matched, cool. */ - buffer_copy_string_buffer(con->request.pathinfo, p->replace_buf); - } - } else if (buffer_is_equal_string(rw->header, CONST_STR_LEN("_scriptname"))) { - int ret; - - if ((ret = pcre_replace(rw->regex, rw->replace, con->uri.path, p->replace_buf)) < 0) { - switch (ret) { - case PCRE_ERROR_NOMATCH: - /* hmm, ok. no problem */ - break; - default: - TRACE("oops, pcre_replace failed with: %d", ret); - break; - } - } else { - /* we matched, cool. */ - buffer_copy_string_buffer(con->uri.path, p->replace_buf); - } - - } - } -#endif - - proxy_encode_request_headers(srv, sess, con->recv); - - return 0; -} - -/* we are event-driven - * - * the first entry is connect() call, if the doesn't need a event - * - * a bit boring - * - connect (+ delayed connect) - * - write header + content - * - read header + content - * - * as soon as have read the response header we switch con->file_started and return HANDLER_GO_ON to - * tell the core we are ready to stream out the content. - * */ -static handler_t proxy_state_engine(server *srv, connection *con, plugin_data *p, proxy_session *sess) { - /* do we have a connection ? */ - - if (p->conf.debug > 0) - TRACE("proxy_state_engine: state=%d", sess->state); - - switch (sess->state) { - case PROXY_STATE_UNSET: - /* we are not started yet */ - sess->connect_start_ts = srv->cur_ts; - switch(proxy_connection_connect(sess->proxy_con)) { - case HANDLER_WAIT_FOR_EVENT: - /* waiting on the connect call */ - - fdevent_register(srv->ev, sess->proxy_con->sock, proxy_handle_fdevent, sess); - fdevent_event_add(srv->ev, sess->proxy_con->sock, FDEVENT_OUT); - - sess->state = PROXY_STATE_CONNECTING; - sess->proxy_con->state = PROXY_CONNECTION_STATE_CONNECTING; - /** - * if we are in - * - * connect(...) = -1 EINPROGRESS - * - * it might take ages until we get a response - */ - sess->proxy_con->state_ts = srv->cur_ts; - sess->proxy_con->proxy_sess = sess; - - /* if the client connection closes its end get notified */ - fdevent_event_add(srv->ev, con->sock, FDEVENT_HUP); - - return HANDLER_WAIT_FOR_EVENT; - case HANDLER_GO_ON: - /* we are connected */ - sess->state = PROXY_STATE_CONNECTED; - sess->proxy_con->state = PROXY_CONNECTION_STATE_CONNECTED; - - /* initialize stream. */ - proxy_stream_init(srv, sess); - - fdevent_register(srv->ev, sess->proxy_con->sock, proxy_handle_fdevent, sess); - - break; - case HANDLER_WAIT_FOR_FD: - /* we have to come back later when we have a fd */ - return HANDLER_WAIT_FOR_FD; - case HANDLER_ERROR: - /* there is no-one on the other side */ - sess->proxy_con->address->disabled_until = srv->cur_ts + p->conf.disable_time; - - TRACE("connecting to address %s (%p) failed, disabling for %u sec", - SAFE_BUF_STR(sess->proxy_con->address->name), - (void*) sess->proxy_con->address, - (unsigned int) p->conf.disable_time); - COUNTER_INC(sess->proxy_backend->requests_failed); - - sess->proxy_con->address->state = PROXY_ADDRESS_STATE_DISABLED; - sess->proxy_backend->disabled_addresses++; - /* if all addresses in address_pool are disabled, then disable this backend. */ - - if (sess->proxy_backend->disabled_addresses == sess->proxy_backend->address_pool->used) { - sess->proxy_backend->state = PROXY_BACKEND_STATE_DISABLED; - } - /* try another backend instead */ - return HANDLER_COMEBACK; - default: - /* not good, something failed */ - return HANDLER_ERROR; - - } - - /* fall through */ - case PROXY_STATE_CONNECTING: - /* skip if already connected */ - if (sess->state == PROXY_STATE_CONNECTING) { - int socket_error; - socklen_t socket_error_len = sizeof(socket_error); - - fdevent_event_del(srv->ev, sess->proxy_con->sock); - - switch (sess->proxy_con->state) { - case PROXY_CONNECTION_STATE_CONNECTING: - if (0 != getsockopt(sess->proxy_con->sock->fd, SOL_SOCKET, SO_ERROR, &socket_error, &socket_error_len)) { - ERROR("getsockopt failed: %s", strerror(errno)); - - return HANDLER_ERROR; - } - if (socket_error != 0) { - switch (socket_error) { - case ECONNREFUSED: - /* there is no-one on the other side */ - sess->proxy_con->address->disabled_until = srv->cur_ts + p->conf.disable_time; - - TRACE("address %s refused us, disabling for %u sec", sess->proxy_con->address->name->ptr, (unsigned int) p->conf.disable_time); - COUNTER_INC(sess->proxy_backend->requests_failed); - - break; - case EHOSTUNREACH: - /* there is no-one on the other side */ - sess->proxy_con->address->disabled_until = srv->cur_ts + p->conf.disable_time; - - TRACE("host %s is unreachable, disabling for %u sec", sess->proxy_con->address->name->ptr, (unsigned int) p->conf.disable_time); - break; - default: - sess->proxy_con->address->disabled_until = srv->cur_ts + p->conf.disable_time; - - TRACE("connected finally failed: %s (%d)", strerror(socket_error), socket_error); - - TRACE("connect to address %s failed and I don't know why, disabling for %u sec", sess->proxy_con->address->name->ptr, (unsigned int) p->conf.disable_time); - - break; - } - - sess->proxy_con->address->state = PROXY_ADDRESS_STATE_DISABLED; - sess->proxy_backend->disabled_addresses++; - /* if all addresses in address_pool are disabled, then disable this backend. */ - if (sess->proxy_backend->disabled_addresses == sess->proxy_backend->address_pool->used) { - sess->proxy_backend->state = PROXY_BACKEND_STATE_DISABLED; - } - return HANDLER_COMEBACK; - } - - sess->state = PROXY_STATE_CONNECTED; - sess->proxy_con->state = PROXY_CONNECTION_STATE_CONNECTED; - - /* initialize stream. */ - proxy_stream_init(srv, sess); - - break; - case PROXY_CONNECTION_STATE_CLOSED: - /* looks like the connect() timed out */ - sess->proxy_con->address->disabled_until = srv->cur_ts + p->conf.disable_time; - sess->proxy_con->address->state = PROXY_ADDRESS_STATE_DISABLED; - - /* the connection */ - sess->proxy_con->state = PROXY_CONNECTION_STATE_CLOSED; - - /* if all addresses in address_pool are disabled, then disable this backend. */ - sess->proxy_backend->disabled_addresses++; - - if (sess->proxy_backend->disabled_addresses == sess->proxy_backend->address_pool->used) { - sess->proxy_backend->state = PROXY_BACKEND_STATE_DISABLED; - } - TRACE("connect(%s) to failed: trying another backed", - SAFE_BUF_STR(sess->proxy_con->address->name)); - return HANDLER_COMEBACK; - default: - ERROR("invalid connection-state: %d", sess->proxy_con->state); - break; - } - } - - /* fall through */ - case PROXY_STATE_CONNECTED: - - sess->state = PROXY_STATE_WRITE_REQUEST_HEADER; - - /* fall through */ - case PROXY_STATE_WRITE_REQUEST_HEADER: - /* build the header */ - proxy_get_request_header(srv, con, p, sess); - - /* send the header together with the body */ - sess->state = PROXY_STATE_WRITE_REQUEST_BODY; - - /* fall through */ - case PROXY_STATE_WRITE_REQUEST_BODY: - /* do we have a content-body to send up to the backend ? */ - - switch (proxy_stream_encode_decode(srv, sess)) { - case HANDLER_FINISHED: - case HANDLER_GO_ON: - /* some backends will send a response before all the request content has been written. */ - if (sess->is_closed || sess->have_response_headers) { - chunk *c; - if (sess->is_closed && !sess->have_response_headers) { - if (sess->p->conf.debug) TRACE("%s", "connection to backend closed when sending request headers/content."); - } - if (con->recv->bytes_out < con->recv->bytes_in) { - /* we have to consume all the request content data. */ - for (c = con->recv->first; c; c = c->next) { - switch(c->type) { - case MEM_CHUNK: - c->offset = c->mem->used - 1; - break; - case FILE_CHUNK: - c->offset = c->file.length; - break; - default: - break; - } - } - con->recv->bytes_out = con->recv->bytes_in; - con->recv->is_closed = 1; - } - break; - } - return HANDLER_WAIT_FOR_EVENT; - case HANDLER_ERROR: - /* error */ - return HANDLER_ERROR; - default: - TRACE("stream-decoder: %s", "foo"); - break; - } - - sess->state = PROXY_STATE_READ_RESPONSE_HEADER; - /* fall through */ - case PROXY_STATE_READ_RESPONSE_HEADER: - - /* decode/encode stream. */ - switch (proxy_stream_encode_decode(srv, sess)) { - case HANDLER_FINISHED: - case HANDLER_GO_ON: - if (!sess->proxy_con->recv->is_closed && !sess->have_response_headers) { - return HANDLER_WAIT_FOR_EVENT; - } - break; - case HANDLER_ERROR: - /* error */ - return HANDLER_ERROR; - default: - TRACE("stream-decoder: %s", "foo"); - return HANDLER_ERROR; - } - - if (sess->have_response_headers) { - /* handle the parsed response headers. */ - switch (proxy_handle_response_headers(srv, con, p, sess, sess->recv)) { - case HANDLER_FINISHED: - case HANDLER_GO_ON: - break; - case HANDLER_ERROR: - /* bad gateway */ - con->http_status = 502; - return HANDLER_FINISHED; - default: - ERROR("%s", "++ oops, something went wrong while parsing response headers"); - con->http_status = 500; /* Internal Server Error */ - return HANDLER_ERROR; - } - } - - if (!sess->is_closed && !sess->have_response_headers) { - if (sess->proxy_con->recv->bytes_in == 0) { - /* the connection went away before we got something back */ - if (p->conf.debug) TRACE("%s", "connection closed while reading the response headers"); - - if (con->request.content_length <= 0) { - /** - * we might run into a 'race-condition' - * - * 1. proxy-con is keep-alive, idling and just being closed (FDEVENT_IN) [fd=27] - * 2. new connection comes in, we use the idling connection [fd=14] - * 3. we write(), successful [to fd=27] - * 3. we read() ... and finally receive the close-event for the connection - */ - - return HANDLER_COMEBACK; - } else { - ERROR("%s", "request content length > 0 can't restart request."); - } - } else { - ERROR("%s", "connection closed after reading part of the response headers."); - } - - con->http_status = 500; /* Internal Server Error */ - return HANDLER_ERROR; - } else if (sess->do_internal_redirect) { - /* no more response data to process. do redirect now. */ - if (sess->recv->is_closed) { - sess->state = PROXY_STATE_FINISHED; - /* now it becomes tricky - * - * mod_staticfile should handle this file for us - * con->mode = DIRECT is taking us out of the loop */ - con->mode = DIRECT; - con->http_status = 0; - - return HANDLER_COMEBACK; - } else { - /* finish processing response data, so we can re-use backend connection. */ - sess->state = PROXY_STATE_READ_RESPONSE_BODY; - } - } else { - con->file_started = 1; - /* if Status: ... is not set, 200 is our default status-code */ - if (con->http_status == 0) con->http_status = 200; - - /* check if all the response content has been read/decoded. */ - if (sess->bytes_read == sess->content_length) { - sess->is_request_finished = 1; - /* close response content chunkqueue */ - sess->recv->is_closed = 1; - } - /* copy any response content we might have. */ - proxy_copy_response(srv, con, sess); - - sess->state = PROXY_STATE_READ_RESPONSE_BODY; - - /** - * set the event to pass the content through to the server - * - * this triggers the event-handler - * @see proxy_handle_fdevent - */ - - return HANDLER_GO_ON; /* tell http_response_prepare that we are done with the header */ - } - - if (sess->state != PROXY_STATE_READ_RESPONSE_BODY) break; - case PROXY_STATE_READ_RESPONSE_BODY: - - switch (proxy_stream_encode_decode(srv, sess)) { - case HANDLER_FINISHED: - case HANDLER_GO_ON: - break; - case HANDLER_ERROR: - /* error */ - return HANDLER_ERROR; - default: - TRACE("stream-decoder: %s", "foo"); - break; - } - - proxy_copy_response(srv, con, sess); - - if (!sess->proxy_con->recv->is_closed && !sess->is_request_finished) { - return HANDLER_WAIT_FOR_EVENT; - } - - if(sess->is_request_finished) { - sess->recv->is_closed = 1; - con->send->is_closed = 1; - /* recycle proxy connection. */ - proxy_recycle_backend_connection(srv, p, sess); - - sess->state = PROXY_STATE_FINISHED; - - if (sess->do_internal_redirect) { - /* now it becomes tricky - * - * mod_staticfile should handle this file for us - * con->mode = DIRECT is taking us out of the loop */ - con->send->is_closed = 0; - con->mode = DIRECT; - con->http_status = 0; - - return HANDLER_COMEBACK; - } - } - - /* we wrote something into the the send-buffers, - * call the connection-handler to push it to the client */ - joblist_append(srv, con); - - break; - default: - break; - } - - return HANDLER_GO_ON; -} - -static proxy_backend *proxy_find_backend(server *srv, connection *con, plugin_data *p, buffer *name) { - size_t i; - - UNUSED(srv); - UNUSED(con); - - for (i = 0; i < p->conf.backends->used; i++) { - proxy_backend *backend = p->conf.backends->ptr[i]; - - if (buffer_is_equal(backend->name, name)) { - return backend; - } - } - - return NULL; -} - -/** - * choose an available backend - * - */ -static proxy_backend *proxy_backend_balancer(server *srv, connection *con, proxy_session *sess) { - size_t i; - plugin_data *p = sess->p; - proxy_backends *backends = p->conf.backends; - unsigned long last_max; /* for the HASH balancer */ - proxy_backend *backend = NULL, *cur_backend = NULL; - int active_backends = 0, rand_ndx; - size_t min_used; - - UNUSED(srv); - - /* if we only have one backend just return it. */ - if (backends->used == 1) { - backend = backends->ptr[0]; - - return backend->state == PROXY_BACKEND_STATE_ACTIVE ? backend : NULL; - } - - /* frist try to select backend based on sticky session. */ - if (sess->sticky_session) { - /* find backend */ - backend = proxy_find_backend(srv, con, p, sess->sticky_session); - if (NULL != backend) return backend; - } - - /* apply balancer algorithm to select backend. */ - switch(p->conf.balancer) { - case PROXY_BALANCE_CARP: - /* hash balancing */ - - for (i = 0, last_max = ULONG_MAX; i < backends->used; i++) { - unsigned long cur_max; - - cur_backend = backends->ptr[i]; - - if (cur_backend->state != PROXY_BACKEND_STATE_ACTIVE) continue; - - cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path)) + - generate_crc32c(CONST_BUF_LEN(cur_backend->name)) + /* we can cache this */ - generate_crc32c(CONST_BUF_LEN(con->uri.authority)); -#if 0 - TRACE("hash-election: %s - %s - %s: %ld", - con->uri.path->ptr, - cur_backend->name->ptr, - con->uri.authority->ptr, - cur_max); -#endif - if (backend == NULL || (cur_max > last_max)) { - last_max = cur_max; - - backend = cur_backend; - } - } - - break; - case PROXY_BALANCE_STATIC: - /* static (only fail-over) */ - - for (i = 0; i < backends->used; i++) { - cur_backend = backends->ptr[i]; - - if (cur_backend->state != PROXY_BACKEND_STATE_ACTIVE) continue; - - backend = cur_backend; - break; - } - - break; - case PROXY_BALANCE_SQF: - /* shortest-queue-first balancing */ - - for (i = 0, min_used = SIZE_MAX; i < backends->used; i++) { - cur_backend = backends->ptr[i]; - - if (cur_backend->state != PROXY_BACKEND_STATE_ACTIVE) continue; - - /* the backend is up, use it */ - if (cur_backend->pool->used < min_used ) { - backend = cur_backend; - min_used = cur_backend->pool->used; - } - } - - break; - case PROXY_BALANCE_UNSET: /* if not set, use round-robin as default */ - case PROXY_BALANCE_RR: - /* round robin */ - - /** - * instead of real RoundRobin we just do a RandomSelect - * - * it is state-less and has the same distribution - */ - - active_backends = 0; - - for (i = 0; i < backends->used; i++) { - cur_backend = backends->ptr[i]; - - if (cur_backend->state != PROXY_BACKEND_STATE_ACTIVE) continue; - - active_backends++; - } - - rand_ndx = (int) (1.0 * active_backends * rand()/(RAND_MAX)); - - active_backends = 0; - for (i = 0; i < backends->used; i++) { - cur_backend = backends->ptr[i]; - - if (cur_backend->state != PROXY_BACKEND_STATE_ACTIVE) continue; - - backend = cur_backend; - - if (rand_ndx == active_backends++) break; - } - - break; - } - - return backend; -} - -/** - * choose an available address from the address-pool - * - * the backend has different balancers - */ -static proxy_address *proxy_address_balancer(server *srv, connection *con, proxy_session *sess) { - size_t i; - proxy_backend *backend = sess->proxy_backend; - proxy_address_pool *address_pool = backend->address_pool; - unsigned long last_max; /* for the HASH balancer */ - proxy_address *address = NULL, *cur_address = NULL; - int active_addresses = 0, rand_ndx; - size_t min_used; - - UNUSED(srv); - - /* if we only have one address just return it. */ - if (address_pool->used == 1) { - address = address_pool->ptr[0]; - - return address->state == PROXY_ADDRESS_STATE_ACTIVE ? address : NULL; - } - - /* apply balancer algorithm to select address. */ - switch(backend->balancer) { - case PROXY_BALANCE_CARP: - /* hash balancing */ - - for (i = 0, last_max = ULONG_MAX; i < address_pool->used; i++) { - unsigned long cur_max; - - cur_address = address_pool->ptr[i]; - - if (cur_address->state != PROXY_ADDRESS_STATE_ACTIVE) continue; - - cur_max = generate_crc32c(CONST_BUF_LEN(con->uri.path)) + - generate_crc32c(CONST_BUF_LEN(cur_address->name)) + /* we can cache this */ - generate_crc32c(CONST_BUF_LEN(con->uri.authority)); -#if 0 - TRACE("hash-election: %s - %s - %s: %ld", - con->uri.path->ptr, - cur_address->name->ptr, - con->uri.authority->ptr, - cur_max); -#endif - if (address == NULL || (cur_max > last_max)) { - last_max = cur_max; - - address = cur_address; - } - } - - break; - case PROXY_BALANCE_STATIC: - /* static (only fail-over) */ - - for (i = 0; i < address_pool->used; i++) { - cur_address = address_pool->ptr[i]; - - if (cur_address->state != PROXY_ADDRESS_STATE_ACTIVE) continue; - - address = cur_address; - break; - } - - break; - case PROXY_BALANCE_SQF: - /* shortest-queue-first balancing */ - - for (i = 0, min_used = SIZE_MAX; i < address_pool->used; i++) { - cur_address = address_pool->ptr[i]; - - if (cur_address->state != PROXY_ADDRESS_STATE_ACTIVE) continue; - - /* the address is up, use it */ - if (cur_address->used < min_used ) { - address = cur_address; - min_used = cur_address->used; - } - } - - break; - case PROXY_BALANCE_UNSET: /* if not set, use round-robin as default */ - case PROXY_BALANCE_RR: - /* round robin */ - - /** - * instead of real RoundRobin we just do a RandomSelect - * - * it is state-less and has the same distribution - */ - - active_addresses = 0; - - for (i = 0; i < address_pool->used; i++) { - cur_address = address_pool->ptr[i]; - - if (cur_address->state != PROXY_ADDRESS_STATE_ACTIVE) continue; - - active_addresses++; - } - - rand_ndx = (int) (1.0 * active_addresses * rand()/(RAND_MAX)); - - active_addresses = 0; - for (i = 0; i < address_pool->used; i++) { - cur_address = address_pool->ptr[i]; - - if (cur_address->state != PROXY_ADDRESS_STATE_ACTIVE) continue; - - address = cur_address; - - if (rand_ndx == active_addresses++) break; - } - - break; - } - - return address; -} - -static int mod_proxy_core_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i, j; - plugin_config *s = p->config_storage[0]; - - /* global defaults */ - PATCH_OPTION(balancer); - PATCH_OPTION(debug); - PATCH_OPTION(backends); - PATCH_OPTION(backlog); - PATCH_OPTION(backlog_size); - PATCH_OPTION(protocol); - PATCH_OPTION(request_rewrites); - PATCH_OPTION(response_rewrites); - PATCH_OPTION(allow_x_sendfile); - PATCH_OPTION(allow_x_rewrite); - PATCH_OPTION(max_pool_size); - PATCH_OPTION(check_local); - PATCH_OPTION(split_hostnames); - PATCH_OPTION(max_keep_alive_requests); - PATCH_OPTION(disable_time); - PATCH_OPTION(max_backlog_size); - - /* 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_PROXY_CORE_BACKENDS))) { - PATCH_OPTION(backends); - PATCH_OPTION(backlog); - PATCH_OPTION(backlog_size); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_DEBUG))) { - PATCH_OPTION(debug); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_BALANCER))) { - PATCH_OPTION(balancer); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_PROTOCOL))) { - PATCH_OPTION(protocol); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_REWRITE_REQUEST))) { - PATCH_OPTION(request_rewrites); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_REWRITE_RESPONSE))) { - PATCH_OPTION(response_rewrites); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_ALLOW_X_SENDFILE))) { - PATCH_OPTION(allow_x_sendfile); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_ALLOW_X_REWRITE))) { - PATCH_OPTION(allow_x_rewrite); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_MAX_POOL_SIZE))) { - PATCH_OPTION(max_pool_size); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_CHECK_LOCAL))) { - PATCH_OPTION(check_local); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_SPLIT_HOSTNAMES))) { - PATCH_OPTION(split_hostnames); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_MAX_KEEP_ALIVE))) { - PATCH_OPTION(max_keep_alive_requests); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_DISABLE_TIME))) { - PATCH_OPTION(disable_time); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_PROXY_CORE_MAX_BACKLOG_SIZE))) { - PATCH_OPTION(max_backlog_size); - } - } - } - - return 0; -} - -static int mod_proxy_core_check_match(server *srv, connection *con, plugin_data *p, int file_match) { - proxy_session *sess = con->plugin_ctx[p->id]; - buffer *path; - - if (sess && !sess->do_new_session) { - /* if this is the second round, sess is already prepared */ - return HANDLER_GO_ON; - } - - /* check if we have a matching conditional for this request */ - mod_proxy_core_patch_connection(srv, con, p); - - /* no proxy backends to handle this request. */ - if (p->conf.backends->used == 0) return HANDLER_GO_ON; -#if 0 - /* if check_local is enabled, then wait for file match. */ - if (file_match != p->conf.check_local) return HANDLER_GO_ON; -#endif - path = file_match ? con->physical.path : con->uri.path; -#if 0 - if (buffer_is_empty(path)) return HANDLER_GO_ON; -#endif - if (sess && sess->do_x_rewrite_backend) { - proxy_backend *backend; - buffer *sticky_session = sess->sticky_session; - - sess->sticky_session = NULL; - /* clear old session state */ - proxy_session_reset(sess); - - /* find backend */ - backend = proxy_find_backend(srv, con, p, sticky_session); - if (backend == NULL) { - backend = proxy_backend_init(); - - backend->balancer = p->conf.balancer; - backend->protocol = p->conf.protocol; - - buffer_copy_string_buffer(backend->name, sticky_session); - /* check if the new backend has a valid backend-address */ - if (0 == proxy_address_pool_add_string(backend->address_pool, backend->name)) { - if (p->conf.max_pool_size) { - backend->pool->max_size = p->conf.max_pool_size; - } - - proxy_backends_add(p->conf.backends, backend); - } else { - proxy_backend_free(backend); - backend = NULL; - } - } - /* no backend available. */ - if (NULL == backend) { - buffer_free(sticky_session); - return HANDLER_GO_ON; - } - sess->proxy_backend = backend; - sess->sticky_session = sticky_session; - } else if (sess) { - proxy_session_reset(sess); - } - - /* make sure we have a protocol. */ - if (p->conf.protocol == NULL) { - con->http_status = 500; /* internal error */ - con->send->is_closed = 1; - - TRACE("proxy-core.backends is set, but proxy-core.protocol is not, can't handle '%s' for host '%s'", - SAFE_BUF_STR(con->uri.path), - SAFE_BUF_STR(con->uri.authority)); - return HANDLER_FINISHED; - } - - if (!sess) { - /* a session lives for a single request */ - sess = proxy_session_init(); - } - /* make sure the state is correct. */ - sess->state = PROXY_STATE_UNSET; - - con->plugin_ctx[p->id] = sess; - con->mode = p->id; - - if (con->conf.log_request_handling) { - TRACE("handling it in mod_proxy_core: %s.path=%s", - file_match ? "physical" : "uri", SAFE_BUF_STR(path)); - } - sess->p = p; - sess->remote_con = con; - - return HANDLER_FINISHED; -} - -SUBREQUEST_FUNC(mod_proxy_core_match_url) { - return mod_proxy_core_check_match(srv, con, p_d, 0); -} - -SUBREQUEST_FUNC(mod_proxy_core_match_local_file) { - return mod_proxy_core_check_match(srv, con, p_d, 1); -} - -/** - * end of a request - */ -REQUESTDONE_FUNC(mod_proxy_connection_close_callback) { - plugin_data *p = p_d; - proxy_session *sess = con->plugin_ctx[p->id]; - - if (!sess) return HANDLER_GO_ON; - - /* TODO: copy p->conf into proxy_session when request starts. */ - /* re-patch p->conf */ - mod_proxy_core_patch_connection(srv, con, p); - - if (p->conf.debug) TRACE("proxy_connection_reset (%d)", con->sock->fd); - - if (sess->proxy_con) { - proxy_recycle_backend_connection(srv, p, sess); - } else { - /* if we have the connection in the backlog, remove it */ - if (0 == proxy_backlog_remove_connection(p->conf.backlog, con)) { - COUNTER_DEC(p->conf.backlog_size); - } - } - - /* cleanup proxy session */ - proxy_session_free(sess); - - con->plugin_ctx[p->id] = NULL; - - return HANDLER_GO_ON; -} - -CONNECTION_FUNC(mod_proxy_core_start_backend) { - plugin_data *p = p_d; - proxy_session *sess = con->plugin_ctx[p->id]; - - if (p->id != con->mode) return HANDLER_GO_ON; - if (!sess) return HANDLER_GO_ON; - - /* TODO: copy p->conf into proxy_session when request starts. */ - /* re-patch p->conf */ - mod_proxy_core_patch_connection(srv, con, p); - - /* - * 0. build session - * 1. get a proxy connection - * 2. create the http-request header - * 3. stream the content to the backend - * 4. wait for http-response header - * 5. decode the response + parse the response - * 6. stream the response-content to the client - * 7. session finished wait for request close - * 8. kill session - * */ - - if (sess->do_internal_redirect) { - if (sess->internal_redirect_count > MAX_INTERNAL_REDIRECTS) { - /* we already handled this request and sent it to the static file handling */ - - return HANDLER_GO_ON; - } - } - - switch (sess->state) { - case PROXY_STATE_FINISHED: - return HANDLER_GO_ON; - case PROXY_STATE_CONNECTING: - /* this connections has waited 10 seconds to connect to the backend - * and didn't got a successful connection yet, sending timeout */ - if (srv->cur_ts - sess->connect_start_ts > 10) { - con->http_status = 504; /* gateway timeout */ - con->send->is_closed = 1; - - if (sess->proxy_con) { - TRACE("connect to backend timed out: %s", SAFE_BUF_STR(sess->proxy_con->address->name)); - /* if we are waiting for a proxy-connection right now, close it */ - proxy_remove_backend_connection(srv, sess); - } else { - TRACE("%s", "timed out when trying to connect to backend and don't have a connection."); - } - - return HANDLER_FINISHED; - } - default: - /* handle-request-timeout, */ -#if 0 - if (srv->cur_ts - con->request_start > 60) { - TRACE("request runs longer than 60sec: current state: %d", sess->state); - } -#endif - break; - } - - - /* if the WRITE fails from the start, restart the connection */ - while (1) { - if (sess->proxy_con == NULL) { - proxy_address *address = NULL; - - /** - * ask the balancer for the next address and - * check the connection pool if we have a connection open - * for that address - */ - if (NULL == (sess->proxy_backend = proxy_backend_balancer(srv, con, sess))) { - if (p->conf.debug) TRACE("backlog: all backends are full or down, putting %s (%d) into the backlog, retry = %d", - SAFE_BUF_STR(con->uri.path), con->sock->fd, sess->sent_to_backlog + 1); - - /* no backends available right now. */ - if (HANDLER_ERROR == mod_proxy_core_backlog_connection(srv, con, p, sess)) { - con->http_status = 504; /* gateway timeout */ - con->send->is_closed = 1; - - TRACE("connecting backends timed out, retry limit reached: %d", sess->sent_to_backlog); - return HANDLER_FINISHED; - } - - /* no, not really an event, - * we just want to block the outer loop from stepping forward - * - * the trigger will bring this connection back into the game - */ - return HANDLER_WAIT_FOR_EVENT; - } - - sess->proxy_backend->protocol = p->conf.protocol; - sess->proxy_backend->balancer = p->conf.balancer; - - if (p->conf.debug && sess->proxy_backend) { - TRACE("selected backend: %s, state: %d", SAFE_BUF_STR(sess->proxy_backend->name), sess->proxy_backend->state); - } - - /** - * ask the balancer for the next address and - * check the connection pool if we have a connection open - * for that address - */ - if (NULL == (address = proxy_address_balancer(srv, con, sess))) { - /* no addresses available for this backend right now. */ - sess->proxy_backend->state = PROXY_BACKEND_STATE_DISABLED; /* disable backend */ - TRACE("backlog: all addresses are down, putting %s (%d) into the backlog, retry = %d", - SAFE_BUF_STR(con->uri.path), con->sock->fd, sess->sent_to_backlog + 1); - - if (HANDLER_ERROR == mod_proxy_core_backlog_connection(srv, con, p, sess)) { - con->http_status = 504; /* gateway timeout */ - con->send->is_closed = 1; - - TRACE("connecting backends timed out, retry limit reached: %d", sess->sent_to_backlog); - return HANDLER_FINISHED; - } - - /* no, not really an event, - * we just want to block the outer loop from stepping forward - * - * the trigger will bring this connection back into the game - */ - - return HANDLER_WAIT_FOR_EVENT; - } - - if (PROXY_CONNECTIONPOOL_FULL == proxy_connection_pool_get_connection( - sess->proxy_backend->pool, address, &(sess->proxy_con))) { - /* all connections are busy. */ - sess->proxy_backend->state = PROXY_BACKEND_STATE_FULL; - - if (p->conf.debug) TRACE("backlog: the con-pool is full, putting %s (%d) into the backlog", SAFE_BUF_STR(con->uri.path), con->sock->fd); - if (HANDLER_ERROR == mod_proxy_core_backlog_connection(srv, con, p, sess)) { - con->http_status = 504; /* gateway timeout */ - con->send->is_closed = 1; - - TRACE("connecting backends timed out, retry limit reached: %d", sess->sent_to_backlog); - return HANDLER_FINISHED; - } - - /* no, not really an event, - * we just want to block the outer loop from stepping forward - * - * the trigger will bring this connection back into the game - */ - return HANDLER_WAIT_FOR_EVENT; - } - COUNTER_SET(sess->proxy_backend->pool_size, sess->proxy_backend->pool->used); - COUNTER_INC(sess->proxy_backend->load); - - /* need to reset flags. */ - sess->is_closing = 0; - sess->is_closed = 0; - /* a fresh connection, we need address for it */ - if (sess->proxy_con->state == PROXY_CONNECTION_STATE_CONNECTING) { - sess->state = PROXY_STATE_UNSET; - sess->bytes_read = 0; - } else { - /* we are already connected */ - sess->state = PROXY_STATE_CONNECTED; - - /* the connection was idling and using the fdevent_idle-handler - * switch it back to the normal proxy-event-handler */ - fdevent_event_del(srv->ev, sess->proxy_con->sock); - fdevent_unregister(srv->ev, sess->proxy_con->sock); - - fdevent_register(srv->ev, sess->proxy_con->sock, proxy_handle_fdevent, sess); - fdevent_event_add(srv->ev, sess->proxy_con->sock, FDEVENT_IN); - } - } - - - switch (proxy_state_engine(srv, con, p, sess)) { - case HANDLER_WAIT_FOR_EVENT: - return HANDLER_WAIT_FOR_EVENT; - case HANDLER_COMEBACK: -#if 0 - TRACE("%s", "setting PROXY_STATE_FINISHED"); - sess->state = PROXY_STATE_FINISHED; -#endif - /* request finished do redirect. */ - if (sess->do_internal_redirect) { - /* recycle proxy connection. */ - proxy_recycle_backend_connection(srv, p, sess); - return HANDLER_COMEBACK; - } - /* restart the connection to the backend */ - if (p->conf.debug) TRACE("%s", "write failed, restarting request"); - proxy_remove_backend_connection(srv, sess); - break; - case HANDLER_WAIT_FOR_FD: - return HANDLER_WAIT_FOR_FD; - case HANDLER_GO_ON: - return HANDLER_GO_ON; - default: - TRACE("state: %d (error)", sess->state); - proxy_remove_backend_connection(srv, sess); - /* only set 500 if not another error code is already set (like 502) */ - if (con->http_status < 500 || con->http_status > 599) { - con->http_status = 500; /* Internal Server Error */ - } - con->mode = DIRECT; - return HANDLER_GO_ON; - } - } - - /* should not be reached */ - return HANDLER_ERROR; -} - -CONNECTION_FUNC(mod_proxy_send_request_content) { - plugin_data *p = p_d; - - if (p->id != con->mode) return HANDLER_GO_ON; - - /* read all the content before we start our backend */ - if (!con->recv->is_closed) { - return HANDLER_GO_ON; - } - - /* copy the chunks to our queue and call the state-engine to send it out */ - return mod_proxy_core_start_backend(srv, con, p_d); -} - -/** - * cleanup dead connections once a second - * - * the idling event-handler can't cleanup connections itself and has to wait until the - * trigger cleans up - */ -static int mod_proxy_wakeup_connections(server *srv, plugin_data *p, plugin_config *p_conf) { - size_t i, j; - proxy_request *req; - int total_conns_available = 0, backends_available = 0; - int woken_up; - - for (i = 0; i < p_conf->backends->used; i++) { - proxy_backend *backend = p_conf->backends->ptr[i]; - proxy_connection_pool *pool = backend->pool; - proxy_address_pool *address_pool = backend->address_pool; - unsigned int conns_available = 0, addrs_disabled = 0; - proxy_session *sess = NULL; - - conns_available = (pool->max_size - pool->used); - for (j = 0; j < pool->used; ) { - proxy_connection *proxy_con = pool->ptr[j]; - - /* remove-con is removing the current con and moves the good connections to the left - * no need to increment i */ - switch (proxy_con->state) { - case PROXY_CONNECTION_STATE_CLOSED: - proxy_connection_pool_remove_connection(backend->pool, proxy_con); - COUNTER_SET(backend->pool_size, backend->pool->used); - - fdevent_event_del(srv->ev, proxy_con->sock); - fdevent_unregister(srv->ev, proxy_con->sock); - - proxy_connection_free(proxy_con); - - conns_available++; - break; - case PROXY_CONNECTION_STATE_CONNECTING: - /* how long are we in this state already ? - * - * if the connect() failed with EINPROGRESS we have to wait until we get a POLLOUT - * if for some reason we don't get that in 4-5 seconds we have to kill the attempt - * - * */ - - if (srv->cur_ts - proxy_con->state_ts < 5) { - j++; - break; - } - - TRACE("connect(%s) timed out, closing backend connection", - SAFE_BUF_STR(proxy_con->address->name)); - - /** timed out - * - * we have to tell the proxy connection to try to connect another backend - */ - - proxy_con->state = PROXY_CONNECTION_STATE_CLOSED; - - sess = proxy_con->proxy_sess; - joblist_append(srv, sess->remote_con); - - j++; - break; - case PROXY_CONNECTION_STATE_IDLE: - conns_available++; - default: - j++; - } - } - - /* active the disabled addresses again */ - for (j = 0; j < address_pool->used; j++) { - proxy_address *address = address_pool->ptr[j]; - - if (address->state != PROXY_ADDRESS_STATE_DISABLED) continue; - - if (srv->cur_ts > address->disabled_until) { - address->disabled_until = 0; - address->state = PROXY_ADDRESS_STATE_ACTIVE; - } else { - addrs_disabled++; - } - } - - total_conns_available += conns_available; - backend->disabled_addresses = addrs_disabled; - /* update backend's state */ - if (conns_available == 0) { - /* connection pool is full and there are no idle connections. */ - backend->state = PROXY_BACKEND_STATE_FULL; - } else if (addrs_disabled == address_pool->used) { - /* all addresses are disabled. */ - backend->state = PROXY_BACKEND_STATE_DISABLED; - } else { - backend->state = PROXY_BACKEND_STATE_ACTIVE; - backends_available++; - } - } - - /* no backends available can't wake any connections. */ - if (backends_available == 0) { - /* all backends are full or disabled. */ - return 0; - } - - /* wake up the connections from the backlog */ - for (woken_up = 0; woken_up < total_conns_available && (req = proxy_backlog_shift(p_conf->backlog)); woken_up++) { - connection *con = req->con; - - if (p_conf->debug) TRACE("wakeup a connection from backlog: con=%d", con->sock->fd); - joblist_append(srv, con); - - COUNTER_DEC(p->conf.backlog_size); - proxy_request_free(req); - } - - return woken_up; -} - -TRIGGER_FUNC(mod_proxy_trigger) { - plugin_data *p = p_d; - size_t i; - - /** - * walk through all the different address pools and check if they are still alive - * - * in case of connect() = -1 -> EINPROGRESS we might have trigger the state-engine - */ - for (i = 0; i < srv->config_context->used; i++) { - mod_proxy_wakeup_connections(srv, p, p->config_storage[i]); - } - - return HANDLER_GO_ON; -} - -LI_EXPORT int mod_proxy_core_plugin_init(plugin *p); -LI_EXPORT int mod_proxy_core_plugin_init(plugin *p) { - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("mod_proxy_core"); - - p->init = mod_proxy_core_init; - p->cleanup = mod_proxy_core_free; - p->set_defaults = mod_proxy_core_set_defaults; - p->handle_physical = mod_proxy_core_match_url; - p->handle_start_backend = mod_proxy_core_match_local_file; - p->handle_send_request_content = mod_proxy_send_request_content; - p->handle_read_response_content = mod_proxy_core_start_backend; - p->connection_reset = mod_proxy_connection_close_callback; - p->handle_connection_close = mod_proxy_connection_close_callback; - p->handle_trigger = mod_proxy_trigger; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_proxy_core.h b/src/mod_proxy_core.h deleted file mode 100644 index a3685539..00000000 --- a/src/mod_proxy_core.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef _MOD_PROXY_CORE_H_ -#define _MOD_PROXY_CORE_H_ - -#include "plugin.h" - -#include "mod_proxy_core_pool.h" -#include "mod_proxy_core_backend.h" -#include "mod_proxy_core_backlog.h" -#include "mod_proxy_core_rewrites.h" - -#include "buffer.h" -#include "http_resp.h" -#include "array.h" - -#define MAX_INTERNAL_REDIRECTS 8 - -struct proxy_protocol; - -typedef struct { - proxy_backends *backends; - - proxy_backlog *backlog; - data_integer *backlog_size; - - proxy_rewrites *request_rewrites; - proxy_rewrites *response_rewrites; - - unsigned short allow_x_sendfile; - unsigned short allow_x_rewrite; - unsigned short debug; - unsigned short max_pool_size; - unsigned short check_local; - unsigned short split_hostnames; - unsigned short max_keep_alive_requests; - unsigned short disable_time; - unsigned short max_backlog_size; - - proxy_balance_t balancer; - struct proxy_protocol *protocol; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - array *possible_balancers; - /*array *possible_protocols; */ - struct proxy_protocol *(*proxy_register_protocol) (const char *name); /* register new protocol */ - - /* statistics counters. */ - data_integer *request_count; - - /* for parsing only */ - array *backends_arr; - buffer *protocol_buf; - buffer *balance_buf; - - buffer *replace_buf; - - buffer *tmp_buf; /** a temporary buffer, used by mod_proxy_backend_fastcgi */ - - plugin_config **config_storage; - - plugin_config conf; -} mod_proxy_core_plugin_data; - -#define plugin_data mod_proxy_core_plugin_data - -typedef enum { - PROXY_STATE_UNSET, - PROXY_STATE_CONNECTING, - PROXY_STATE_CONNECTED, - PROXY_STATE_WRITE_REQUEST_HEADER, - PROXY_STATE_WRITE_REQUEST_BODY, - PROXY_STATE_READ_RESPONSE_HEADER, - PROXY_STATE_READ_RESPONSE_BODY, - PROXY_STATE_FINISHED -} proxy_state_t; - -typedef struct proxy_session { - proxy_connection *proxy_con; - proxy_backend *proxy_backend; - - connection *remote_con; - - buffer *request_uri; - array *request_headers; /** the con->request.headers without the hop-to-hop headers */ - array *env_headers; /** transformed request-headers for the backend */ - - http_resp *resp; /** response http headers from backend. */ - - plugin_data *p; /** used by proxy_xxx_get_request_chunk protocol callbacks */ - - int is_chunked; /** is the incoming content chunked (for HTTP) */ - int is_closing; /** our connection will close when we are done */ - int is_closed; /** our connection closed. we might have to restart the request. */ - int have_response_headers; /** finished parsing response headers. */ - int is_request_finished; /** encoding/decoding this request is finished. */ - int send_response_content; /** 0 if we have to ignore the content-body */ - int do_internal_redirect; /** 1 if we do a internal redirect to the ->mode = DIRECT */ - int internal_redirect_count; /** protection against infinite loops */ - int do_new_session; /** 1 if we want a new proxy session can be created. */ - int do_x_rewrite_backend; /** 1 if we want to do custom backend balancing */ - - buffer *sticky_session; /** holds name of backend for custom balancing or sticky sessions */ - - /** - * chunkqueues - * - recv is the decoded response headers and content - */ - chunkqueue *recv; - - off_t bytes_read; - off_t content_length; - - proxy_state_t state; - - time_t connect_start_ts; - - int sent_to_backlog; -} proxy_session; - -#endif diff --git a/src/mod_proxy_core_address.c b/src/mod_proxy_core_address.c deleted file mode 100644 index 0978c9d1..00000000 --- a/src/mod_proxy_core_address.c +++ /dev/null @@ -1,210 +0,0 @@ -#include <stdlib.h> -#include <string.h> - -#include "log.h" -#include "sys-socket.h" -#include "mod_proxy_core_address.h" - -static proxy_address *proxy_address_init(void) { - proxy_address *address; - - address = calloc(1, sizeof(*address)); - - address->name = buffer_init(); - address->used = 0; - - return address; -} - -static void proxy_address_free(proxy_address *address) { - if (!address) return; - - buffer_free(address->name); - - free(address); -} - - -proxy_address_pool *proxy_address_pool_init(void) { - proxy_address_pool *address_pool; - - address_pool = calloc(1, sizeof(*address_pool)); - - return address_pool; -} - -void proxy_address_pool_free(proxy_address_pool *address_pool) { - if (!address_pool) return; - - ARRAY_STATIC_FREE(address_pool, proxy_address, element, proxy_address_free(element)); - - free(address_pool); -} - -void proxy_address_pool_add(proxy_address_pool *address_pool, proxy_address *address) { - size_t i; - - /* check if this address is already known */ - - for (i = 0; i < address_pool->used; i++) { - proxy_address *pool_address = address_pool->ptr[i]; - - if (buffer_is_equal(address->name, pool_address->name)) { - /* TRACE("%s is already in the address-pool", SAFE_BUF_STR(address->name)); */ - - proxy_address_free(address); - - return; - } - } - - /* TRACE("adding %s to the address-pool", SAFE_BUF_STR(address->name)); */ - - ARRAY_STATIC_PREPARE_APPEND(address_pool); - - address_pool->ptr[address_pool->used++] = address; -} - -int proxy_address_pool_add_string(proxy_address_pool *address_pool, buffer *name) { - struct addrinfo *res = NULL, pref, *cur; - int ret; - buffer *hostname = NULL, *port = NULL; - char *colon; - - pref.ai_flags = 0; - pref.ai_family = PF_UNSPEC; - pref.ai_socktype = SOCK_STREAM; - pref.ai_protocol = 0; - pref.ai_addrlen = 0; - pref.ai_addr = NULL; - pref.ai_canonname = NULL; - pref.ai_next = NULL; - - /* check the address style - * - * unix:/tmp/socket - * www.example.org - * www.example.org:80 - * 127.0.0.1 - * 127.0.0.1:80 - * [::1]:80 - * [::1] - */ - - if (buffer_is_empty(name)) return -1; - - if (0 == strncmp(BUF_STR(name), "unix:", 5)) { - /* a unix domain socket */ -#ifdef HAVE_SYS_UN_H - proxy_address *a = proxy_address_init(); - - /* setup AF_UNIX sockaddr */ - a->addr.un.sun_family = AF_UNIX; - strcpy(a->addr.un.sun_path, BUF_STR(name) + 5); - a->addrlen = sizeof(a->addr.un); -/* -#ifdef SUN_LEN - a->addrlen = SUN_LEN(&(a->addr.un)); -#else - a->addrlen = (name->used - 5) + sizeof(a->addr.un.sun_family); -#endif -*/ - - a->state = PROXY_ADDRESS_STATE_ACTIVE; - buffer_copy_string_buffer(a->name, name); - - proxy_address_pool_add(address_pool, a); - return 0; -#else - ERROR("unix: scheme is not supported for %s", SAFE_BUF_STR(name)); - return -1; -#endif - } else if (name->ptr[0] == '[') { - if (name->ptr[name->used - 1] == ']') { - /* no port-number attached */ - - hostname = buffer_init(); - buffer_copy_string_len(hostname, BUF_STR(name) + 1, name->used - 3); - port = buffer_init_string("80"); - } else if (NULL != (colon = strrchr(BUF_STR(name), ':'))) { - /* with port number */ - - hostname = buffer_init(); - buffer_copy_string_len(hostname, BUF_STR(name) + 1, colon - BUF_STR(name) - 2); - port = buffer_init(); - buffer_copy_string(port, colon + 1); - - } else { - ERROR("this is neither [<ipv6-address>] nor [<ipv6-address>]:<port>: %s", SAFE_BUF_STR(name)); - - return -1; - } - } else if (name->ptr[name->used - 1] != ']' && - NULL != (colon = strrchr(BUF_STR(name), ':'))) { - - hostname = buffer_init(); - buffer_copy_string_len(hostname, BUF_STR(name), colon - BUF_STR(name)); - port = buffer_init(); - buffer_copy_string(port, colon + 1); - } else { - /* no colon, just a IPv4 address or a domain name */ - - hostname = buffer_init_string(BUF_STR(name)); - port = buffer_init_string("80"); - } - - /* TRACE("resolving %s on port %s", SAFE_BUF_STR(hostname), SAFE_BUF_STR(port)); */ - - if (0 != (ret = getaddrinfo(BUF_STR(hostname), BUF_STR(port), &pref, &res))) { - ERROR("getaddrinfo failed: %s", gai_strerror(ret)); - - buffer_free(hostname); - buffer_free(port); - - return -1; - } - - buffer_free(hostname); - buffer_free(port); - - for (cur = res; cur; cur = cur->ai_next) { - proxy_address *a = proxy_address_init(); - - memcpy(&(a->addr), cur->ai_addr, cur->ai_addrlen); - a->addrlen = cur->ai_addrlen; - - a->state = PROXY_ADDRESS_STATE_ACTIVE; - buffer_prepare_copy(a->name, 128); - - switch (cur->ai_family) { -#ifdef HAVE_IPV6 - case AF_INET6: - a->name->ptr[0] = '['; - inet_ntop(cur->ai_family, &(a->addr.ipv6.sin6_addr), a->name->ptr + 1, a->name->size - 2); - a->name->used = strlen(a->name->ptr) + 1; - buffer_append_string_len(a->name, CONST_STR_LEN("]:")); - buffer_append_long(a->name, ntohs(a->addr.ipv6.sin6_port)); - break; -#endif - case AF_INET: - inet_ntop(cur->ai_family, &(a->addr.ipv4.sin_addr), a->name->ptr, a->name->size - 1); - a->name->used = strlen(a->name->ptr) + 1; - - buffer_append_string_len(a->name, CONST_STR_LEN(":")); - buffer_append_long(a->name, ntohs(a->addr.ipv4.sin_port)); - break; - default: - ERROR("unknown address-family: %d", cur->ai_family); - return -1; - } - - - proxy_address_pool_add(address_pool, a); - } - - freeaddrinfo(res); - - return 0; -} - - diff --git a/src/mod_proxy_core_address.h b/src/mod_proxy_core_address.h deleted file mode 100644 index c7177ca7..00000000 --- a/src/mod_proxy_core_address.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef _MOD_PROXY_CORE_ADDRESS_H_ -#define _MOD_PROXY_CORE_ADDRESS_H_ - -#include <time.h> -#include "buffer.h" -#include "sys-socket.h" -#include "array-static.h" - -typedef enum { - PROXY_ADDRESS_STATE_UNSET, - PROXY_ADDRESS_STATE_ACTIVE, - PROXY_ADDRESS_STATE_DISABLED, -} proxy_address_state_t; - -typedef struct { - sock_addr addr; - socklen_t addrlen; - - buffer *name; /* a inet_ntoa() prepresentation of the address */ - - time_t last_used; - time_t disabled_until; - - size_t used; /* count of connections currently using this address */ - - proxy_address_state_t state; -} proxy_address; - -ARRAY_STATIC_DEF(proxy_address_pool, proxy_address, ); - -proxy_address_pool *proxy_address_pool_init(void); -void proxy_address_pool_free(proxy_address_pool *address_pool); -void proxy_address_pool_add(proxy_address_pool *address_pool, proxy_address *address); -int proxy_address_pool_add_string(proxy_address_pool *address_pool, buffer *address); - -#endif diff --git a/src/mod_proxy_core_backend.c b/src/mod_proxy_core_backend.c deleted file mode 100644 index 60648f6c..00000000 --- a/src/mod_proxy_core_backend.c +++ /dev/null @@ -1,50 +0,0 @@ -#include <stdlib.h> - -#include "mod_proxy_core_backend.h" -#include "mod_proxy_core_pool.h" -#include "mod_proxy_core_address.h" - -proxy_backend *proxy_backend_init(void) { - proxy_backend *backend; - - backend = calloc(1, sizeof(*backend)); - backend->pool = proxy_connection_pool_init(); - backend->address_pool = proxy_address_pool_init(); - backend->balancer = PROXY_BALANCE_RR; - backend->name = buffer_init(); - backend->state = PROXY_BACKEND_STATE_ACTIVE; - - return backend; -} - -void proxy_backend_free(proxy_backend *backend) { - if (!backend) return; - - proxy_connection_pool_free(backend->pool); - proxy_address_pool_free(backend->address_pool); - buffer_free(backend->name); - - free(backend); -} - -proxy_backends *proxy_backends_init(void) { - proxy_backends *backends; - - backends = calloc(1, sizeof(*backends)); - - return backends; -} - -void proxy_backends_free(proxy_backends *backends) { - if (!backends) return; - - ARRAY_STATIC_FREE(backends, proxy_backend, element, proxy_backend_free(element)); - - free(backends); -} - -void proxy_backends_add(proxy_backends *backends, proxy_backend *backend) { - ARRAY_STATIC_PREPARE_APPEND(backends); - - backends->ptr[backends->used++] = backend; -} diff --git a/src/mod_proxy_core_backend.h b/src/mod_proxy_core_backend.h deleted file mode 100644 index b9fa1c33..00000000 --- a/src/mod_proxy_core_backend.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef _MOD_PROXY_CORE_BACKEND_H_ -#define _MOD_PROXY_CORE_BACKEND_H_ - -#include "array-static.h" -#include "array.h" -#include "buffer.h" -#include "mod_proxy_core_address.h" -#include "mod_proxy_core_pool.h" -#include "sys-socket.h" - -/** - * a single DNS name might explode to several IP addresses - * - * url: - * - http://foo.bar/suburl/ - * - https://foo.bar/suburl/ - * - unix:/tmp/socket - * - tcp://foobar:1025/ - * - * backend: - * - scgi - * - http - * - fastcgi - * - ajp13 - * - * request-url-rewrite - * response-url-rewrite - */ -typedef enum { - PROXY_BALANCE_UNSET, - PROXY_BALANCE_SQF, - PROXY_BALANCE_CARP, - PROXY_BALANCE_RR, - PROXY_BALANCE_STATIC -} proxy_balance_t; - -typedef enum { - PROXY_BACKEND_STATE_UNSET, - PROXY_BACKEND_STATE_ACTIVE, - PROXY_BACKEND_STATE_FULL, - PROXY_BACKEND_STATE_DISABLED, -} proxy_backend_state_t; - -typedef struct { - buffer *name; - - proxy_connection_pool *pool; /* pool of active connections */ - int use_keepalive; - - proxy_address_pool *address_pool; /* possible destination-addresses, disabling is done here */ - unsigned int disabled_addresses; /* track how many addresses are disabled. */ - proxy_balance_t balancer; /* how to choose a address from the address-pool */ - struct proxy_protocol *protocol; /* protocol handler */ - - proxy_backend_state_t state; - - /* statistics counters. */ - data_integer *request_count; - data_integer *load; - data_integer *pool_size; - data_integer *requests_failed; -} proxy_backend; - -ARRAY_STATIC_DEF(proxy_backends, proxy_backend, ); - -proxy_backend *proxy_backend_init(void); -void proxy_backend_free(proxy_backend *backend); - -proxy_backends *proxy_backends_init(void); -void proxy_backends_free(proxy_backends *backends); -void proxy_backends_add(proxy_backends *backends, proxy_backend *backend); - -#endif - diff --git a/src/mod_proxy_core_backlog.c b/src/mod_proxy_core_backlog.c deleted file mode 100644 index 11a59a59..00000000 --- a/src/mod_proxy_core_backlog.c +++ /dev/null @@ -1,109 +0,0 @@ -#include <stdlib.h> - -#include "mod_proxy_core_backlog.h" -#include "array-static.h" - -proxy_backlog *proxy_backlog_init(void) { - STRUCT_INIT(proxy_backlog, backlog); - - return backlog; -} - -void proxy_backlog_free(proxy_backlog *backlog) { - if (!backlog) return; - - free(backlog); -} - -int proxy_backlog_push(proxy_backlog *backlog, proxy_request *req) { - /* first entry */ - if (NULL == backlog->first) { - backlog->first = backlog->last = req; - } else { - backlog->last->next = req; - backlog->last = req; - } - backlog->length++; - - return 0; -} - -/** - * remove the first element from the backlog - */ -proxy_request *proxy_backlog_shift(proxy_backlog *backlog) { - proxy_request *req = NULL; - - if (!backlog->first) return req; - - backlog->length--; - - req = backlog->first; - - backlog->first = req->next; - - /* the backlog is empty */ - if (backlog->first == NULL) backlog->last = NULL; - - return req; -} - -int proxy_backlog_remove_connection(proxy_backlog *backlog, void *con) { - proxy_request *req = NULL; - - if (!backlog->first) return -1; - if (!con) return -1; - - /* the first element is what we look for */ - if (backlog->first->con == con) { - req = backlog->first; - - backlog->first = req->next; - if (backlog->first == NULL) backlog->last = NULL; - - backlog->length--; - - proxy_request_free(req); - - return 0; - } - - - for (req = backlog->first; req && req->next; req = req->next) { - proxy_request *cur; - - if (req->next->con != con) continue; - - backlog->length--; - /* the next node is our searched connection */ - - cur = req->next; - req->next = cur->next; - - /* the next node is the last one, make the current the new last */ - if (cur == backlog->last) { - backlog->last = req; - } - cur->next = NULL; - - proxy_request_free(cur); - - return 0; - } - - return -1; -} - -proxy_request *proxy_request_init(void) { - STRUCT_INIT(proxy_request, request); - - return request; -} - -void proxy_request_free(proxy_request *request) { - if (!request) return; - - free(request); -} - - diff --git a/src/mod_proxy_core_backlog.h b/src/mod_proxy_core_backlog.h deleted file mode 100644 index a59141f8..00000000 --- a/src/mod_proxy_core_backlog.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef _MOD_PROXY_CORE_BACKLOG_H_ -#define _MOD_PROXY_CORE_BACKLOG_H_ - -#include <sys/types.h> -#ifndef _WIN32 -#include <sys/time.h> -#else -#include <time.h> -#endif - -typedef struct _proxy_request { - void *con; /* a pointer to the client-connection, (type: connection) */ - - time_t added_ts; /* when was the entry added (for timeout handling) */ - - struct _proxy_request *next; -} proxy_request; - -/** - * a we can't get a connection from the pool, queue the request in the - * request queue (FIFO) - * - * - the queue is infinite - * - entries are removed after a timeout (status 504) - */ -typedef struct { - proxy_request *first; /* pull() does q->first = q->first->next */ - proxy_request *last; /* push() does q->last = r */ - - size_t length; -} proxy_backlog; - -proxy_backlog *proxy_backlog_init(void); -void proxy_backlog_free(proxy_backlog *backlog); - -/** - * append a request to the end - * - * @return 0 in success, -1 if full - */ -int proxy_backlog_push(proxy_backlog *backlog, proxy_request *req); - -/** - * remove the first request from the backlog - * - * @return NULL if backlog is empty, the request otherwise - */ -proxy_request *proxy_backlog_shift(proxy_backlog *backlog); -/** - * remove the request with the connection 'con' from the backlog - * - * @return -1 if not found, 0 otherwise - */ -int proxy_backlog_remove_connection(proxy_backlog *backlog, void *con); - -proxy_request *proxy_request_init(void); -void proxy_request_free(proxy_request *req); - -#endif - - diff --git a/src/mod_proxy_core_pool.c b/src/mod_proxy_core_pool.c deleted file mode 100644 index 65606fa1..00000000 --- a/src/mod_proxy_core_pool.c +++ /dev/null @@ -1,141 +0,0 @@ - -#include <stdlib.h> - -#include "array-static.h" -#include "sys-files.h" -#include "log.h" -#include "mod_proxy_core_pool.h" - -proxy_connection * proxy_connection_init(void) { - proxy_connection *con; - - con = calloc(1, sizeof(*con)); - - con->sock = iosocket_init(); - - con->send = chunkqueue_init(); - con->recv = chunkqueue_init(); - - return con; -} - -void proxy_connection_free(proxy_connection *con) { - if (!con) return; - - con->address->used--; - - iosocket_free(con->sock); - - chunkqueue_free(con->send); - chunkqueue_free(con->recv); - - free(con); -} - -proxy_connection_pool *proxy_connection_pool_init(void) { - proxy_connection_pool *pool; - - pool = calloc(1, sizeof(*pool)); - - /* default: max parallel connections to the backend - * - * this should match max-procs if we manage the procs ourself - */ - - pool->max_size = 1; - - return pool; -} - -void proxy_connection_pool_free(proxy_connection_pool *pool) { - size_t i; - - if (!pool) return; - - for (i = 0; i < pool->used; i++) { - proxy_connection_free(pool->ptr[i]); - } - - if (pool->size) free(pool->ptr); - - free(pool); -} - -static void proxy_connection_pool_add_connection(proxy_connection_pool *pool, proxy_connection *c) { - ARRAY_STATIC_PREPARE_APPEND(pool); - - pool->ptr[pool->used++] = c; -} - -/** - * remove the connection from the pool - * - * usually called on conn-shutdown - */ -int proxy_connection_pool_remove_connection(proxy_connection_pool *pool, proxy_connection *c) { - size_t i; - - if (pool->used == 0) return -1; /* empty */ - - for (i = 0; i < pool->used; i++) { - if (pool->ptr[i] == c) { - break; - } - } - - if (i == pool->used) return -1; /* not found */ - - /** - * move all elements one to the left - * - * if the last element is going to be removed, skip the loop - */ - for (; i < pool->used - 1; i++) { - pool->ptr[i] = pool->ptr[i + 1]; - } - - pool->used--; - - return 0; -} - -proxy_connection_pool_t proxy_connection_pool_get_connection(proxy_connection_pool *pool, proxy_address *address, proxy_connection **rcon) { - proxy_connection *proxy_con = NULL; - size_t i; - - /* search for a idling proxy connection with the given address */ - for (i = 0; i < pool->used; i++) { - proxy_con = pool->ptr[i]; - - if (proxy_con->address == address && - proxy_con->state == PROXY_CONNECTION_STATE_IDLE) { - break; - } - } - - if (i == pool->used) { - /* no idling connection found - * - * check if we can open another connection to this address - */ - - if (pool->used == pool->max_size) return PROXY_CONNECTIONPOOL_FULL; - - proxy_con = proxy_connection_init(); - - proxy_con->state = PROXY_CONNECTION_STATE_CONNECTING; - proxy_con->address = address; - - proxy_connection_pool_add_connection(pool, proxy_con); - } else { - proxy_con->state = PROXY_CONNECTION_STATE_CONNECTED; - } - - /* inc. the use-counter of the address */ - proxy_con->address->used++; - - *rcon = proxy_con; - - return PROXY_CONNECTIONPOOL_GOT_CONNECTION; -} - diff --git a/src/mod_proxy_core_pool.h b/src/mod_proxy_core_pool.h deleted file mode 100644 index 62040d16..00000000 --- a/src/mod_proxy_core_pool.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef _MOD_PROXY_CORE_POOL_H_ -#define _MOD_PROXY_CORE_POOL_H_ - -#ifndef _WIN32 -#include <sys/time.h> -#else -#include <time.h> -#endif - -#include "iosocket.h" -#include "array-static.h" -#include "mod_proxy_core_address.h" -#include "chunk.h" - -typedef enum { - PROXY_CONNECTION_STATE_UNSET, - PROXY_CONNECTION_STATE_CONNECTING, - PROXY_CONNECTION_STATE_CONNECTED, - PROXY_CONNECTION_STATE_IDLE, - PROXY_CONNECTION_STATE_CLOSED, -} proxy_connection_state_t; - -/** - * a connection to a proxy backend - * - * the connection is independent of the incoming request to allow keep-alive - */ -typedef struct { - iosocket *sock; - - unsigned short request_count; /* used for max-keep-alive-requests */ - time_t last_read; /* timeout handling for keep-alive connections */ - time_t last_write; - - proxy_address *address; /* the struct sock_addr for the sock */ - - struct proxy_protocol *protocol; /* protocol handler */ - void *protocol_data; /** protocol handler's state data for parsing response from backend. */ - - chunkqueue *send; /* encoded stream data that needs to be send to backend server. */ - chunkqueue *recv; /* encoded stream data received form the backend that needs to be decoded. */ - - proxy_connection_state_t state; - time_t state_ts; - - void *proxy_sess; /** we are used by this proxy session right now */ -} proxy_connection; - -ARRAY_STATIC_DEF(proxy_connection_pool, proxy_connection, size_t max_size;); - -typedef enum { - PROXY_CONNECTIONPOOL_UNSET, - PROXY_CONNECTIONPOOL_FULL, - PROXY_CONNECTIONPOOL_GOT_CONNECTION, -} proxy_connection_pool_t; - -proxy_connection_pool *proxy_connection_pool_init(void); -void proxy_connection_pool_free(proxy_connection_pool *pool); - -proxy_connection_pool_t proxy_connection_pool_get_connection(proxy_connection_pool *pool, proxy_address *address, proxy_connection **rcon); -int proxy_connection_pool_remove_connection(proxy_connection_pool *pool, proxy_connection *c); - -proxy_connection * proxy_connection_init(void); -void proxy_connection_free(proxy_connection *pool); - -#endif - - diff --git a/src/mod_proxy_core_protocol.c b/src/mod_proxy_core_protocol.c deleted file mode 100644 index 66513df5..00000000 --- a/src/mod_proxy_core_protocol.c +++ /dev/null @@ -1,66 +0,0 @@ -#include <stdlib.h> - -#include "mod_proxy_core.h" -#include "mod_proxy_core_protocol.h" - -static proxy_protocols *protocols = NULL; -static buffer *protocol_names = NULL; - -proxy_protocol *proxy_protocol_init(void) { - proxy_protocol *protocol; - - protocol = calloc(1, sizeof(*protocol)); - - return protocol; -} - -void proxy_protocol_free(proxy_protocol *protocol) { - if (!protocol) return; - - buffer_free(protocol->name); - - free(protocol); -} - -void proxy_protocols_init(void) { - if(protocols) return; - protocols = calloc(1, sizeof(*protocols)); - protocol_names = buffer_init(); -} - -void proxy_protocols_free(void) { - if(!protocols) return; - ARRAY_STATIC_FREE(protocols, proxy_protocol, element, proxy_protocol_free(element)); - - free(protocols); - buffer_free(protocol_names); -} - -void proxy_protocols_register(proxy_protocol *protocol) { - if(!protocols) return; - ARRAY_STATIC_PREPARE_APPEND(protocols); - - protocols->ptr[protocols->used++] = protocol; - - /* append protocol name to list of names. */ - if(!buffer_is_empty(protocol_names)) { - buffer_append_string_len(protocol_names, CONST_STR_LEN(", '")); - } else { - buffer_append_string_len(protocol_names, CONST_STR_LEN("'")); - } - buffer_append_string(protocol_names, BUF_STR(protocol->name)); - buffer_append_string_len(protocol_names, CONST_STR_LEN("'")); -} - -proxy_protocol *proxy_get_protocol(buffer *name) { - if(!protocols) return NULL; - - FOREACH(protocols, proxy_protocol, element, if(buffer_is_equal(element->name,name)) return element; ); - - return NULL; -} - -const char *proxy_available_protocols() { - return BUF_STR(protocol_names); -} - diff --git a/src/mod_proxy_core_protocol.h b/src/mod_proxy_core_protocol.h deleted file mode 100644 index e846089f..00000000 --- a/src/mod_proxy_core_protocol.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _MOD_PROXY_CORE_PROTOCOL_H_ -#define _MOD_PROXY_CORE_PROTOCOL_H_ - -#include "base.h" -#include "array-static.h" -#include "buffer.h" - -#define PROXY_CONNECTION_FUNC(x) \ - static int x(server *srv, proxy_connection *proxy_con) - -#define PROXY_STREAM_DECODER_FUNC(x) \ - static handler_t x(server *srv, proxy_session *sess, chunkqueue *out) - -#define PROXY_STREAM_ENCODER_FUNC(x) \ - static handler_t x(server *srv, proxy_session *sess, chunkqueue *in) - -typedef struct proxy_protocol { - buffer *name; - - int (*proxy_stream_init) (server *srv, proxy_connection *proxy_con); - int (*proxy_stream_cleanup) (server *srv, proxy_connection *proxy_con); - handler_t (*proxy_stream_decoder) (server *srv, proxy_session *sess, chunkqueue *out); - handler_t (*proxy_stream_encoder) (server *srv, proxy_session *sess, chunkqueue *in); - handler_t (*proxy_encode_request_headers) (server *srv, proxy_session *sess, chunkqueue *in); - -} proxy_protocol; - -ARRAY_STATIC_DEF(proxy_protocols, proxy_protocol, ); - -proxy_protocol *proxy_protocol_init(void); -void proxy_protocol_free(proxy_protocol *protocol); - -void proxy_protocols_init(void); -void proxy_protocols_free(void); -void proxy_protocols_register(proxy_protocol *protocol); -proxy_protocol *proxy_get_protocol(buffer *name); -const char *proxy_available_protocols(void); - -#endif - diff --git a/src/mod_proxy_core_rewrites.c b/src/mod_proxy_core_rewrites.c deleted file mode 100644 index 234f8068..00000000 --- a/src/mod_proxy_core_rewrites.c +++ /dev/null @@ -1,127 +0,0 @@ -#include <stdlib.h> -#include <string.h> -#include <ctype.h> - -#include "mod_proxy_core_rewrites.h" -#include "log.h" - -proxy_rewrite *proxy_rewrite_init(void) { - STRUCT_INIT(proxy_rewrite, rewrite); - - rewrite->header = buffer_init(); - rewrite->match = buffer_init(); - rewrite->replace = buffer_init(); - - return rewrite; - -} -void proxy_rewrite_free(proxy_rewrite *rewrite) { - if (!rewrite) return; -#ifdef HAVE_PCRE_H - if (rewrite->regex) pcre_free(rewrite->regex); -#endif - - buffer_free(rewrite->header); - buffer_free(rewrite->match); - buffer_free(rewrite->replace); - - free(rewrite); -} - -int proxy_rewrite_set_regex(proxy_rewrite *rewrite, buffer *regex) { - const char *errptr; - int erroff; - -#ifdef HAVE_PCRE_H - if (NULL == (rewrite->regex = pcre_compile(BUF_STR(regex), - 0, &errptr, &erroff, NULL))) { - - TRACE("regex compilation for %s failed at %s", SAFE_BUF_STR(regex), errptr); - - return -1; - } -#endif - - return 0; -} - - -proxy_rewrites *proxy_rewrites_init(void) { - STRUCT_INIT(proxy_rewrites, rewrites); - - return rewrites; -} - -void proxy_rewrites_add(proxy_rewrites *rewrites, proxy_rewrite *rewrite) { - ARRAY_STATIC_PREPARE_APPEND(rewrites); - - rewrites->ptr[rewrites->used++] = rewrite; -} - -void proxy_rewrites_free(proxy_rewrites *rewrites) { - if (!rewrites) return; - - ARRAY_STATIC_FREE(rewrites, proxy_rewrite, rewrite, proxy_rewrite_free(rewrite)); - - free(rewrites); -} - -int pcre_replace(pcre *match, buffer *replace, buffer *match_buf, buffer *result) { -#ifdef HAVE_PCRE_H - const char *pattern = replace->ptr; - size_t pattern_len = replace->used - 1; - -# define N 10 - int ovec[N * 3]; - int n; - - if ((n = pcre_exec(match, NULL, match_buf->ptr, match_buf->used - 1, 0, 0, ovec, 3 * N)) < 0) { - if (n != PCRE_ERROR_NOMATCH) { - return n; - } - } else { - const char **list; - size_t start, end; - size_t k; - - /* it matched */ - pcre_get_substring_list(match_buf->ptr, ovec, n, &list); - - /* search for $[0-9] */ - - buffer_reset(result); - - start = 0; end = pattern_len; - for (k = 0; k < pattern_len; k++) { - if ((pattern[k] == '$') && - isdigit((unsigned char)pattern[k + 1])) { - /* got one */ - - size_t num = pattern[k + 1] - '0'; - - end = k; - - buffer_append_string_len(result, pattern + start, end - start); - - /* n is always > 0 */ - if (num < (size_t)n) { - buffer_append_string(result, list[num]); - } - - k++; - start = k + 1; - } - } - - buffer_append_string_len(result, pattern + start, pattern_len - start); - - pcre_free(list); - } - - return n; -#else - return -1; -#endif -} - - diff --git a/src/mod_proxy_core_rewrites.h b/src/mod_proxy_core_rewrites.h deleted file mode 100644 index bd7cc21a..00000000 --- a/src/mod_proxy_core_rewrites.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef _MOD_PROXY_CORE_REWRITES_H_ -#define _MOD_PROXY_CORE_REWRITES_H_ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef HAVE_PCRE_H -#include <pcre.h> -#endif -#include "array-static.h" -#include "buffer.h" - -#ifndef HAVE_PCRE_H -#define pcre void -#endif - -typedef struct { - buffer *header; - - pcre *regex; /* regex compiled from the <match> */ - - buffer *match; - buffer *replace; -} proxy_rewrite; - -ARRAY_STATIC_DEF(proxy_rewrites, proxy_rewrite,); - -proxy_rewrite *proxy_rewrite_init(void); -void proxy_rewrite_free(proxy_rewrite *rewrite); -int proxy_rewrite_set_regex(proxy_rewrite *rewrite, buffer *regex); - -proxy_rewrites *proxy_rewrites_init(void); -void proxy_rewrites_add(proxy_rewrites *rewrites, proxy_rewrite *rewrite); -void proxy_rewrites_free(proxy_rewrites *rewrites); - -int pcre_replace(pcre *match, buffer *replace, buffer *match_buf, buffer *result); - -#endif - diff --git a/src/mod_redirect.c b/src/mod_redirect.c deleted file mode 100644 index ed09f8dc..00000000 --- a/src/mod_redirect.c +++ /dev/null @@ -1,229 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" -#include "response.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -typedef struct { - pcre_keyvalue_buffer *redirect; - data_config *context; /* to which apply me */ - - unsigned short redirect_code; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - buffer *match_buf; - buffer *location; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -INIT_FUNC(mod_redirect_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->match_buf = buffer_init(); - p->location = buffer_init(); - - return p; -} - -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]; - - pcre_keyvalue_buffer_free(s->redirect); - - free(s); - } - free(p->config_storage); - } - - - buffer_free(p->match_buf); - buffer_free(p->location); - - free(p); - - return HANDLER_GO_ON; -} - -SETDEFAULTS_FUNC(mod_redirect_set_defaults) { - plugin_data *p = p_d; - data_unset *du; - 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - size_t j; - array *ca; - data_array *da = NULL; - - s = calloc(1, sizeof(plugin_config)); - s->redirect = pcre_keyvalue_buffer_init(); - - cv[0].destination = s->redirect; - cv[1].destination = &(s->redirect_code); - - p->config_storage[i] = s; - ca = ((data_config *)srv->config_context->data[i])->value; - - if (0 != config_insert_values_global(srv, ca, cv)) { - return HANDLER_ERROR; - } - - if (NULL == (du = array_get_element(ca, CONST_STR_LEN("url.redirect")))) { - /* no url.redirect defined */ - continue; - } - - if (du->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "sss", - "unexpected type for key: ", "url.redirect", "array of strings"); - - return HANDLER_ERROR; - } - - da = (data_array *)du; - - for (j = 0; j < da->value->used; j++) { - if (da->value->data[j]->type != TYPE_STRING) { - log_error_write(srv, __FILE__, __LINE__, "sssbs", - "unexpected type for key: ", - "url.redirect", - "[", da->value->data[j]->key, "](string)"); - - return HANDLER_ERROR; - } - - if (0 != pcre_keyvalue_buffer_append(s->redirect, - ((data_string *)(da->value->data[j]))->key->ptr, - ((data_string *)(da->value->data[j]))->value->ptr)) { - - log_error_write(srv, __FILE__, __LINE__, "sb", - "pcre-compile failed for", da->value->data[j]->key); - } - } - } - - return HANDLER_GO_ON; -} -#ifdef HAVE_PCRE_H -static int mod_redirect_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i, j; - plugin_config *s = p->config_storage[0]; - - PATCH_OPTION(redirect); - PATCH_OPTION(redirect_code); - - /* 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.redirect"))) { - PATCH_OPTION(redirect); - p->conf.context = dc; - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.redirect-code"))) { - PATCH_OPTION(redirect_code); - } - } - } - - return 0; -} -#endif -static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_data) { -#ifdef HAVE_PCRE_H - plugin_data *p = p_data; - int i; - - /* - * REWRITE URL - * - * e.g. redirect /base/ to /index.php?section=base - * - */ - - mod_redirect_patch_connection(srv, con, p); - - buffer_copy_string_buffer(p->match_buf, con->request.uri); - i = config_exec_pcre_keyvalue_buffer(con, p->conf.redirect, p->conf.context, p->match_buf, p->location); - - if (i >= 0) { - response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->location)); - - con->http_status = p->conf.redirect_code > 99 && p->conf.redirect_code < 1000 ? p->conf.redirect_code : 301; - con->send->is_closed = 1; - - return HANDLER_FINISHED; - } - else if (i != PCRE_ERROR_NOMATCH) { - log_error_write(srv, __FILE__, __LINE__, "s", - "execution error while matching", i); - } -#undef N - -#else - UNUSED(srv); - UNUSED(con); - UNUSED(p_data); -#endif - - return HANDLER_GO_ON; -} - - -LI_EXPORT int mod_redirect_plugin_init(plugin *p); -LI_EXPORT 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/src/mod_rewrite.c b/src/mod_rewrite.c deleted file mode 100644 index 94609300..00000000 --- a/src/mod_rewrite.c +++ /dev/null @@ -1,337 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -typedef struct { - pcre_keyvalue_buffer *rewrite; - buffer *once; - data_config *context; /* to which apply me */ -} plugin_config; - -typedef struct { - enum { REWRITE_STATE_UNSET, REWRITE_STATE_FINISHED} state; - int loops; -} handler_ctx; - -typedef struct { - PLUGIN_DATA; - buffer *match_buf; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -static handler_ctx * handler_ctx_init() { - 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) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->match_buf = buffer_init(); - - return p; -} - -FREE_FUNC(mod_rewrite_free) { - plugin_data *p = p_d; - - UNUSED(srv); - - if (!p) return HANDLER_GO_ON; - - buffer_free(p->match_buf); - 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; - - pcre_keyvalue_buffer_free(s->rewrite); - buffer_free(s->once); - - free(s); - } - free(p->config_storage); - } - - free(p); - - return HANDLER_GO_ON; -} - -static handler_t parse_config_entry(server *UNUSED_PARAM(srv), plugin_config *s, array *ca, const char *option, size_t option_len, int once) { - data_unset *du; - - if (NULL != (du = array_get_element(ca, option, option_len))) { - data_array *da = (data_array *)du; - size_t j; - - if (du->type != TYPE_ARRAY) { - ERROR("unexpected type (%d) for key %s, expected 'array of strings'", - du->type, option); - - return HANDLER_ERROR; - } - - da = (data_array *)du; - - for (j = 0; j < da->value->used; j++) { - if (da->value->data[j]->type != TYPE_STRING) { - ERROR("unexpected type (%d) for value of %s[%s], expected 'string'", - da->value->data[j]->type, option, - SAFE_BUF_STR(da->value->data[j]->key)); - - return HANDLER_ERROR; - } - - if (0 != pcre_keyvalue_buffer_append(s->rewrite, - ((data_string *)(da->value->data[j]))->key->ptr, - ((data_string *)(da->value->data[j]))->value->ptr)) { -#ifdef HAVE_PCRE_H - ERROR("pcre-compile failed for: %s", SAFE_BUF_STR(da->value->data[j]->key)); -#else - ERROR("pcre support is missing, please install libpcre and the headers%s", ""); -#endif - } - - if (once) { - buffer_append_string_len(s->once, CONST_STR_LEN("1")); - } else { - buffer_append_string_len(s->once, CONST_STR_LEN("0")); - } - } - } - - return HANDLER_GO_ON; -} - -SETDEFAULTS_FUNC(mod_rewrite_set_defaults) { - plugin_data *p = p_d; - 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 */ - - /* 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 }, /* 2 */ - { "url.rewrite-final", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ - { 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - array *ca; - - s = calloc(1, sizeof(plugin_config)); - s->rewrite = pcre_keyvalue_buffer_init(); - s->once = buffer_init(); - - p->config_storage[i] = s; - ca = ((data_config *)srv->config_context->data[i])->value; - - if (0 != config_insert_values_global(srv, ca, cv)) { - return HANDLER_ERROR; - } - - if (HANDLER_GO_ON != parse_config_entry(srv, s, ca, CONST_STR_LEN("url.rewrite-once"), 1)) return HANDLER_ERROR; - if (HANDLER_GO_ON != parse_config_entry(srv, s, ca, CONST_STR_LEN("url.rewrite-final"), 1)) return HANDLER_ERROR; - if (HANDLER_GO_ON != parse_config_entry(srv, s, ca, CONST_STR_LEN("url.rewrite"), 1)) return HANDLER_ERROR; - if (HANDLER_GO_ON != parse_config_entry(srv, s, ca, CONST_STR_LEN("url.rewrite-repeat"), 0)) return HANDLER_ERROR; - } - - return HANDLER_GO_ON; -} -#ifdef HAVE_PCRE_H -static int mod_rewrite_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i, j; - plugin_config *s = p->config_storage[0]; - p->conf.rewrite = s->rewrite; - p->conf.once = s->once; - - /* 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]; - - if (COMP_HTTP_URL == dc->comp) continue; - - /* 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"))) { - p->conf.rewrite = s->rewrite; - p->conf.once = s->once; - p->conf.context = dc; - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-once"))) { - p->conf.rewrite = s->rewrite; - p->conf.once = s->once; - p->conf.context = dc; - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-repeat"))) { - p->conf.rewrite = s->rewrite; - p->conf.once = s->once; - p->conf.context = dc; - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-final"))) { - p->conf.rewrite = s->rewrite; - p->conf.once = s->once; - p->conf.context = dc; - } - } - } - - return 0; -} -#endif -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; -} - -URIHANDLER_FUNC(mod_rewrite_uri_handler) { -#ifdef HAVE_PCRE_H - plugin_data *p = p_d; - int i; - handler_ctx *hctx = NULL; - - /* - * REWRITE URL - * - * e.g. rewrite /base/ to /index.php?section=base - * - */ - - if (con->plugin_ctx[p->id]) { - hctx = con->plugin_ctx[p->id]; - - if (hctx->loops++ > 100) { - ERROR("ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request after %d loops at %s", - hctx->loops, SAFE_BUF_STR(con->request.uri)); - - con->http_status = 500; - - return HANDLER_FINISHED; - } - - if (hctx->state == REWRITE_STATE_FINISHED) return HANDLER_GO_ON; - } - - mod_rewrite_patch_connection(srv, con, p); - - if (!p->conf.rewrite) return HANDLER_GO_ON; - - buffer_copy_string_buffer(p->match_buf, con->request.uri); - i = config_exec_pcre_keyvalue_buffer(con, p->conf.rewrite, p->conf.context, p->match_buf, con->request.uri); - - if (i >= 0) { - if (!hctx) { - hctx = handler_ctx_init(); - - con->plugin_ctx[p->id] = hctx; - } - - if (p->conf.once->ptr[i] == '1') - hctx->state = REWRITE_STATE_FINISHED; - - /* looks like we finished the rewrite - * - * start the uri-splitting again - * */ - - if (con->request.uri->used == 0 || - con->request.uri->ptr[0] != '/') { - con->http_status = 500; - - ERROR("url.rewrite contains a regex for '%s' which leads to a URI without a leading slash: %s", - SAFE_BUF_STR(p->match_buf), SAFE_BUF_STR(con->request.uri)); - - return HANDLER_FINISHED; - } - - return HANDLER_COMEBACK; - } else if (i != PCRE_ERROR_NOMATCH) { - ERROR("execution error while matching '%s' against '%s': %d", - SAFE_BUF_STR(p->match_buf), SAFE_BUF_STR(con->request.uri), i); - con->http_status = 500; - - return HANDLER_FINISHED; - } -#undef N - -#else - UNUSED(srv); - UNUSED(con); - UNUSED(p_d); -#endif - - return HANDLER_GO_ON; -} - -LI_EXPORT int mod_rewrite_plugin_init(plugin *p); -LI_EXPORT 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->set_defaults = mod_rewrite_set_defaults; - p->cleanup = mod_rewrite_free; - p->connection_reset = mod_rewrite_con_reset; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_rrdtool.c b/src/mod_rrdtool.c deleted file mode 100644 index f7e4fad1..00000000 --- a/src/mod_rrdtool.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * make sure _GNU_SOURCE is defined - */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include <sys/types.h> - -#include <fcntl.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <time.h> - -#include "server.h" -#include "connections.h" -#include "response.h" -#include "connections.h" -#include "log.h" - -#include "plugin.h" -#ifdef HAVE_FORK -/* no need for waitpid if we don't have fork */ -#include <sys/wait.h> -#endif - -#include "sys-files.h" -#include "sys-process.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; - - int rrdtool_running; - - plugin_config **config_storage; - plugin_config conf; -} plugin_data; - -INIT_FUNC(mod_rrd_init) { - plugin_data *p; - - UNUSED(srv); - - 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]; - - 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->rrdtool_pid) { - int status; - close(p->read_fd); - close(p->write_fd); -#ifdef HAVE_FORK - /* collect status */ - waitpid(p->rrdtool_pid, &status, 0); -#endif - } - - free(p); - - return HANDLER_GO_ON; -} - -static int mod_rrd_create_pipe(server *srv, plugin_data *p) { -#ifdef HAVE_FORK - pid_t pid; - - int to_rrdtool_fds[2]; - int from_rrdtool_fds[2]; - 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; - } - - /* fork, execve */ - switch (pid = fork()) { - case 0: { - /* child */ - char **args; - int argc; - int i = 0; - char *dash = "-"; - - /* move stdout to from_rrdtool_fd[1] */ - dup2(from_rrdtool_fds[1], STDOUT_FILENO); - close(from_rrdtool_fds[1]); - /* not needed */ - close(from_rrdtool_fds[0]); - - /* move the stdin to to_rrdtool_fd[0] */ - dup2(to_rrdtool_fds[0], STDIN_FILENO); - close(to_rrdtool_fds[0]); - /* not needed */ - close(to_rrdtool_fds[1]); - - /* set up args */ - argc = 3; - args = malloc(sizeof(*args) * argc); - i = 0; - - args[i++] = p->conf.path_rrdtool_bin->ptr; - args[i++] = dash; - args[i++] = NULL; - - /* we don't need the client socket */ - for (i = 3; i < 256; i++) { - close(i); - } - - /* exec the cgi */ - execv(args[0], args); - - /* */ - SEGFAULT("spawing '%s' failed: %s", args[0], strerror(errno)); - - break; - } - case -1: - /* error */ - ERROR("fork failed: %s", strerror(errno)); - break; - default: { - /* father */ - - close(from_rrdtool_fds[1]); - close(to_rrdtool_fds[0]); - - /* register PID and wait for them asyncronously */ - p->write_fd = to_rrdtool_fds[1]; - p->read_fd = from_rrdtool_fds[0]; - p->rrdtool_pid = pid; - -#ifdef FD_CLOEXEC - fcntl(p->write_fd, F_SETFD, FD_CLOEXEC); - fcntl(p->read_fd, F_SETFD, FD_CLOEXEC); -#endif - - break; - } - } - - return 0; -#else - return -1; -#endif -} - -/* 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, void *buf, size_t count) { - ssize_t res; - - for (;;) { - res = read(fd, buf, count); - if (res >= 0) return res; - switch (errno) { - case EINTR: - continue; - default: - return -1; - } - } -} - -static int mod_rrdtool_create_rrd(server *srv, plugin_data *p, plugin_config *s) { - struct stat st; - int r ; - - /* 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 == (r = safe_write(p->write_fd, p->cmd->ptr, p->cmd->used - 1))) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "rrdtool-write: failed", strerror(errno)); - - return HANDLER_ERROR; - } - - buffer_prepare_copy(p->resp, 4096); - if (-1 == (r = safe_read(p->read_fd, p->resp->ptr, p->resp->size))) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "rrdtool-read: failed", strerror(errno)); - - return HANDLER_ERROR; - } - - p->resp->used = r; - - 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; -} - -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_OPTION(path_rrdtool_bin); - PATCH_OPTION(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_OPTION(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; -} - -SETDEFAULTS_FUNC(mod_rrd_set_defaults) { - plugin_data *p = p_d; - size_t i; - - config_values_t cv[] = { - { "rrdtool.binary", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, - { "rrdtool.db-name", 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; 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, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - - if (i > 0 && !buffer_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; - } - - } - - p->conf.path_rrdtool_bin = p->config_storage[0]->path_rrdtool_bin; - p->rrdtool_running = 0; - - /* check for dir */ - - if (buffer_is_empty(p->conf.path_rrdtool_bin)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "rrdtool.binary has to be set"); - return HANDLER_ERROR; - } - - /* open the pipe to rrdtool */ - if (mod_rrd_create_pipe(srv, p)) { - return HANDLER_ERROR; - } - - p->rrdtool_running = 1; - - return HANDLER_GO_ON; -} - -TRIGGER_FUNC(mod_rrd_trigger) { - plugin_data *p = p_d; - size_t i; - - if (!p->rrdtool_running) 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]; - int r; - - if (buffer_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_ERROR; - - 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_off_t(p->cmd, s->bytes_read); - buffer_append_string_len(p->cmd, CONST_STR_LEN(":")); - buffer_append_off_t(p->cmd, s->bytes_written); - buffer_append_string_len(p->cmd, CONST_STR_LEN(":")); - buffer_append_long(p->cmd, s->requests); - buffer_append_string_len(p->cmd, CONST_STR_LEN("\n")); - - if (-1 == (r = safe_write(p->write_fd, p->cmd->ptr, p->cmd->used - 1))) { - p->rrdtool_running = 0; - - log_error_write(srv, __FILE__, __LINE__, "ss", - "rrdtool-write: failed", strerror(errno)); - - return HANDLER_ERROR; - } - - buffer_prepare_copy(p->resp, 4096); - if (-1 == (r = safe_read(p->read_fd, p->resp->ptr, p->resp->size))) { - p->rrdtool_running = 0; - - log_error_write(srv, __FILE__, __LINE__, "ss", - "rrdtool-read: failed", strerror(errno)); - - return HANDLER_ERROR; - } - - p->resp->used = r; - - 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))) { - p->rrdtool_running = 0; - - log_error_write(srv, __FILE__, __LINE__, "sbb", - "rrdtool-response:", p->cmd, p->resp); - - return HANDLER_ERROR; - } - } - s->requests = 0; - s->bytes_written = 0; - s->bytes_read = 0; - } - - return HANDLER_GO_ON; -} - -REQUESTDONE_FUNC(mod_rrd_account) { - plugin_data *p = p_d; - - 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; -} - -LI_EXPORT int mod_rrdtool_plugin_init(plugin *p); -LI_EXPORT 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_response_done = mod_rrd_account; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_secure_download.c b/src/mod_secure_download.c deleted file mode 100644 index a5d2b663..00000000 --- a/src/mod_secure_download.c +++ /dev/null @@ -1,351 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef USE_OPENSSL -# include <openssl/md5.h> -#else -# include "md5.h" - -typedef li_MD5_CTX MD5_CTX; -#define MD5_Init li_MD5_Init -#define MD5_Update li_MD5_Update -#define MD5_Final li_MD5_Final - -#endif - -#define HASHLEN 16 -typedef unsigned char HASH[HASHLEN]; -#define HASHHEXLEN 32 -typedef char HASHHEX[HASHHEXLEN+1]; -#ifdef USE_OPENSSL -#define IN const -#else -#define IN -#endif -#define OUT - - -/* plugin config for all request/connections */ - -typedef struct { - buffer *doc_root; - buffer *secret; - buffer *uri_prefix; - - unsigned int timeout; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - buffer *md5; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -/* init the plugin data */ -INIT_FUNC(mod_secdownload_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->md5 = buffer_init(); - - 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]; - - buffer_free(s->secret); - buffer_free(s->doc_root); - buffer_free(s->uri_prefix); - - free(s); - } - free(p->config_storage); - } - - buffer_free(p->md5); - - 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 */ - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - if (!p) return HANDLER_ERROR; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - s->secret = buffer_init(); - s->doc_root = buffer_init(); - s->uri_prefix = buffer_init(); - s->timeout = 60; - - cv[0].destination = s->secret; - cv[1].destination = s->doc_root; - cv[2].destination = s->uri_prefix; - cv[3].destination = &(s->timeout); - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -/** - * checks if the supplied string is a MD5 string - * - * @param str a possible MD5 string - * @return if the supplied string is a valid MD5 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; -} - -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_OPTION(secret); - PATCH_OPTION(doc_root); - PATCH_OPTION(uri_prefix); - PATCH_OPTION(timeout); - - /* 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_OPTION(secret); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.document-root"))) { - PATCH_OPTION(doc_root); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.uri-prefix"))) { - PATCH_OPTION(uri_prefix); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("secdownload.timeout"))) { - PATCH_OPTION(timeout); - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_secdownload_uri_handler) { - plugin_data *p = p_d; - MD5_CTX Md5Ctx; - HASH HA1; - const char *rel_uri, *ts_str, *md5_str; - time_t ts = 0; - size_t i; - - if (con->uri.path->used == 0) return HANDLER_GO_ON; - - mod_secdownload_patch_connection(srv, con, p); - - if (buffer_is_empty(p->conf.uri_prefix)) return HANDLER_GO_ON; - - if (buffer_is_empty(p->conf.secret)) { - ERROR("secdownload.secret has to be set: %s", ""); - - return HANDLER_ERROR; - } - - if (buffer_is_empty(p->conf.doc_root)) { - ERROR("secdownload.document-root has to be set: %s", ""); - - return HANDLER_ERROR; - } - - if (con->conf.log_request_handling) { - TRACE("-- handling %s in mod_secdownload", SAFE_BUF_STR(con->uri.path)); - } - - /* - * /<uri-prefix>[a-f0-9]{32}/[a-f0-9]{8}/<rel-path> - */ - - if (0 != strncmp(con->uri.path->ptr, p->conf.uri_prefix->ptr, p->conf.uri_prefix->used - 1)) { - if (con->conf.log_request_handling) { - TRACE("prefix '%s' didn't matched the url: %s", SAFE_BUF_STR(p->conf.uri_prefix), SAFE_BUF_STR(con->uri.path)); - } - - return HANDLER_GO_ON; - } - - md5_str = con->uri.path->ptr + p->conf.uri_prefix->used - 1; - - if (!is_hex_len(md5_str, 32)) { - if (con->conf.log_request_handling) { - TRACE("expected a 32-char hex-val as md5-hash: %s", SAFE_BUF_STR(con->uri.path)); - } - - return HANDLER_GO_ON; - } - if (*(md5_str + 32) != '/') { - if (con->conf.log_request_handling) { - TRACE("missing a / after the md5-hash: %s", SAFE_BUF_STR(con->uri.path)); - } - - return HANDLER_GO_ON; - } - - ts_str = md5_str + 32 + 1; - - if (!is_hex_len(ts_str, 8)) { - if (con->conf.log_request_handling) { - TRACE("expected a 8-char hex-val after md5-hash: %s", SAFE_BUF_STR(con->uri.path)); - } - - return HANDLER_GO_ON; - } - if (*(ts_str + 8) != '/') { - if (con->conf.log_request_handling) { - TRACE("missing a / after the timestamp: %s", SAFE_BUF_STR(con->uri.path)); - } - - 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) ) { - if (con->conf.log_request_handling) { - TRACE("timestamp is too old: %ld, timeout: %d", (long int) 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; - - /* checking MD5 - * - * <secret><rel-path><timestamp-hex> - */ - - buffer_copy_string_buffer(p->md5, p->conf.secret); - buffer_append_string(p->md5, rel_uri); - buffer_append_string_len(p->md5, ts_str, 8); - - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)p->md5->ptr, p->md5->used - 1); - MD5_Final(HA1, &Md5Ctx); - - buffer_copy_string_hex(p->md5, (char *)HA1, 16); - - if (0 != strncasecmp(md5_str, p->md5->ptr, 32)) { - con->http_status = 403; - - TRACE("MD5 didn't matched: %s == %s", md5_str, SAFE_BUF_STR(p->md5)); - - return HANDLER_FINISHED; - } - - /* starting with the last / we should have relative-path to the docroot - */ - - buffer_copy_string_buffer(con->physical.doc_root, p->conf.doc_root); - buffer_copy_string(con->physical.rel_path, rel_uri); - buffer_copy_string_buffer(con->physical.path, con->physical.doc_root); - buffer_append_string_buffer(con->physical.path, con->physical.rel_path); - - if (con->conf.log_request_handling) { - TRACE("MD5 matched, timestamp is ok, sending %s", SAFE_BUF_STR(con->physical.path)); - } - - return HANDLER_GO_ON; -} - -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_secdownload_plugin_init(plugin *p); -LI_EXPORT 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/src/mod_setenv.c b/src/mod_setenv.c deleted file mode 100644 index 026adb16..00000000 --- a/src/mod_setenv.c +++ /dev/null @@ -1,254 +0,0 @@ -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#include "response.h" - -/* plugin config for all request/connections */ - -typedef struct { - int handled; /* make sure that we only apply the headers once */ -} handler_ctx; - -typedef struct { - array *request_header; - array *response_header; - - array *environment; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -static handler_ctx * handler_ctx_init() { - 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; - - UNUSED(srv); - - 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]; - - array_free(s->request_header); - array_free(s->response_header); - array_free(s->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 */ - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - if (!p) return HANDLER_ERROR; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - s->request_header = array_init(); - s->response_header = array_init(); - s->environment = array_init(); - - cv[0].destination = s->request_header; - cv[1].destination = s->response_header; - cv[2].destination = s->environment; - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -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_OPTION(request_header); - PATCH_OPTION(response_header); - PATCH_OPTION(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_OPTION(request_header); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-response-header"))) { - PATCH_OPTION(response_header); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-environment"))) { - PATCH_OPTION(environment); - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_setenv_uri_handler) { - plugin_data *p = p_d; - size_t k; - handler_ctx *hctx; - - mod_setenv_patch_connection(srv, con, p); - - if (p->conf.request_header->used == 0 && - p->conf.environment->used == 0 && - p->conf.response_header->used == 0) { - return HANDLER_GO_ON; - } - - 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; - - for (k = 0; k < p->conf.request_header->used; k++) { - data_string *ds = (data_string *)p->conf.request_header->data[k]; - data_string *ds_dst; - - if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { - ds_dst = data_string_init(); - } - - buffer_copy_string_buffer(ds_dst->key, ds->key); - buffer_copy_string_buffer(ds_dst->value, ds->value); - - array_insert_unique(con->request.headers, (data_unset *)ds_dst); - } - - for (k = 0; k < p->conf.environment->used; k++) { - data_string *ds = (data_string *)p->conf.environment->data[k]; - data_string *ds_dst; - - if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { - ds_dst = data_string_init(); - } - - buffer_copy_string_buffer(ds_dst->key, ds->key); - buffer_copy_string_buffer(ds_dst->value, ds->value); - - array_insert_unique(con->environment, (data_unset *)ds_dst); - } - - for (k = 0; k < p->conf.response_header->used; k++) { - data_string *ds = (data_string *)p->conf.response_header->data[k]; - - response_header_insert(srv, con, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value)); - } - - /* not found */ - return HANDLER_GO_ON; -} - -REQUESTDONE_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 */ - -LI_EXPORT int mod_setenv_plugin_init(plugin *p); -LI_EXPORT 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_start_backend = mod_setenv_uri_handler; - 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/src/mod_simple_vhost.c b/src/mod_simple_vhost.c deleted file mode 100644 index eb100e1b..00000000 --- a/src/mod_simple_vhost.c +++ /dev/null @@ -1,282 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" -#include "stat_cache.h" - -#include "plugin.h" - -#include "sys-files.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -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; - - UNUSED(srv); - - 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]; - - 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; 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, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -static int build_doc_root(server *srv, connection *con, plugin_data *p, buffer *out, buffer *host) { - stat_cache_entry *sce = NULL; - - buffer_prepare_copy(out, 128); - - if (p->conf.server_root->used) { - buffer_copy_string_buffer(out, p->conf.server_root); - - if (host->used) { - /* a hostname has to start with a alpha-numerical character - * and must not contain a slash "/" - */ - char *dp; - - PATHNAME_APPEND_SLASH(out); - - if (NULL == (dp = strchr(host->ptr, ':'))) { - buffer_append_string_buffer(out, host); - } else { - buffer_append_string_len(out, host->ptr, dp - host->ptr); - } - } - PATHNAME_APPEND_SLASH(out); - - if (p->conf.document_root->used > 2 && p->conf.document_root->ptr[0] == '/') { - buffer_append_string_len(out, p->conf.document_root->ptr + 1, p->conf.document_root->used - 2); - } else { - buffer_append_string_buffer(out, p->conf.document_root); - PATHNAME_APPEND_SLASH(out); - } - } else { - buffer_copy_string_buffer(out, con->conf.document_root); - PATHNAME_APPEND_SLASH(out); - } - - 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; -} - -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_OPTION(server_root); - PATCH_OPTION(default_host); - PATCH_OPTION(document_root); - - PATCH_OPTION(docroot_cache_key); - PATCH_OPTION(docroot_cache_value); - PATCH_OPTION(docroot_cache_servername); - - PATCH_OPTION(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_OPTION(server_root); - PATCH_OPTION(docroot_cache_key); - PATCH_OPTION(docroot_cache_value); - PATCH_OPTION(docroot_cache_servername); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("simple-vhost.default-host"))) { - PATCH_OPTION(default_host); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("simple-vhost.document-root"))) { - PATCH_OPTION(document_root); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("simple-vhost.debug"))) { - PATCH_OPTION(debug); - } - } - } - - return 0; -} - -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); - - if (p->conf.docroot_cache_key->used && - con->uri.authority->used && - buffer_is_equal(p->conf.docroot_cache_key, con->uri.authority)) { - /* cache hit */ - buffer_copy_string_buffer(con->physical.doc_root, p->conf.docroot_cache_value); - buffer_copy_string_buffer(con->server_name, p->conf.docroot_cache_servername); - } else { - /* build document-root */ - if ((con->uri.authority->used == 0) || - build_doc_root(srv, con, p, p->doc_root, con->uri.authority)) { - /* not found, fallback the default-host */ - if (build_doc_root(srv, con, p, - p->doc_root, - p->conf.default_host)) { - return HANDLER_GO_ON; - } else { - buffer_copy_string_buffer(con->server_name, p->conf.default_host); - } - } else { - buffer_copy_string_buffer(con->server_name, con->uri.authority); - } - - /* copy to cache */ - buffer_copy_string_buffer(p->conf.docroot_cache_key, con->uri.authority); - buffer_copy_string_buffer(p->conf.docroot_cache_value, p->doc_root); - buffer_copy_string_buffer(p->conf.docroot_cache_servername, con->server_name); - - buffer_copy_string_buffer(con->physical.doc_root, p->doc_root); - } - - return HANDLER_GO_ON; -} - - -LI_EXPORT int mod_simple_vhost_plugin_init(plugin *p); -LI_EXPORT 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/src/mod_skeleton.c b/src/mod_skeleton.c deleted file mode 100644 index d79b08e9..00000000 --- a/src/mod_skeleton.c +++ /dev/null @@ -1,207 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -/** - * this is a skeleton for a lighttpd plugin - * - * just replaces every occurance 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; - - buffer *match_buf; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -typedef struct { - size_t foo; -} handler_ctx; - -static handler_ctx * handler_ctx_init() { - 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_skeleton_init) { - plugin_data *p; - - p = calloc(1, sizeof(*p)); - - p->match_buf = buffer_init(); - - return p; -} - -/* detroy 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) { - 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->match); - - free(s); - } - free(p->config_storage); - } - - buffer_free(p->match_buf); - - 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - s->match = array_init(); - - cv[0].destination = s->match; - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -static int mod_skeleton_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i, j; - plugin_config *s = p->config_storage[0]; - - PATCH_OPTION(match); - - /* 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("skeleton.array"))) { - PATCH_OPTION(match); - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_skeleton_uri_handler) { - plugin_data *p = p_d; - int s_len; - size_t k, i; - - UNUSED(srv); - - if (con->uri.path->used == 0) return HANDLER_GO_ON; - - mod_skeleton_patch_connection(srv, con, p); - - s_len = con->uri.path->used - 1; - - for (k = 0; k < p->conf.match->used; k++) { - data_string *ds = (data_string *)p->conf.match->data[k]; - int ct_len = ds->value->used - 1; - - if (ct_len > s_len) continue; - if (ds->value->used == 0) continue; - - if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) { - con->http_status = 403; - - return HANDLER_FINISHED; - } - } - - /* not found */ - return HANDLER_GO_ON; -} - -/* this function is called at dlopen() time and inits the callbacks */ - -int mod_skeleton_plugin_init(plugin *p) { - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("skeleton"); - - p->init = mod_skeleton_init; - p->handle_uri_clean = mod_skeleton_uri_handler; - p->set_defaults = mod_skeleton_set_defaults; - p->cleanup = mod_skeleton_free; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_sql_vhost_core.c b/src/mod_sql_vhost_core.c deleted file mode 100644 index 8cef1c8b..00000000 --- a/src/mod_sql_vhost_core.c +++ /dev/null @@ -1,384 +0,0 @@ -#include <stdio.h> -#include <errno.h> -#include <fcntl.h> -#include <string.h> - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "plugin.h" -#include "log.h" -#include "sys-files.h" - -#include "stat_cache.h" - -#include "mod_sql_vhost_core.h" - -#define plugin_data mod_sql_vhost_core_plugin_data -#define plugin_config mod_sql_vhost_core_plugin_config - -typedef struct { - buffer *docroot; - time_t added_ts; - - time_t ttl; -} cached_vhost; - -/* init the plugin data */ -INIT_FUNC(mod_sql_vhost_core_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->docroot = buffer_init(); - p->host = buffer_init(); - - return p; -} - -/* cleanup the plugin data */ -SERVER_FUNC(mod_sql_vhost_core_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; - - buffer_free(s->db); - buffer_free(s->user); - buffer_free(s->pass); - buffer_free(s->sock); - buffer_free(s->backend); - buffer_free(s->hostname); - buffer_free(s->select_vhost); -#ifdef HAVE_GLIB_H - g_hash_table_destroy(s->vhost_table); -#endif - - free(s); - } - free(p->config_storage); - } - buffer_free(p->docroot); - buffer_free(p->host); - - free(p); - - return HANDLER_GO_ON; -} - -#ifdef HAVE_GLIB_H -#if 0 -static cached_vhost *cached_vhost_init(void) { - cached_vhost *vhost; - - vhost = g_new0(cached_vhost, 1); - - return vhost; -} -#endif - -static void cached_vhost_free(cached_vhost *vhost) { - if (!vhost) return; - - if (vhost->docroot) buffer_free(vhost->docroot); - - g_free(vhost); -} - -static void cached_vhost_free_hash(gpointer vhost) { - cached_vhost_free(vhost); -} - -static uint buffer_hash(gconstpointer key) { - buffer *b = (buffer *)key; - - return g_str_hash(b->ptr); -} - -static gboolean buffer_hash_equal(gconstpointer _a, gconstpointer _b) { - buffer *a = (buffer *)_a; - buffer *b = (buffer *)_b; - - return buffer_is_equal(a, b); -} - -static void buffer_hash_free(gpointer d) { - buffer *b = d; - - buffer_free(b); -} -#endif - -#define CONFIG_CACHE_TTL "sql-vhost.cache-ttl" -#define CONFIG_DEBUG "sql-vhost.debug" - -/* set configuration values */ -SERVER_FUNC(mod_sql_vhost_core_set_defaults) { - plugin_data *p = p_d; - - size_t i = 0; - - config_values_t cv[] = { - { "sql-vhost.db", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 0 * e.g. vhost */ - { "sql-vhost.user", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 1 * lighty */ - { "sql-vhost.pass", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 2 * secrect */ - { "sql-vhost.sock", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 3 * /tmp/mysql.sock */ - { "sql-vhost.select-vhost", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 4 * SELECT ... FROM hosts WHERE hostname = ? */ - { "sql-vhost.hostname", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 5 * 127.0.0.1 */ - { "sql-vhost.port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 6 * 3306 */ - { "sql-vhost.backend", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 7 * mysql */ - - /* backward compat */ - { "mysql-vhost.db", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 8 == 0 */ - { "mysql-vhost.user", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 9 == 1 */ - { "mysql-vhost.pass", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 10 == 2 */ - { "mysql-vhost.sock", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 11 == 3 */ - { "mysql-vhost.sql", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER }, /* 12 == 4 */ - { "mysql-vhost.hostname", NULL, T_CONFIG_STRING,T_CONFIG_SCOPE_SERVER }, /* 13 == 5 */ - { "mysql-vhost.port", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 14 == 6 */ - - { CONFIG_CACHE_TTL, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 15 * 60 */ - { CONFIG_DEBUG, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 15 * 60 */ - - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - s->db = buffer_init(); - s->user = buffer_init(); - s->pass = buffer_init(); - s->sock = buffer_init(); - s->hostname = buffer_init(); - s->backend = buffer_init(); - s->port = 0; /* default port for mysql */ - s->select_vhost = buffer_init(); - s->backend_data = NULL; -#ifdef HAVE_GLIB_H - s->vhost_table = g_hash_table_new_full(buffer_hash, buffer_hash_equal, buffer_hash_free, cached_vhost_free_hash); -#endif - s->cache_ttl = 60; - s->debug = 0; - - cv[0].destination = s->db; - cv[1].destination = s->user; - cv[2].destination = s->pass; - cv[3].destination = s->sock; - cv[4].destination = s->select_vhost; - cv[5].destination = s->hostname; - cv[6].destination = &(s->port); - cv[7].destination = s->backend; - - /* backend compat */ - cv[8].destination = cv[0].destination; - cv[9].destination = cv[1].destination; - cv[10].destination = cv[2].destination; - cv[11].destination = cv[3].destination; - cv[12].destination = cv[4].destination; - cv[13].destination = cv[5].destination; - cv[14].destination = cv[6].destination; - - cv[15].destination = &(s->cache_ttl); - cv[16].destination = &(s->debug); - - p->config_storage[i] = s; - - if (config_insert_values_global(srv, - ((data_config *)srv->config_context->data[i])->value, - cv)) return HANDLER_ERROR; - - /* we only parse the config, the backend plugin will patch itself into the plugin-struct */ - } - - return HANDLER_GO_ON; -} - -static int mod_sql_vhost_core_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i, j; - plugin_config *s = p->config_storage[0]; - - PATCH_OPTION(backend_data); - PATCH_OPTION(get_vhost); -#ifdef HAVE_GLIB_H - PATCH_OPTION(vhost_table); -#endif - PATCH_OPTION(cache_ttl); - PATCH_OPTION(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; - - if (s->backend_data) { - PATCH_OPTION(backend_data); - PATCH_OPTION(get_vhost); -#ifdef HAVE_GLIB_H - PATCH_OPTION(vhost_table); -#endif - } - - 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_CACHE_TTL))) { - PATCH_OPTION(cache_ttl); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DEBUG))) { - PATCH_OPTION(debug); - } - } - } - - return 0; -} - -/* handle document root request - * - * glib: if available we cache the entries - * - * */ -CONNECTION_FUNC(mod_sql_vhost_core_handle_docroot) { - plugin_data *p = p_d; - stat_cache_entry *sce; -#ifdef HAVE_GLIB_H - cached_vhost *vhost = NULL; -#endif - - /* no host specified? */ - if (!con->uri.authority->used) return HANDLER_GO_ON; - - mod_sql_vhost_core_patch_connection(srv, con, p); - - /* do we have backend ? */ - if (!p->conf.get_vhost) return HANDLER_GO_ON; - -#ifdef HAVE_GLIB_H - if (p->conf.cache_ttl == 0 || /* 1. we don't cache */ - NULL == (vhost = g_hash_table_lookup(p->conf.vhost_table, con->uri.authority)) || /* 2. check if the host is already known */ - srv->cur_ts - vhost->added_ts >= p->conf.cache_ttl - ) { /* 3. the cache value is old */ - /* ask the backend for the data */ - if (p->conf.debug) TRACE("cache-miss for %s", SAFE_BUF_STR(con->uri.authority)); - - if (HANDLER_GO_ON != p->conf.get_vhost(srv, con, p->conf.backend_data, p->docroot, p->host)) { - return HANDLER_GO_ON; - } - - if (p->conf.cache_ttl > 0) { - /* check if the cache-ttl is > 0, otherwise we would always trash the cache-entry */ - if (vhost) { - if (p->conf.debug) TRACE("refreshing %s: %s", SAFE_BUF_STR(con->uri.authority), SAFE_BUF_STR(p->docroot)); - buffer_copy_string_buffer(vhost->docroot, p->docroot); - vhost->added_ts = srv->cur_ts; - } else { - buffer *key; - - vhost = g_new0(cached_vhost, 1); - vhost->docroot = buffer_init_buffer(p->docroot); - vhost->added_ts = srv->cur_ts; - vhost->ttl = p->conf.cache_ttl; - - key = buffer_init_buffer(con->uri.authority); - - if (p->conf.debug) TRACE("adding %s: %s", SAFE_BUF_STR(key), SAFE_BUF_STR(vhost->docroot)); - - g_hash_table_insert(p->conf.vhost_table, key, vhost); - - g_assert(g_hash_table_lookup(p->conf.vhost_table, key)); - } - } - } else { - if (p->conf.debug) TRACE("cache-hit for %s: %s", SAFE_BUF_STR(vhost->docroot), SAFE_BUF_STR(con->uri.authority)); - - buffer_copy_string_buffer(p->docroot, vhost->docroot); - } -#else - if (HANDLER_GO_ON != p->conf.get_vhost(srv, con, p->conf.backend_data, p->docroot, p->host)) { - return HANDLER_GO_ON; - } -#endif - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, p->docroot, &sce)) { - ERROR("stat_cache_get_entry(%s) failed: %s", SAFE_BUF_STR(p->docroot), strerror(errno)); - - return HANDLER_GO_ON; - } - if (!S_ISDIR(sce->st.st_mode)) { - ERROR("%s is not a dir", SAFE_BUF_STR(p->docroot)); - - return HANDLER_GO_ON; - } - - buffer_copy_string_buffer(con->server_name, p->host); - buffer_copy_string_buffer(con->physical.doc_root, p->docroot); - - return HANDLER_GO_ON; -} - -#if 0 -#ifdef HAVE_GLIB_H -static gboolean cached_vhost_remove_expired(gpointer _key, gpointer _val, gpointer data) { -// buffer *key = _key; - cached_vhost *val = _val; - server *srv = data; - UNUSED(_key); - - return (srv->cur_ts - val->added_ts > val->ttl); -} -#endif - -TRIGGER_FUNC(mod_sql_vhost_core_trigger) { - plugin_data *p = p_d; - size_t i; - - /* test once every 10 seconds */ - if (srv->cur_ts % 10 != 0) return HANDLER_GO_ON; - -#ifdef HAVE_GLIB_H - /* cleanup all caches */ - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s = p->config_storage[i]; - - if (s->vhost_table) { - g_hash_table_foreach_remove(s->vhost_table, cached_vhost_remove_expired, srv); - } - } -#endif - - return HANDLER_GO_ON; -} -#endif - -/* this function is called at dlopen() time and inits the callbacks */ -LI_EXPORT int mod_sql_vhost_core_plugin_init(plugin *p); -LI_EXPORT int mod_sql_vhost_core_plugin_init(plugin *p) { - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("mod_sql_vhost_core"); - - p->init = mod_sql_vhost_core_init; - p->cleanup = mod_sql_vhost_core_cleanup; - - p->set_defaults = mod_sql_vhost_core_set_defaults; - p->handle_docroot = mod_sql_vhost_core_handle_docroot; - - return 0; -} - diff --git a/src/mod_sql_vhost_core.h b/src/mod_sql_vhost_core.h deleted file mode 100644 index 2d70f62d..00000000 --- a/src/mod_sql_vhost_core.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef _MOD_SQL_VHOST_CORE_H_ -#define _MOD_SQL_VHOST_CORE_H_ - -#include "buffer.h" -#include "plugin.h" - -#ifdef HAVE_GLIB_H -#include <glib.h> -#endif - -#define SQLVHOST_BACKEND_GETVHOST_PARAMS \ - (server *srv, connection *con, void *p_d, buffer *docroot, buffer *host) - -#define SQLVHOST_BACKEND_GETVHOST_RETVAL handler_t - -#define SQLVHOST_BACKEND_GETVHOST(name) \ - SQLVHOST_BACKEND_GETVHOST_RETVAL name SQLVHOST_BACKEND_GETVHOST_PARAMS - -#define SQLVHOST_BACKEND_GETVHOST_PTR(name) \ - SQLVHOST_BACKEND_GETVHOST_RETVAL (* name)SQLVHOST_BACKEND_GETVHOST_PARAMS - -typedef struct { - buffer *db; - buffer *user; - buffer *pass; - buffer *sock; - - buffer *hostname; - unsigned short port; - - buffer *backend; - void *backend_data; - - buffer *select_vhost; - - unsigned short cache_ttl; - unsigned short debug; - -#ifdef HAVE_GLIB_H - GHashTable *vhost_table; -#endif - - SQLVHOST_BACKEND_GETVHOST_PTR(get_vhost); -} mod_sql_vhost_core_plugin_config; - -/* global plugin data */ -typedef struct { - PLUGIN_DATA; - - buffer *docroot; - buffer *host; - - mod_sql_vhost_core_plugin_config **config_storage; - - mod_sql_vhost_core_plugin_config conf; -} mod_sql_vhost_core_plugin_data; - - - -#endif diff --git a/src/mod_ssi.c b/src/mod_ssi.c deleted file mode 100644 index 91370e01..00000000 --- a/src/mod_ssi.c +++ /dev/null @@ -1,1095 +0,0 @@ -#include <sys/types.h> - -#include <ctype.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> -#include <time.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" -#include "stat_cache.h" - -#include "plugin.h" -#include "stream.h" - -#include "response.h" - -#include "mod_ssi.h" - -#include "inet_ntop_cache.h" - -#include "sys-socket.h" -#include "sys-strings.h" -#include "sys-files.h" - -#ifdef HAVE_PWD_H -#include <pwd.h> -#endif - -#ifdef HAVE_FORK -#include <sys/wait.h> -#endif - -#ifdef HAVE_SYS_FILIO_H -#include <sys/filio.h> -#endif - -/* init the plugin data */ -INIT_FUNC(mod_ssi_init) { - plugin_data *p; - - UNUSED(srv); - - 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]; - - 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); -#ifdef HAVE_PCRE_H - pcre_free(p->ssi_regex); -#endif - 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; -#ifdef HAVE_PCRE_H - const char *errptr; - int erroff; -#endif - - 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 */ - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - if (!p) return HANDLER_ERROR; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - s->ssi_extension = array_init(); - s->content_type = buffer_init(); - - cv[0].destination = s->ssi_extension; - cv[1].destination = s->content_type; - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - -#ifdef HAVE_PCRE_H - /* allow 2 params */ - if (NULL == (p->ssi_regex = pcre_compile("<!--#([a-z]+)\\s+(?:([a-z]+)=\"(.*?)(?<!\\\\)\"\\s*)?(?:([a-z]+)=\"(.*?)(?<!\\\\)\"\\s*)?-->", 0, &errptr, &erroff, NULL))) { - log_error_write(srv, __FILE__, __LINE__, "sds", - "ssi: pcre ", - erroff, errptr); - return HANDLER_ERROR; - } -#else - log_error_write(srv, __FILE__, __LINE__, "s", - "mod_ssi: pcre support is missing, please recompile with pcre support or remove mod_ssi from the list of modules"); - return HANDLER_ERROR; -#endif - - return HANDLER_GO_ON; -} - -static int ssi_env_add(array *env, const char *key, const char *val) { - data_string *ds; - - if (NULL == (ds = (data_string *)array_get_unused_element(env, TYPE_STRING))) { - ds = data_string_init(); - } - buffer_copy_string(ds->key, key); - buffer_copy_string(ds->value, val); - - array_insert_unique(env, (data_unset *)ds); - - return 0; -} - -/** - * - * the next two functions are take from fcgi.c - * - */ - -static int ssi_env_add_request_headers(server *srv, connection *con, plugin_data *p) { - size_t i; - - for (i = 0; i < con->request.headers->used; i++) { - data_string *ds; - - ds = (data_string *)con->request.headers->data[i]; - - if (ds->value->used && ds->key->used) { - size_t j; - buffer_reset(srv->tmp_buf); - - /* don't forward the Authorization: Header */ - if (0 == strcasecmp(ds->key->ptr, "AUTHORIZATION")) { - continue; - } - - if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) { - buffer_copy_string_len(srv->tmp_buf, CONST_STR_LEN("HTTP_")); - srv->tmp_buf->used--; - } - - buffer_prepare_append(srv->tmp_buf, ds->key->used + 2); - for (j = 0; j < ds->key->used - 1; j++) { - char c = '_'; - if (light_isalpha(ds->key->ptr[j])) { - /* upper-case */ - c = ds->key->ptr[j] & ~32; - } else if (light_isdigit(ds->key->ptr[j])) { - /* copy */ - c = ds->key->ptr[j]; - } - srv->tmp_buf->ptr[srv->tmp_buf->used++] = c; - } - srv->tmp_buf->ptr[srv->tmp_buf->used] = '\0'; - - ssi_env_add(p->ssi_cgi_env, srv->tmp_buf->ptr, ds->value->ptr); - } - } - - return 0; -} - -static int build_ssi_cgi_vars(server *srv, connection *con, plugin_data *p) { - char buf[32]; - - server_socket *srv_sock = con->srv_socket; - -#ifdef HAVE_IPV6 - char b2[INET6_ADDRSTRLEN + 1]; -#endif - -#define CONST_STRING(x) \ - x - - array_reset(p->ssi_cgi_env); - - ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_SOFTWARE"), PACKAGE_NAME"/"PACKAGE_VERSION); - ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_NAME"), -#ifdef HAVE_IPV6 - inet_ntop(srv_sock->addr.plain.sa_family, - srv_sock->addr.plain.sa_family == AF_INET6 ? - (const void *) &(srv_sock->addr.ipv6.sin6_addr) : - (const void *) &(srv_sock->addr.ipv4.sin_addr), - b2, sizeof(b2)-1) -#else - inet_ntoa(srv_sock->addr.ipv4.sin_addr) -#endif - ); - ssi_env_add(p->ssi_cgi_env, CONST_STRING("GATEWAY_INTERFACE"), "CGI/1.1"); - - LI_ltostr(buf, -#ifdef HAVE_IPV6 - ntohs(srv_sock->addr.plain.sa_family ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port) -#else - ntohs(srv_sock->addr.ipv4.sin_port) -#endif - ); - - ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_PORT"), buf); - - ssi_env_add(p->ssi_cgi_env, CONST_STRING("REMOTE_ADDR"), - inet_ntop_cache_get_ip(srv, &(con->dst_addr))); - - if (con->authed_user->used) { - ssi_env_add(p->ssi_cgi_env, CONST_STRING("REMOTE_USER"), - con->authed_user->ptr); - } - - if (con->request.content_length > 0) { - /* CGI-SPEC 6.1.2 and FastCGI spec 6.3 */ - - /* request.content_length < SSIZE_MAX, see request.c */ - LI_ltostr(buf, con->request.content_length); - ssi_env_add(p->ssi_cgi_env, CONST_STRING("CONTENT_LENGTH"), buf); - } - - /* - * 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) - */ - - ssi_env_add(p->ssi_cgi_env, CONST_STRING("SCRIPT_NAME"), con->uri.path->ptr); - ssi_env_add(p->ssi_cgi_env, CONST_STRING("PATH_INFO"), ""); - - /* - * 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. - * TODO: this code should be checked against cgi.fix_pathinfo php - * parameter. - */ - - if (con->request.pathinfo->used) { - ssi_env_add(p->ssi_cgi_env, CONST_STRING("PATH_INFO"), con->request.pathinfo->ptr); - } - - ssi_env_add(p->ssi_cgi_env, CONST_STRING("SCRIPT_FILENAME"), con->physical.path->ptr); - ssi_env_add(p->ssi_cgi_env, CONST_STRING("DOCUMENT_ROOT"), con->physical.doc_root->ptr); - - ssi_env_add(p->ssi_cgi_env, CONST_STRING("REQUEST_URI"), con->request.uri->ptr); - ssi_env_add(p->ssi_cgi_env, CONST_STRING("QUERY_STRING"), con->uri.query->used ? con->uri.query->ptr : ""); - ssi_env_add(p->ssi_cgi_env, CONST_STRING("REQUEST_METHOD"), get_http_method_name(con->request.http_method)); - ssi_env_add(p->ssi_cgi_env, CONST_STRING("REDIRECT_STATUS"), "200"); - ssi_env_add(p->ssi_cgi_env, CONST_STRING("SERVER_PROTOCOL"), get_http_version_name(con->request.http_version)); - - ssi_env_add_request_headers(srv, con, p); - - return 0; -} - -static int process_ssi_stmt(server *srv, connection *con, plugin_data *p, - const char **l, size_t n) { - size_t i, ssicmd = 0; - char buf[255]; - buffer *b = NULL; - - 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 } 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 }, - - { 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, enc = 0; - const char *var_val = NULL; - stat_cache_entry *sce = NULL; - - 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 } 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 }, - - { NULL, SSI_ECHO_UNSET } - }; - - 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: unknow 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; - } - - stat_cache_get_entry(srv, con, con->physical.path, &sce); - - switch(var) { - case SSI_ECHO_USER_NAME: { - struct passwd *pw; - - b = chunkqueue_get_append_buffer(con->send); -#ifdef HAVE_PWD_H - if (NULL == (pw = getpwuid(sce->st.st_uid))) { - buffer_copy_long(b, sce->st.st_uid); - } else { - buffer_copy_string(b, pw->pw_name); - } -#else - buffer_copy_long(b, sce->st.st_uid); -#endif - break; - } - case SSI_ECHO_LAST_MODIFIED: { - time_t t = sce->st.st_mtime; - - b = chunkqueue_get_append_buffer(con->send); - if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { - buffer_copy_string_len(b, CONST_STR_LEN("(none)")); - } else { - buffer_copy_string(b, buf); - } - break; - } - case SSI_ECHO_DATE_LOCAL: { - time_t t = time(NULL); - - b = chunkqueue_get_append_buffer(con->send); - if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { - buffer_copy_string_len(b, CONST_STR_LEN("(none)")); - } else { - buffer_copy_string(b, buf); - } - break; - } - case SSI_ECHO_DATE_GMT: { - time_t t = time(NULL); - - b = chunkqueue_get_append_buffer(con->send); - if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, gmtime(&t))) { - buffer_copy_string_len(b, CONST_STR_LEN("(none)")); - } else { - buffer_copy_string(b, buf); - } - break; - } - case SSI_ECHO_DOCUMENT_NAME: { - char *sl; - - b = chunkqueue_get_append_buffer(con->send); - if (NULL == (sl = strrchr(con->physical.path->ptr, '/'))) { - buffer_copy_string_buffer(b, con->physical.path); - } else { - buffer_copy_string(b, sl + 1); - } - break; - } - case SSI_ECHO_DOCUMENT_URI: { - b = chunkqueue_get_append_buffer(con->send); - buffer_copy_string_buffer(b, con->uri.path); - break; - } - default: { - data_string *ds; - /* check if it is a cgi-var */ - - b = chunkqueue_get_append_buffer(con->send); - - if (NULL != (ds = (data_string *)array_get_element(p->ssi_cgi_env, var_val, strlen(var_val)))) { - buffer_copy_string_buffer(b, ds->value); - } else { - buffer_copy_string_len(b, 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 st; - 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: unknow 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); - buffer_path_simplify(srv->tmp_buf, srv->tmp_buf); - buffer_append_string_buffer(p->stat_fn, srv->tmp_buf); - } else { - /* virtual */ - - if (virt_path[0] == '/') { - buffer_copy_string(p->stat_fn, virt_path); - } else { - /* there is always a / */ - sl = strrchr(con->uri.path->ptr, '/'); - - buffer_copy_string_len(p->stat_fn, con->uri.path->ptr, sl - con->uri.path->ptr + 1); - buffer_append_string(p->stat_fn, virt_path); - } - - buffer_urldecode_path(p->stat_fn); - buffer_path_simplify(srv->tmp_buf, p->stat_fn); - - /* we have an uri */ - - buffer_copy_string_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, &st)) { - time_t t = st.st_mtime; - - switch (ssicmd) { - case SSI_FSIZE: - b = chunkqueue_get_append_buffer(con->send); - if (p->sizefmt) { - int j = 0; - const char *abr[] = { " B", " kB", " MB", " GB", " TB", NULL }; - - off_t s = st.st_size; - - for (j = 0; s > 1024 && abr[j+1]; s /= 1024, j++); - - buffer_copy_off_t(b, s); - buffer_append_string(b, abr[j]); - } else { - buffer_copy_off_t(b, st.st_size); - } - break; - case SSI_FLASTMOD: - b = chunkqueue_get_append_buffer(con->send); - if (0 == strftime(buf, sizeof(buf), p->timefmt->ptr, localtime(&t))) { - buffer_copy_string_len(b, CONST_STR_LEN("(none)")); - } else { - buffer_copy_string(b, buf); - } - break; - case SSI_INCLUDE: - chunkqueue_append_file(con->send, p->stat_fn, 0, st.st_size); - 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: unknow attribute for ", - l[1], l[i]); - } - } - - if (p->if_is_false) break; - - if (key && val) { - data_string *ds; - - if (NULL == (ds = (data_string *)array_get_unused_element(p->ssi_vars, TYPE_STRING))) { - ds = data_string_init(); - } - buffer_copy_string(ds->key, key); - buffer_copy_string(ds->value, val); - - array_insert_unique(p->ssi_vars, (data_unset *)ds); - } else { - log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: var and value have to be set in", - l[0], l[1]); - } - 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], "abbrev")) { - p->sizefmt = 0; - } else { - log_error_write(srv, __FILE__, __LINE__, "sssss", - "ssi: unknow value for attribute '", - l[i], - "' for ", - l[1], l[i+1]); - } - } else { - log_error_write(srv, __FILE__, __LINE__, "sss", - "ssi: unknow attribute for ", - l[1], l[i]); - } - } - break; - case SSI_PRINTENV: - if (p->if_is_false) break; - - b = chunkqueue_get_append_buffer(con->send); - buffer_copy_string_len(b, CONST_STR_LEN("<pre>")); - 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_buffer(b, ds->value); - buffer_append_string_len(b, CONST_STR_LEN("<br />")); - - } - buffer_append_string_len(b, CONST_STR_LEN("</pre>")); - - break; - case SSI_EXEC: { -#ifndef _WIN32 - - const char *cmd = NULL; - pid_t pid; - int from_exec_fds[2]; - - 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: unknow attribute for ", - l[1], l[i]); - } - } - - if (p->if_is_false) break; - - /* create a return pipe and send output to the html-page - * - * as exec is assumed evil it is implemented synchronously - */ - - if (!cmd) break; - - if (pipe(from_exec_fds)) { - log_error_write(srv, __FILE__, __LINE__, "ss", - "pipe failed: ", strerror(errno)); - return -1; - } - - /* fork, execve */ - switch (pid = fork()) { - case 0: { - /* move stdout to from_rrdtool_fd[1] */ - close(STDOUT_FILENO); - dup2(from_exec_fds[1], STDOUT_FILENO); - close(from_exec_fds[1]); - /* not needed */ - close(from_exec_fds[0]); - - /* close stdin */ - close(STDIN_FILENO); - - execl("/bin/sh", "sh", "-c", cmd, (char *)NULL); - - /* */ - SEGFAULT("spawing '%s' failed: %s", cmd, strerror(errno)); - break; - } - case -1: - /* error */ - log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); - break; - default: { - /* father */ - int status; - ssize_t r; - - close(from_exec_fds[1]); - - /* wait for the client to end */ - if (-1 == waitpid(pid, &status, 0)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed:", strerror(errno)); - } else if (WIFEXITED(status)) { - int toread; - /* read everything from client and paste it into the output */ - - while(1) { - if (ioctl(from_exec_fds[0], FIONREAD, &toread)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "unexpected end-of-file (perhaps the ssi-exec process died)"); - return -1; - } - - if (toread > 0) { - b = chunkqueue_get_append_buffer(con->send); - - buffer_prepare_copy(b, toread + 1); - - if ((r = read(from_exec_fds[0], b->ptr, b->size - 1)) < 0) { - /* read failed */ - break; - } else { - b->used = r; - b->ptr[b->used++] = '\0'; - } - } else { - break; - } - } - } else { - log_error_write(srv, __FILE__, __LINE__, "s", "process exited abnormally"); - } - close(from_exec_fds[0]); - - break; - } - } -#else - return -1; -#endif - - 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: unknow 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: unknow 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; - default: - log_error_write(srv, __FILE__, __LINE__, "ss", - "ssi: unknow ssi-command:", - l[1]); - break; - } - - return 0; - -} - -static int mod_ssi_handle_request(server *srv, connection *con, plugin_data *p) { - stream s; -#ifdef HAVE_PCRE_H - int i, n; - -#define N 10 - int ovec[N * 3]; -#endif - - /* get a stream to the file */ - - array_reset(p->ssi_vars); - array_reset(p->ssi_cgi_env); - buffer_copy_string_len(p->timefmt, CONST_STR_LEN("%a, %d %b %Y %H:%M:%S %Z")); - p->sizefmt = 0; - build_ssi_cgi_vars(srv, con, p); - p->if_is_false = 0; - - if (-1 == stream_open(&s, con->physical.path)) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "stream-open: ", con->physical.path); - return -1; - } - - - /** - * <!--#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. - * - */ -#ifdef HAVE_PCRE_H - for (i = 0; (n = pcre_exec(p->ssi_regex, NULL, s.start, s.size, i, 0, ovec, N * 3)) > 0; i = ovec[1]) { - const char **l; - /* take everything from last offset to current match pos */ - - if (!p->if_is_false) chunkqueue_append_file(con->send, con->physical.path, i, ovec[0] - i); - - pcre_get_substring_list(s.start, ovec, n, &l); - process_ssi_stmt(srv, con, p, l, n); - pcre_free_substring_list(l); - } - - switch(n) { - case PCRE_ERROR_NOMATCH: - /* copy everything/the rest */ - chunkqueue_append_file(con->send, con->physical.path, i, s.size - i); - - break; - default: - log_error_write(srv, __FILE__, __LINE__, "sd", - "execution error while matching: ", n); - break; - } -#endif - - - stream_close(&s); - - con->file_started = 1; - con->send->is_closed = 1; - - if (p->conf.content_type->used <= 1) { - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); - } else { - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->conf.content_type)); - } - - /* reset physical.path */ - buffer_reset(con->physical.path); - - return 0; -} - -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_OPTION(ssi_extension); - PATCH_OPTION(content_type); - - /* 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_OPTION(ssi_extension); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssi.content-type"))) { - PATCH_OPTION(content_type); - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_ssi_physical_path) { - plugin_data *p = p_d; - size_t k; - - if (con->physical.path->used == 0) return HANDLER_GO_ON; - - mod_ssi_patch_connection(srv, con, p); - - for (k = 0; k < p->conf.ssi_extension->used; k++) { - data_string *ds = (data_string *)p->conf.ssi_extension->data[k]; - - if (ds->value->used == 0) continue; - - if (buffer_is_equal_right_len(con->physical.path, ds->value, ds->value->used - 1)) { - /* handle ssi-request */ - - if (mod_ssi_handle_request(srv, con, p)) { - /* on error */ - con->http_status = 500; - } - - return HANDLER_FINISHED; - } - } - - /* not found */ - return HANDLER_GO_ON; -} - -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_ssi_plugin_init(plugin *p); -LI_EXPORT 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_start_backend = mod_ssi_physical_path; - p->set_defaults = mod_ssi_set_defaults; - p->cleanup = mod_ssi_free; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_ssi.h b/src/mod_ssi.h deleted file mode 100644 index 241e8320..00000000 --- a/src/mod_ssi.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _MOD_SSI_H_ -#define _MOD_SSI_H_ - -#include "base.h" -#include "buffer.h" -#include "array.h" - -#include "plugin.h" - -#ifdef HAVE_PCRE_H -#include <pcre.h> -#endif - -/* plugin config for all request/connections */ - -typedef struct { - array *ssi_extension; - buffer *content_type; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - -#ifdef HAVE_PCRE_H - pcre *ssi_regex; -#endif - 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; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -int ssi_eval_expr(server *srv, connection *con, plugin_data *p, const char *expr); - -#endif diff --git a/src/mod_ssi_expr.c b/src/mod_ssi_expr.c deleted file mode 100644 index 0766e50d..00000000 --- a/src/mod_ssi_expr.c +++ /dev/null @@ -1,325 +0,0 @@ -#include <ctype.h> -#include <string.h> - -#include "mod_ssi.h" -#include "mod_ssi_expr.h" -#include "mod_ssi_exprparser.h" - -#include "buffer.h" -#include "log.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() { - 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 B->str->used > 1 ? 1 : 0; - } else { - return B->bo; - } -} - -static int ssi_expr_tokenizer(server *srv, connection *con, plugin_data *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++); - - buffer_copy_string_len(token, t->input + t->offset + 1, i-1); - } - - tid = TK_VALUE; - - if (NULL != (ds = (data_string *)array_get_element(p->ssi_cgi_env, CONST_BUF_LEN(token)))) { - buffer_copy_string_buffer(token, ds->value); - } else if (NULL != (ds = (data_string *)array_get_element(p->ssi_vars, CONST_BUF_LEN(token)))) { - buffer_copy_string_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, plugin_data *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 ); - 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/src/mod_ssi_expr.h b/src/mod_ssi_expr.h deleted file mode 100644 index 2d3ae8bb..00000000 --- a/src/mod_ssi_expr.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _MOD_SSI_EXPR_H_ -#define _MOD_SSI_EXPR_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 ssi_val_free(ssi_val_t *s); - -#endif diff --git a/src/mod_ssi_exprparser.y b/src/mod_ssi_exprparser.y deleted file mode 100644 index ffaafd05..00000000 --- a/src/mod_ssi_exprparser.y +++ /dev/null @@ -1,122 +0,0 @@ -%token_prefix TK_ -%token_type {buffer *} -%extra_argument {ssi_ctx_t *ctx} -%name ssiexprparser - -%include { -#include <assert.h> -#include <string.h> -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include "mod_ssi_expr.h" -#include "buffer.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/src/mod_staticfile.c b/src/mod_staticfile.c deleted file mode 100644 index 1be1d594..00000000 --- a/src/mod_staticfile.c +++ /dev/null @@ -1,552 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#include "stat_cache.h" -#include "etag.h" -#include "response.h" - -#include "sys-files.h" -#include "sys-strings.h" - -#include "http_req_range.h" -/** - * this is a staticfile for a lighttpd plugin - * - */ - - - -/* plugin config for all request/connections */ - -typedef struct { - array *exclude_ext; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - buffer *range_buf; - - http_req_range *ranges; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -/* init the plugin data */ -INIT_FUNC(mod_staticfile_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->range_buf = buffer_init(); - - p->ranges = http_request_range_init(); - - return p; -} - -/* destroy 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]; - - array_free(s->exclude_ext); - - free(s); - } - free(p->config_storage); - } - buffer_free(p->range_buf); - - http_request_range_free(p->ranges); - - 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 */ - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - if (!p) return HANDLER_ERROR; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - s->exclude_ext = array_init(); - - cv[0].destination = s->exclude_ext; - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -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_OPTION(exclude_ext); - - /* 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_OPTION(exclude_ext); - } - } - } - - return 0; -} - -static int http_response_parse_range(server *srv, connection *con, plugin_data *p) { - int multipart = 0; - char *boundary = "fkj49sn38dcn3"; - data_string *ds; - stat_cache_entry *sce = NULL; - buffer *content_type = NULL; - buffer *range = NULL; - http_req_range *ranges, *r; - - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Range")))) { - range = ds->value; - } else { - /* we don't have a Range header */ - - return -1; - } - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { - SEGFAULT("stat_cache_get_entry(%s) returned %d", SAFE_BUF_STR(con->physical.path), HANDLER_ERROR); - } - - con->response.content_length = 0; - - if (NULL != (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Content-Type")))) { - content_type = ds->value; - } - - /* start the range-header parser - * bytes=<num> */ - - ranges = p->ranges; - http_request_range_reset(ranges); - switch (http_request_range_parse(range, ranges)) { - case PARSE_ERROR: - return -1; /* no range valid Range Header */ - case PARSE_SUCCESS: - break; - default: - TRACE("%s", "foobar"); - return -1; - } - - if (ranges->next) { - multipart = 1; - } - - /* patch the '-1' */ - for (r = ranges; r; r = r->next) { - if (r->start == -1) { - /* -<end> - * - * the last <end> bytes */ - r->start = sce->st.st_size - r->end; - r->end = sce->st.st_size - 1; - } - if (r->end == -1) { - /* <start>- - * all but the first <start> bytes */ - - r->end = sce->st.st_size - 1; - } - - if (r->end > sce->st.st_size - 1) { - /* RFC 2616 - 14.35.1 - * - * if last-byte-pos not present or > size-of-file - * take the size-of-file - * - * */ - r->end = sce->st.st_size - 1; - } - - if (r->start > sce->st.st_size - 1) { - /* RFC 2616 - 14.35.1 - * - * if first-byte-pos > file-size, 416 - */ - - con->http_status = 416; - return -1; - } - - if (r->start > r->end) { - /* RFC 2616 - 14.35.1 - * - * if last-byte-pos is present, it has to be >= first-byte-pos - * - * invalid ranges have to be handle as no Range specified - * */ - - return -1; - } - } - - if (r) { - /* we ran into an range violation */ - return -1; - } - - if (multipart) { - buffer *b; - for (r = ranges; r; r = r->next) { - /* write boundary-header */ - - b = chunkqueue_get_append_buffer(con->send); - - buffer_copy_string_len(b, CONST_STR_LEN("\r\n--")); - buffer_append_string(b, boundary); - - /* write Content-Range */ - buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes ")); - buffer_append_off_t(b, r->start); - buffer_append_string_len(b, CONST_STR_LEN("-")); - buffer_append_off_t(b, r->end); - buffer_append_string_len(b, CONST_STR_LEN("/")); - buffer_append_off_t(b, sce->st.st_size); - - 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 += b->used - 1; - con->send->bytes_in += b->used - 1; - - chunkqueue_append_file(con->send, con->physical.path, r->start, r->end - r->start + 1); - con->response.content_length += r->end - r->start + 1; - con->send->bytes_in += r->end - r->start + 1; - } - - /* add boundary end */ - b = chunkqueue_get_append_buffer(con->send); - - buffer_copy_string_len(b, "\r\n--", 4); - buffer_append_string(b, boundary); - buffer_append_string_len(b, "--\r\n", 4); - - con->response.content_length += b->used - 1; - con->send->bytes_in += b->used - 1; - - /* set header-fields */ - - buffer_copy_string_len(p->range_buf, CONST_STR_LEN("multipart/byteranges; boundary=")); - buffer_append_string(p->range_buf, boundary); - - /* overwrite content-type */ - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->range_buf)); - - } else { - r = ranges; - - chunkqueue_append_file(con->send, con->physical.path, r->start, r->end - r->start + 1); - con->response.content_length += r->end - r->start + 1; - con->send->bytes_in += r->end - r->start + 1; - - buffer_copy_string_len(p->range_buf, CONST_STR_LEN("bytes ")); - buffer_append_off_t(p->range_buf, r->start); - buffer_append_string_len(p->range_buf, CONST_STR_LEN("-")); - buffer_append_off_t(p->range_buf, r->end); - buffer_append_string_len(p->range_buf, CONST_STR_LEN("/")); - buffer_append_off_t(p->range_buf, sce->st.st_size); - - response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(p->range_buf)); - } - - /* ok, the file is set-up */ - return 0; -} - -URIHANDLER_FUNC(mod_staticfile_subrequest) { - plugin_data *p = p_d; - size_t k; - int s_len; - stat_cache_entry *sce = NULL; - buffer *mtime; - data_string *ds; - - if (con->conf.log_request_handling) { - TRACE("-- %s", "checking file for static file"); - } - - - /* someone else has done a decision for us */ - if (con->http_status != 0) return HANDLER_GO_ON; - if (con->uri.path->used == 0) return HANDLER_GO_ON; - if (con->physical.path->used == 0) 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 (con->conf.log_request_handling) { - TRACE("-- %s", "handling file as static file"); - } - - - s_len = con->uri.path->used - 1; - - /* ignore certain extensions */ - for (k = 0; k < p->conf.exclude_ext->used; k++) { - ds = (data_string *)p->conf.exclude_ext->data[k]; - - if (ds->value->used == 0) continue; - - if (buffer_is_equal_right_len(con->physical.path, ds->value, ds->value->used - 1)) { - if (con->conf.log_request_handling) { - TRACE("'%s' matched exclude(%s), sending 403", - SAFE_BUF_STR(con->physical.path), - SAFE_BUF_STR(ds->value)); - } - - con->http_status = 403; - - return HANDLER_FINISHED; - } - } - - - 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) { - 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_ISREG(sce->st.st_mode)) { - con->http_status = 404; - - if (con->conf.log_file_not_found) { - log_error_write(srv, __FILE__, __LINE__, "sbsb", - "not a regular file:", con->uri.path, - "->", sce->name); - } - - return HANDLER_FINISHED; - } - - /* mod_compress might set several parameters directly; don't overwrite them */ - - /* set response content-type, if not set already */ - - if (NULL == array_get_element(con->response.headers, CONST_STR_LEN("Content-Type"))) { - if (buffer_is_empty(sce->content_type)) { - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream")); - } else { - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type)); - } - } - - if (NULL == array_get_element(con->response.headers, CONST_STR_LEN("ETag"))) { - /* generate e-tag */ - etag_mutate(con->physical.etag, sce->etag); - - response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); - } - if (con->conf.range_requests) { - response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes")); - } - - /* prepare header */ - if (NULL == (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Last-Modified")))) { - mtime = strftime_cache_get(srv, sce->st.st_mtime); - response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); - } else { - mtime = ds->value; - } - - if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { - return HANDLER_FINISHED; - } else if (con->conf.range_requests && - NULL != array_get_element(con->request.headers, CONST_STR_LEN("Range"))) { - int do_range_request = 1; - /* check if we have a conditional GET */ - - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("If-Range")))) { - /* if the value is the same as our ETag, we do a Range-request, - * otherwise a full 200 */ - - if (!buffer_is_equal(ds->value, con->physical.etag)) { - do_range_request = 0; - } - } - - if (do_range_request) { - /* content prepared, I'm done */ - con->send->is_closed = 1; - - if (0 == http_response_parse_range(srv, con, p)) { - con->http_status = 206; - } - return HANDLER_FINISHED; - } - } - - /* if we are still here, prepare body */ - - /* we add it here for all requests - * the HEAD request will drop it afterwards again - */ - chunkqueue_append_file(con->send, con->physical.path, 0, sce->st.st_size); - - con->send->is_closed = 1; - con->send->bytes_in = sce->st.st_size; - - return HANDLER_FINISHED; -} - -/** - * mark all the content as read - */ -CONNECTION_FUNC(mod_staticfile_dev_null) { - chunk *c; - chunkqueue *in = con->recv; - - UNUSED(srv); - UNUSED(p_d); - - if (con->mode != DIRECT) return HANDLER_GO_ON; - - /* there is nothing that we have to send out anymore */ - if (in->bytes_in == in->bytes_out && - in->is_closed) return HANDLER_GO_ON; - - for (c = in->first; in->bytes_out < in->bytes_in; c = c->next) { - off_t weWant = in->bytes_in - in->bytes_out; - off_t weHave = 0; - - /* we announce toWrite octects - * now take all the request_content chunk that we need to fill this request - */ - - switch (c->type) { - case FILE_CHUNK: - weHave = c->file.length - c->offset; - - if (weHave > weWant) weHave = weWant; - - c->offset += weHave; - in->bytes_out += weHave; - - break; - case MEM_CHUNK: - /* append to the buffer */ - weHave = c->mem->used - 1 - c->offset; - - if (weHave > weWant) weHave = weWant; - - c->offset += weHave; - in->bytes_out += weHave; - - break; - default: - break; - } - } - - return HANDLER_GO_ON; - -} -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_staticfile_plugin_init(plugin *p); -LI_EXPORT 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_start_backend = mod_staticfile_subrequest; - p->handle_send_request_content = mod_staticfile_dev_null; - p->set_defaults = mod_staticfile_set_defaults; - p->cleanup = mod_staticfile_free; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_status.c b/src/mod_status.c deleted file mode 100644 index 459db41c..00000000 --- a/src/mod_status.c +++ /dev/null @@ -1,893 +0,0 @@ -/* - * make sure _GNU_SOURCE is defined. - */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif -#include <sys/types.h> - -#include <fcntl.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <time.h> -#include <stdio.h> - -#include "server.h" -#include "connections.h" -#include "response.h" -#include "connections.h" -#include "log.h" -#include "status_counter.h" -#include "network_backends.h" - -#include "plugin.h" - -#include "inet_ntop_cache.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 *tmp_buf; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -INIT_FUNC(mod_status_init) { - plugin_data *p; - size_t i; - - UNUSED(srv); - - 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->tmp_buf = 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->tmp_buf); - - if (p->config_storage) { - size_t i; - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s = p->config_storage[i]; - - 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; 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, ((data_config *)srv->config_context->data[i])->value, cv)) { - 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; - size_t j; - double avg; - char multiplier = '\0'; - char buf[128]; - time_t ts; - - int days, hours, mins, seconds; - - b = chunkqueue_get_append_buffer(con->send); - - 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 (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</h1>")); - - buffer_append_string_len(b, CONST_STR_LEN("<table class=\"status\" id=\"status\" summary=\"Server Status\">")); - buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Hostname</td><td class=\"string\"><span id=\"host_addr\">")); - buffer_append_string_buffer(b, con->uri.authority); - buffer_append_string_len(b, CONST_STR_LEN("</span> (<span id=\"host_name\">")); - buffer_append_string_buffer(b, con->server_name); - buffer_append_string_len(b, CONST_STR_LEN("</span>)</td></tr>\n")); - buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Uptime</td><td class=\"string\" id=\"uptime\">")); - - 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_long(b, days); - buffer_append_string_len(b, CONST_STR_LEN(" days ")); - } - - if (hours) { - buffer_append_long(b, hours); - buffer_append_string_len(b, CONST_STR_LEN(" hours ")); - } - - if (mins) { - buffer_append_long(b, mins); - buffer_append_string_len(b, CONST_STR_LEN(" min ")); - } - - buffer_append_long(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, "<span id=\"start_date\">%Y-%m-%d</span> <span id=\"start_time\">%H:%M:%S</span>", 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\" ><span id=\"requests\">")); - avg = p->abs_requests; - - mod_status_get_multiplier(&avg, &multiplier, 1000); - - buffer_append_long(b, avg); - buffer_append_string_len(b, CONST_STR_LEN("</span> <span id=\"requests_mult\">")); - if (multiplier) buffer_append_string_len(b, &multiplier, 1); - buffer_append_string_len(b, CONST_STR_LEN("</span>req</td></tr>\n")); - - buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\"><span id=\"traffic\">")); - avg = p->abs_traffic_out; - - mod_status_get_multiplier(&avg, &multiplier, 1024); - - sprintf(buf, "%.2f", avg); - buffer_append_string(b, buf); - buffer_append_string_len(b, CONST_STR_LEN("</span> <span id=\"traffic_mult\">")); - if (multiplier) buffer_append_string_len(b, &multiplier, 1); - buffer_append_string_len(b, CONST_STR_LEN("</span>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\"><span id=\"requests_avg\">")); - avg = p->abs_requests / (srv->cur_ts - srv->startup_ts); - - mod_status_get_multiplier(&avg, &multiplier, 1000); - - buffer_append_long(b, avg); - buffer_append_string_len(b, CONST_STR_LEN("</span> <span id=\"requests_avg_mult\">")); - if (multiplier) buffer_append_string_len(b, &multiplier, 1); - buffer_append_string_len(b, CONST_STR_LEN("</span>req/s</td></tr>\n")); - - buffer_append_string_len(b, CONST_STR_LEN("<tr><td>Traffic</td><td class=\"string\"><span id=\"traffic_avg\">")); - avg = p->abs_traffic_out / (srv->cur_ts - srv->startup_ts); - - mod_status_get_multiplier(&avg, &multiplier, 1024); - - sprintf(buf, "%.2f", avg); - buffer_append_string(b, buf); - buffer_append_string_len(b, CONST_STR_LEN("</span> <span id=\"traffic_avg_mult\">")); - if (multiplier) buffer_append_string_len(b, &multiplier, 1); - buffer_append_string_len(b, CONST_STR_LEN("</span>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\"><span id=\"requests_sliding_avg\">")); - - mod_status_get_multiplier(&avg, &multiplier, 1000); - - buffer_append_long(b, avg); - buffer_append_string_len(b, CONST_STR_LEN("</span> <span id=\"requests_sliding_avg_mult\">")); - if (multiplier) buffer_append_string_len(b, &multiplier, 1); - - buffer_append_string_len(b, CONST_STR_LEN("</span>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\"><span id=\"requests_sliding_traffic\">")); - - mod_status_get_multiplier(&avg, &multiplier, 1024); - - sprintf(buf, "%.2f", avg); - buffer_append_string(b, buf); - buffer_append_string_len(b, CONST_STR_LEN("</span> <span id=\"requests_sliding_traffic_mult\">")); - if (multiplier) buffer_append_string_len(b, &multiplier, 1); - buffer_append_string_len(b, CONST_STR_LEN("</span>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><b>legend</b>\n")); - buffer_append_string_len(b, CONST_STR_LEN(". = connect, C = close, E = hard error\n")); - buffer_append_string_len(b, CONST_STR_LEN("r = read, R = read-POST, W = write, h = handle-request\n")); - buffer_append_string_len(b, CONST_STR_LEN("q = request-start, Q = request-end\n")); - buffer_append_string_len(b, CONST_STR_LEN("s = response-start, S = response-end\n")); - - buffer_append_string_len(b, CONST_STR_LEN("<strong><span id=\"connections\">")); - buffer_append_long(b, srv->conns->used); - buffer_append_string_len(b, CONST_STR_LEN("</span> connections</strong>\n")); - - for (j = 0; j < srv->conns->used; j++) { - connection *c = srv->conns->ptr[j]; - const char *state = connection_get_short_state(c->state); - - 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</pre><hr />\n<h2>Connections</h2>\n")); - - buffer_append_string_len(b, CONST_STR_LEN("<table class=\"status\" summary=\"Current connections\" id=\"clients\">\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 ip\">")); - - buffer_append_string(b, inet_ntop_cache_get_ip(srv, &(c->dst_addr))); - - buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int bytes_read\">")); - - if (c->request.content_length != -1) { - buffer_append_long(b, c->recv->bytes_in); - buffer_append_string_len(b, CONST_STR_LEN("/")); - buffer_append_long(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 bytes_written\">")); - - buffer_append_off_t(b, c->send_raw->bytes_out); - buffer_append_string_len(b, CONST_STR_LEN("/")); - buffer_append_off_t(b, c->send_raw->bytes_in); - - buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string state\">")); - - buffer_append_string(b, connection_get_state(c->state)); - - buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"int time\">")); - - buffer_append_long(b, srv->cur_ts - c->request_start); - - buffer_append_string_len(b, CONST_STR_LEN("</td><td class=\"string host\">")); - - if (buffer_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 uri\">")); - - if (!buffer_is_empty(c->uri.path)) { - buffer_append_string_encoded(b, CONST_BUF_LEN(c->uri.path), ENCODING_HTML); - } - - if (!buffer_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_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 file\">")); - - 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" - - " </body>\n" - "</html>\n" - )); - - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); - con->send->bytes_in += b->used-1; - - 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; - double avg; - time_t ts; - size_t j; - unsigned int k; - unsigned int l; - - b = chunkqueue_get_append_buffer(con->send); - - /* output total number of requests */ - buffer_append_string_len(b, CONST_STR_LEN("Total Accesses: ")); - avg = p->abs_requests; - buffer_append_long(b, avg); - 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; - buffer_append_long(b, avg); - 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_long(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_long(b, srv->conns->used); - buffer_append_string_len(b, CONST_STR_LEN("\n")); - - buffer_append_string_len(b, CONST_STR_LEN("IdleServers: ")); - buffer_append_long(b, srv->conns->size - srv->conns->used); - buffer_append_string_len(b, CONST_STR_LEN("\n")); - - /* output traffic */ - buffer_append_string_len(b, CONST_STR_LEN("Traffic: ")); - avg = p->abs_traffic_out / (srv->cur_ts - srv->startup_ts); - buffer_append_long(b, avg); - buffer_append_string_len(b, CONST_STR_LEN("\n")); - - /* output traffic 5s */ - buffer_append_string_len(b, CONST_STR_LEN("Traffic5s: ")); - for (j = 0, avg = 0; j < 5; j++) { - avg += p->mod_5s_traffic_out[j]; - } - avg /= 5; - buffer_append_long(b, avg); - 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 = 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")); - - /* set text/plain output */ - - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain")); - con->send->bytes_in += b->used-1; - - return 0; -} - -static handler_t mod_status_handle_server_statistics(server *srv, connection *con, void *p_d) { - buffer *b; - size_t i; - array *st = status_counter_get_array(); - - UNUSED(p_d); - - if (0 == st->used) { - /* we have nothing to send */ - con->http_status = 204; - con->send->is_closed = 1; - - return HANDLER_FINISHED; - } - - b = chunkqueue_get_append_buffer(con->send); - - 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_long(b, ((data_integer *)(st->data[ndx]))->value); - buffer_append_string_len(b, CONST_STR_LEN("\n")); - } - - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/plain")); - - con->http_status = 200; - con->send->bytes_in += b->used-1; - con->send->is_closed = 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 { - mod_status_handle_server_status_html(srv, con, p_d); - } - - con->http_status = 200; - con->send->is_closed = 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, *tmp_buf = p->tmp_buf; - size_t i; - const fdevent_handler_info_t *handler; - const network_backend_info_t *backend; - - b = chunkqueue_get_append_buffer(con->send); - - BUFFER_COPY_STRING_CONST(b, - "<?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>" PACKAGE_NAME " " PACKAGE_VERSION "</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", fdevent_get_handler_info_by_type(srv->event_handler)->name); - buffer_reset(tmp_buf); - for (handler = fdevent_get_handlers(); handler->name; handler++) { - if (handler->init) { - buffer_append_string_len(tmp_buf, CONST_STR_LEN("+ ")); - } else { - buffer_append_string_len(tmp_buf, CONST_STR_LEN("- ")); - } - - buffer_append_string(tmp_buf, handler->name); - buffer_append_string_len(tmp_buf, CONST_STR_LEN("<br />")); - } - mod_status_row_append(b, "Supported fd-Event-Handlers", tmp_buf->ptr); - - mod_status_row_append(b, "Network-Backend", network_get_backend_info_by_type(srv->network_backend)->name); - buffer_reset(tmp_buf); - for (backend = network_get_backends(); backend->name; backend++) { - if (backend->write_handler) { - buffer_append_string_len(tmp_buf, CONST_STR_LEN("+ ")); - } else { - buffer_append_string_len(tmp_buf, CONST_STR_LEN("- ")); - } - - buffer_append_string(tmp_buf, backend->name); - buffer_append_string_len(tmp_buf, CONST_STR_LEN("<br />")); - } -#ifdef USE_MMAP - buffer_append_string_len(tmp_buf, CONST_STR_LEN("+ (mmap)<br />")); -#else - buffer_append_string_len(tmp_buf, CONST_STR_LEN("- (mmap)<br />")); -#endif - mod_status_row_append(b, "Supported Network-Backends", tmp_buf->ptr); - - mod_status_header_append(b, "Config-File-Settings"); - - buffer_reset(tmp_buf); - for (i = 0; i < srv->plugins.used; i++) { - plugin **ps = srv->plugins.ptr; - - plugin *pl = ps[i]; - - if (i == 0) { - buffer_copy_string_buffer(tmp_buf, pl->name); - } else { - buffer_append_string_len(tmp_buf, CONST_STR_LEN("<br />")); - buffer_append_string_buffer(tmp_buf, pl->name); - } - } - - mod_status_row_append(b, "Loaded Modules", tmp_buf->ptr); - - buffer_append_string_len(b, CONST_STR_LEN(" </table>\n")); - - buffer_append_string_len(b, CONST_STR_LEN( - " </body>\n" - "</html>\n" - )); - - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); - - con->http_status = 200; - con->send->bytes_in += b->used-1; - con->send->is_closed = 1; - - return HANDLER_FINISHED; -} - -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_OPTION(status_url); - PATCH_OPTION(config_url); - PATCH_OPTION(sort); - PATCH_OPTION(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_OPTION(status_url); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.config-url"))) { - PATCH_OPTION(config_url); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.enable-sort"))) { - PATCH_OPTION(sort); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("status.statistics-url"))) { - PATCH_OPTION(statistics_url); - } - } - } - - return 0; -} - -static handler_t mod_status_handler(server *srv, connection *con, void *p_d) { - plugin_data *p = p_d; - - mod_status_patch_connection(srv, con, p); - - if (!buffer_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_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_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; -} - -LI_EXPORT int mod_status_plugin_init(plugin *p); -LI_EXPORT 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_response_done = mod_status_account; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_trigger_b4_dl.c b/src/mod_trigger_b4_dl.c deleted file mode 100644 index 2ef3b49f..00000000 --- a/src/mod_trigger_b4_dl.c +++ /dev/null @@ -1,589 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" -#include "response.h" -#include "inet_ntop_cache.h" - -#if defined(HAVE_GDBM_H) -#include <gdbm.h> -#endif - -#if defined(HAVE_PCRE_H) -#include <pcre.h> -#endif - -#if defined(HAVE_MEMCACHE_H) -#include <memcache.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(HAVE_MEMCACHE_H) - struct memcache *mc; -#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; - - UNUSED(srv); - - 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 (!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(HAVE_MEMCACHE_H) - if (s->mc) mc_free(s->mc); -#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(specific_config *)); - - for (i = 0; i < srv->config_context->used; 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, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } -#if defined(HAVE_GDBM_H) - if (!buffer_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; - } - } -#endif -#if defined(HAVE_PCRE_H) - if (!buffer_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_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 (s->mc_hosts->used) { -#if defined(HAVE_MEMCACHE_H) - size_t k; - s->mc = mc_new(); - - for (k = 0; k < s->mc_hosts->used; k++) { - data_string *ds = (data_string *)s->mc_hosts->data[k]; - - if (0 != mc_server_add4(s->mc, ds->value->ptr)) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "connection to host failed:", - ds->value); - - 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 - } - - -#if (!defined(HAVE_GDBM_H) && !defined(HAVE_MEMCACHE_H)) || !defined(HAVE_PCRE_H) - log_error_write(srv, __FILE__, __LINE__, "s", - "(either gdbm or libmemcache) and pcre are require, but were not found, aborting"); - return HANDLER_ERROR; -#endif - } - - return HANDLER_GO_ON; -} - -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_OPTION(db); -#endif -#if defined(HAVE_PCRE_H) - PATCH_OPTION(download_regex); - PATCH_OPTION(trigger_regex); -#endif - PATCH_OPTION(trigger_timeout); - PATCH_OPTION(deny_url); - PATCH_OPTION(mc_namespace); - PATCH_OPTION(debug); -#if defined(HAVE_MEMCACHE_H) - PATCH_OPTION(mc); -#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_OPTION(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_OPTION(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_OPTION(db); -#endif - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.trigger-timeout"))) { - PATCH_OPTION(trigger_timeout); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.debug"))) { - PATCH_OPTION(debug); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.deny-url"))) { - PATCH_OPTION(deny_url); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.memcache-namespace"))) { - PATCH_OPTION(mc_namespace); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("trigger-before-download.memcache-hosts"))) { -#if defined(HAVE_MEMCACHE_H) - PATCH_OPTION(mc); -#endif - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_trigger_b4_dl_uri_handler) { - plugin_data *p = p_d; - const char *remote_ip; - data_string *ds; - -#if defined(HAVE_PCRE_H) - int n; -# define N 10 - int ovec[N * 3]; - - if (con->uri.path->used == 0) 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(HAVE_MEMCACHE_H) - return HANDLER_GO_ON; -# elif defined(HAVE_GDBM_H) && defined(HAVE_MEMCACHE_H) - if (!p->conf.db && !p->conf.mc) return HANDLER_GO_ON; - if (p->conf.db && p->conf.mc) { - /* 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.mc) return HANDLER_GO_ON; -# endif - - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("X-Forwarded-For")))) { - /* X-Forwarded-For contains the ip behind the proxy */ - - remote_ip = ds->value->ptr; - - /* memcache can't handle spaces */ - } else { - remote_ip = inet_ntop_cache_get_ip(srv, &(con->dst_addr)); - } - - 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, con->uri.path->ptr, con->uri.path->used - 1, 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(HAVE_MEMCACHE_H) - if (p->conf.mc) { - size_t i; - buffer_copy_string_buffer(p->tmp_buf, p->conf.mc_namespace); - buffer_append_string(p->tmp_buf, remote_ip); - - for (i = 0; i < p->tmp_buf->used - 1; 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 (0 != mc_set(p->conf.mc, - CONST_BUF_LEN(p->tmp_buf), - (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, con->uri.path->ptr, con->uri.path->used - 1, 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 */ - - response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url)); - - con->http_status = 307; - con->send->is_closed = 1; - - return HANDLER_FINISHED; - } - - last_hit = *(time_t *)(val.dptr); - - free(val.dptr); - - if (srv->cur_ts - last_hit > p->conf.trigger_timeout) { - /* found, but timeout, redirect */ - - response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url)); - con->http_status = 307; - con->send->is_closed = 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(HAVE_MEMCACHE_H) - if (p->conf.mc) { - void *r; - size_t i; - - buffer_copy_string_buffer(p->tmp_buf, p->conf.mc_namespace); - buffer_append_string(p->tmp_buf, remote_ip); - - for (i = 0; i < p->tmp_buf->used - 1; 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 (NULL == (r = mc_aget(p->conf.mc, - CONST_BUF_LEN(p->tmp_buf) - ))) { - - response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->conf.deny_url)); - - con->http_status = 307; - con->send->is_closed = 1; - - return HANDLER_FINISHED; - } - - free(r); - - /* set a new timeout */ - if (0 != mc_set(p->conf.mc, - CONST_BUF_LEN(p->tmp_buf), - (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); - - last_hit = *(time_t *)(val.dptr); - - 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 */ - -LI_EXPORT int mod_trigger_b4_dl_plugin_init(plugin *p); -LI_EXPORT 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/src/mod_uploadprogress.c b/src/mod_uploadprogress.c deleted file mode 100644 index 56423680..00000000 --- a/src/mod_uploadprogress.c +++ /dev/null @@ -1,638 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#include "response.h" -#include "stat_cache.h" - -#define CONFIG_UPLOAD_PROGRESS_URL "upload-progress.progress-url" -#define CONFIG_UPLOAD_PROGRESS_TIMEOUT "upload-progress.remove-timeout" -#define CONFIG_UPLOAD_PROGRESS_DEBUG "upload-progress.debug" - -/** - * uploadprogress for lighttpd - * - * Initial: Jan Kneschke <jan@kneschke.de> - * Timeout+Status addon: Bjoern Kalkbrenner <terminar@cyberphoria.org> [20070112] - * - * the timeout is used to keep in the status information intact even if the parent - * connection is gone already - */ - -typedef struct { - buffer *tracking_id; - connection *con; - - time_t timeout; - int status; - off_t size; -} 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; - unsigned short debug; - unsigned short remove_timeout; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - connection_map *con_map; - - buffer *tmp_buf; /** used as temporary buffer for extracting the tracking id */ - - 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->tracking_id) { - buffer_free(cme->tracking_id); - } - free(cme); - } - - free(cm); -} - -static connection_map_entry *connection_map_insert(connection_map *cm, buffer *tracking_id, connection *con) { - 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->tracking_id = buffer_init(); - } - cme->timeout = 0; - cme->status = 0; - buffer_copy_string_buffer(cme->tracking_id, tracking_id); - cme->con = con; - - cm->ptr[cm->used++] = cme; - - return cme; -} - -static connection_map_entry *connection_map_get_connection_entry(connection_map *cm, buffer *tracking_id) { - size_t i; - - for (i = 0; i < cm->used; i++) { - connection_map_entry *cme = cm->ptr[i]; - - if (buffer_is_equal(cme->tracking_id, tracking_id)) { - /* found connection */ - return cme; - } - } - return NULL; -} - -static void connection_map_remove_connection(connection_map *cm, size_t i) { - connection_map_entry *cme = cm->ptr[i]; - - buffer_reset(cme->tracking_id); - cme->timeout=0; - cme->status=0; - - cm->used--; - - /* swap positions with the last entry */ - if (cm->used) { - cm->ptr[i] = cm->ptr[cm->used]; - cm->ptr[cm->used] = cme; - } -} - -/** - * remove dead tracking IDs - * - * uploadprogress.remove-timeout sets a grace-period in which the - * connection status is still known even of the connection is already - * being removed - * - */ -static void connection_map_clear_timeout_connections(connection_map *cm) { - size_t i; - time_t now_t = time(NULL); - - for (i = 0; i < cm->used; i++) { - connection_map_entry *cme = cm->ptr[i]; - - if (cme->timeout != 0 && cme->timeout < now_t) { - /* found connection */ - connection_map_remove_connection(cm, i); - } - } -} - -/** - * extract the tracking-id from the parameters - * - * for POST requests it is part of the request headers - * for GET requests ... too - */ -static buffer *get_tracking_id(plugin_data *p, connection *con) { - data_string *ds; - buffer *b = NULL; - char *qstr=NULL; - size_t i; - - /* the request has to contain a 32byte ID */ - if (NULL == (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("X-Progress-ID")))) { - char *amp = NULL; - - /* perhaps the POST request is using the querystring to pass the X-Progress-ID */ - if (buffer_is_empty(con->uri.query)) { - /* - * con->uri.query will not be parsed out if a 413 error happens - */ - if (NULL != (qstr = strchr(con->request.uri->ptr, '?'))) { - /** extract query string from request.uri */ - buffer_copy_string(con->uri.query, qstr + 1); - } else { - return NULL; - } - } - - /** split the query-string and extract the X-Progress-ID */ - do { - char *eq = NULL; - char *start = amp ? amp + 1 : con->uri.query->ptr; - - amp = strchr(start, '&'); - - /* check the string between start and amp for = */ - - if (amp) { - buffer_copy_string_len(p->tmp_buf, start, amp - start); - } else { - buffer_copy_string(p->tmp_buf, start); - } - - eq = strchr(p->tmp_buf->ptr, '='); - - if (eq) { - *eq = '\0'; - - if (0 == strcmp(p->tmp_buf->ptr, "X-Progress-ID")) { - size_t key_len = sizeof("X-Progress-ID") - 1; - size_t var_len = p->tmp_buf->used - 1; - /* found */ - - buffer_copy_string_len(p->tmp_buf, start + key_len + 1, var_len - key_len - 1); - - b = p->tmp_buf; - - break; - } - } - } while (amp); - - if (!b) return NULL; - } else { - /* request header was found, use it */ - b = ds->value; - } - - if (b->used != 32 + 1) { - if (p->conf.debug) ERROR("the Progress-ID has to be 32 characters long, got %zd characters", b->used - 1); - return NULL; - } - - for (i = 0; i < b->used - 1; i++) { - char c = b->ptr[i]; - - if (!light_isxdigit(c)) { - if (p->conf.debug) ERROR("only hex-digits are allowed (0-9 + a-f): (ascii: %d)", c); - return NULL; - } - } - - return b; -} - -/* init the plugin data */ -INIT_FUNC(mod_uploadprogress_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->con_map = connection_map_init(); - p->tmp_buf = buffer_init(); - - return p; -} - -/* detroy the plugin data */ -FREE_FUNC(mod_uploadprogress_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]; - - buffer_free(s->progress_url); - s->remove_timeout=0; - - free(s); - } - free(p->config_storage); - } - - connection_map_free(p->con_map); - buffer_free(p->tmp_buf); - - 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[] = { - { CONFIG_UPLOAD_PROGRESS_URL, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { CONFIG_UPLOAD_PROGRESS_TIMEOUT, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { CONFIG_UPLOAD_PROGRESS_DEBUG, 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - s->progress_url = buffer_init(); - s->remove_timeout = 60; - s->debug = 0; - - cv[0].destination = s->progress_url; - cv[1].destination = &(s->remove_timeout); - cv[2].destination = &(s->debug); - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -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_OPTION(progress_url); - PATCH_OPTION(remove_timeout); - PATCH_OPTION(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(CONFIG_UPLOAD_PROGRESS_URL))) { - PATCH_OPTION(progress_url); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_UPLOAD_PROGRESS_TIMEOUT))) { - PATCH_OPTION(remove_timeout); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_UPLOAD_PROGRESS_DEBUG))) { - PATCH_OPTION(debug); - } - } - } - - return 0; -} - -/** - * - * 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; - buffer *tracking_id; - buffer *b; - connection_map_entry *post_con_entry = NULL; - connection_map_entry *map_con_entry = NULL; - - if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; - - mod_uploadprogress_patch_connection(srv, con, p); - - /* no progress URL set, ignore request */ - if (buffer_is_empty(p->conf.progress_url)) return HANDLER_GO_ON; - - switch(con->request.http_method) { - case HTTP_METHOD_POST: - /** - * a POST request is the UPLOAD itself - * - * get the unique tracker id - */ - if (NULL == (tracking_id = get_tracking_id(p, con))) { - return HANDLER_GO_ON; - } - - if (NULL == (map_con_entry = connection_map_get_connection_entry(p->con_map, tracking_id))) { - connection_map_insert(p->con_map, tracking_id, con); - - if (p->conf.debug) TRACE("POST: connection is new, registered: %s", SAFE_BUF_STR(tracking_id)); - } else { - map_con_entry->timeout = 0; - map_con_entry->status = 0; - - if (p->conf.debug) TRACE("POST: connection is known, id: %s", SAFE_BUF_STR(tracking_id)); - } - - return HANDLER_GO_ON; - case HTTP_METHOD_GET: - /** - * the status request for the current connection - */ - if (p->conf.debug) TRACE("(uploadprogress) urls %s == %s", SAFE_BUF_STR(con->uri.path), SAFE_BUF_STR(p->conf.progress_url)); - - if (!buffer_is_equal(con->uri.path, p->conf.progress_url)) { - return HANDLER_GO_ON; - } - - /* get the tracker id */ - if (NULL == (tracking_id = get_tracking_id(p, con))) { - return HANDLER_GO_ON; - } - - buffer_reset(con->physical.path); - - con->file_started = 1; - con->http_status = 200; - con->send->is_closed = 1; - - /* send JSON content */ - - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/javascript")); - - /* just an attempt the force the IE/proxies to NOT cache the request */ - response_header_overwrite(srv, con, CONST_STR_LEN("Pragma"), CONST_STR_LEN("no-cache")); - response_header_overwrite(srv, con, CONST_STR_LEN("Expires"), CONST_STR_LEN("Thu, 19 Nov 1981 08:52:00 GMT")); - response_header_overwrite(srv, con, CONST_STR_LEN("Cache-Control"), - CONST_STR_LEN("no-store, no-cache, must-revalidate, post-check=0, pre-check=0")); - - b = chunkqueue_get_append_buffer(con->send); - - /* get the connection */ - if (NULL == (post_con_entry = connection_map_get_connection_entry(p->con_map, tracking_id))) { - /** - * looks like we don't know the tracking id yet, GET and POST out of sync ? */ - buffer_append_string_len(b, CONST_STR_LEN("new Object({ 'state' : 'starting' })\r\n")); - con->send->bytes_in += b->used-1; - - if (p->conf.debug) TRACE("connection unknown: %s, sending: %s", SAFE_BUF_STR(tracking_id), SAFE_BUF_STR(b)); - - return HANDLER_FINISHED; - } - - buffer_copy_string_len(b, CONST_STR_LEN("new Object({ 'state' : ")); - - if (post_con_entry->status == 413) { - /* the upload was too large */ - buffer_append_string_len(b, CONST_STR_LEN("'error', 'status' : 413")); - } else if (post_con_entry->con == NULL) { - /* the connection is already gone */ - buffer_append_string_len(b, CONST_STR_LEN("'done', 'size' : ")); - buffer_append_off_t(b, post_con_entry->size); - } else { - /* the upload is already done, but the connection might be still open */ - buffer_append_string(b, post_con_entry->con->recv->is_closed ? "'done'" : "'uploading'"); - buffer_append_string_len(b, CONST_STR_LEN(", 'received' : ")); - buffer_append_off_t(b, post_con_entry->con->recv->bytes_in); - buffer_append_string_len(b, CONST_STR_LEN(", 'size' : ")); - buffer_append_off_t(b, post_con_entry->con->request.content_length == -1 ? 0 : post_con_entry->con->request.content_length); - } - buffer_append_string_len(b, CONST_STR_LEN("})\r\n")); - con->send->bytes_in += b->used-1; - - if (p->conf.debug) TRACE("connection is known: %s, sending: %s", SAFE_BUF_STR(tracking_id), SAFE_BUF_STR(b)); - - return HANDLER_FINISHED; - default: - break; - } - - return HANDLER_GO_ON; -} - -/** - * check if request parser sent 413 for our POST request - */ -URIHANDLER_FUNC(mod_uploadprogress_response_header) { - plugin_data *p = p_d; - - buffer *tracking_id; - connection_map_entry *map_con_entry = NULL; - - UNUSED(srv); - - /* - * we only want to process an 413 (Bad length) error for the upload (POST request) - */ - if (con->request.http_method != HTTP_METHOD_POST || con->http_status != 413) { - return HANDLER_GO_ON; - } - - if (p->conf.debug) { - TRACE("response_header: con=%p, http_method=%d, http_status=%d", (void*) con, - con->request.http_method, con->http_status); - } - - /* get the tracker id */ - if (NULL == (tracking_id = get_tracking_id(p, con))) { - return HANDLER_GO_ON; - } - - if (NULL == (map_con_entry = connection_map_get_connection_entry(p->con_map, tracking_id))) { - /** - * in case the request parser meant the request was too large the URI handler won't - * get called. Insert the connection mapping here - */ - if (NULL == (map_con_entry = connection_map_insert(p->con_map, tracking_id, con))) { - return HANDLER_GO_ON; - } - } - - /* ok, found our entries, setting 413 here for status */ - map_con_entry->status = 413; - - return HANDLER_GO_ON; -} - -/** - * remove the parent connection from the connection mapping - * when it got closed - * - * keep the mapping active for a while to send a valid final status - */ -REQUESTDONE_FUNC(mod_uploadprogress_request_done) { - plugin_data *p = p_d; - buffer *tracking_id; - connection_map_entry *cm = NULL; - - UNUSED(srv); - - if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; - - /* - * only need to handle the upload request. - */ - if (con->request.http_method != HTTP_METHOD_POST) { - return HANDLER_GO_ON; - } - - if (NULL == (tracking_id = get_tracking_id(p, con))) { - return HANDLER_GO_ON; - } - - if (p->conf.debug) { - TRACE("upload is done, moving tracking-id to backlog: tracking-id=%s, http_status=%d", - SAFE_BUF_STR(tracking_id), - con->http_status); - } - - /* - * set timeout on the upload's connection_map_entry. - */ - if (NULL == (cm = connection_map_get_connection_entry(p->con_map, tracking_id))) { - if (p->conf.debug) { - TRACE("tracking ID %s not found, can't set timeout", SAFE_BUF_STR(tracking_id)); - } - return HANDLER_GO_ON; - } - - /* save request size to be able to report it even when cm->con == NULL */ - cm->size = con->request.content_length; - - cm->timeout = time(NULL) + p->conf.remove_timeout; - cm->con = NULL; /* con becomes invalid very soon */ - - return HANDLER_GO_ON; -} - -/** - * remove dead connections once in while - */ -TRIGGER_FUNC(mod_uploadprogress_trigger) { - plugin_data *p = p_d; - - if ((srv->cur_ts % 10) != 0) return HANDLER_GO_ON; - - connection_map_clear_timeout_connections(p->con_map); - - return HANDLER_GO_ON; -} - - -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_uploadprogress_plugin_init(plugin *p); -LI_EXPORT 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->handle_response_done = mod_uploadprogress_request_done; - p->set_defaults = mod_uploadprogress_set_defaults; - p->cleanup = mod_uploadprogress_free; - p->handle_trigger = mod_uploadprogress_trigger; - p->handle_response_header = mod_uploadprogress_response_header; - - p->data = NULL; - - return 0; -} diff --git a/src/mod_userdir.c b/src/mod_userdir.c deleted file mode 100644 index 8ee91295..00000000 --- a/src/mod_userdir.c +++ /dev/null @@ -1,325 +0,0 @@ -#include <sys/types.h> - -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "response.h" - -#include "plugin.h" -#include "sys-files.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; -} 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; - - UNUSED(srv); - - 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]; - - 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 */ - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - if (!p) return HANDLER_ERROR; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; 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; - - 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); - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - } - - return HANDLER_GO_ON; -} - -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_OPTION(path); - PATCH_OPTION(exclude_user); - PATCH_OPTION(include_user); - PATCH_OPTION(basepath); - PATCH_OPTION(letterhomes); - - /* 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_OPTION(path); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.exclude-user"))) { - PATCH_OPTION(exclude_user); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.include-user"))) { - PATCH_OPTION(include_user); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.basepath"))) { - PATCH_OPTION(basepath); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("userdir.letterhomes"))) { - PATCH_OPTION(letterhomes); - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_userdir_docroot_handler) { - plugin_data *p = p_d; - int uri_len; - size_t k; - char *rel_url; -#ifdef HAVE_PWD_H - struct passwd *pwd = NULL; -#endif - - if (con->uri.path->used == 0) return HANDLER_GO_ON; - - mod_userdir_patch_connection(srv, con, p); - - uri_len = con->uri.path->used - 1; - - /* /~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_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 */ - - if (buffer_is_empty(p->conf.basepath)) { -#ifdef HAVE_PWD_H - buffer_copy_string(p->temp_path, pwd->pw_dir); -#endif - } else { - char *cp; - /* check if the username is valid - * a request for /~../ should lead to a directory traversal - * limiting to [-_a-z0-9.] should fix it */ - - for (cp = p->username->ptr; *cp; cp++) { - char c = *cp; - - if (!(c == '-' || - c == '_' || - c == '.' || - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9'))) { - - return HANDLER_GO_ON; - } - } - if (con->conf.force_lowercase_filenames) { - buffer_to_lower(p->username); - } - - buffer_copy_string_buffer(p->temp_path, p->conf.basepath); - PATHNAME_APPEND_SLASH(p->temp_path); - if (p->conf.letterhomes) { - buffer_append_string_len(p->temp_path, p->username->ptr, 1); - PATHNAME_APPEND_SLASH(p->temp_path); - } - buffer_append_string_buffer(p->temp_path, p->username); - } - PATHNAME_APPEND_SLASH(p->temp_path); - buffer_append_string_buffer(p->temp_path, p->conf.path); - - if (buffer_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; - } - } - - /* 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_secure_download doesn't work with userdir anyway) - */ - PATHNAME_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_string_buffer(con->physical.path, p->temp_path); - - buffer_reset(p->temp_path); - - return HANDLER_GO_ON; -} - -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_userdir_plugin_init(plugin *p); -LI_EXPORT 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/src/mod_usertrack.c b/src/mod_usertrack.c deleted file mode 100644 index b0275107..00000000 --- a/src/mod_usertrack.c +++ /dev/null @@ -1,277 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#ifdef USE_OPENSSL -# include <openssl/md5.h> -#else -# include "md5.h" - -typedef li_MD5_CTX MD5_CTX; -#define MD5_Init li_MD5_Init -#define MD5_Update li_MD5_Update -#define MD5_Final li_MD5_Final - -#endif - -/* plugin config for all request/connections */ - -typedef struct { - buffer *cookie_name; - buffer *cookie_domain; - unsigned short 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; - - UNUSED(srv); - - 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]; - - buffer_free(s->cookie_name); - 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_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { "usertrack.cookie-domain", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - - { "usertrack.cookiename", NULL, T_CONFIG_DEPRECATED, 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - - s = calloc(1, sizeof(plugin_config)); - s->cookie_name = 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; - - p->config_storage[i] = s; - - if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - - if (buffer_is_empty(s->cookie_name)) { - buffer_copy_string_len(s->cookie_name, CONST_STR_LEN("TRACKID")); - } else { - size_t j; - for (j = 0; j < s->cookie_name->used - 1; 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_is_empty(s->cookie_domain)) { - size_t j; - for (j = 0; j < s->cookie_domain->used - 1; 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; -} - -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_OPTION(cookie_name); - PATCH_OPTION(cookie_domain); - PATCH_OPTION(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_OPTION(cookie_name); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-max-age"))) { - PATCH_OPTION(cookie_max_age); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("usertrack.cookie-domain"))) { - PATCH_OPTION(cookie_domain); - } - } - } - - return 0; -} - -URIHANDLER_FUNC(mod_usertrack_uri_handler) { - plugin_data *p = p_d; - data_string *ds; - unsigned char h[16]; - MD5_CTX Md5Ctx; - char hh[32]; - - if (con->uri.path->used == 0) return HANDLER_GO_ON; - - mod_usertrack_patch_connection(srv, con, p); - - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, 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(ds->value->ptr, p->conf.cookie_name->ptr))) { - char *nc; - - /* skip WS */ - for (nc = g + p->conf.cookie_name->used-1; *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 */ - if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { - ds = data_response_init(); - } - buffer_copy_string_len(ds->key, CONST_STR_LEN("Set-Cookie")); - buffer_copy_string_buffer(ds->value, p->conf.cookie_name); - buffer_append_string_len(ds->value, CONST_STR_LEN("=")); - - - /* taken from mod_auth.c */ - - /* generate shared-secret */ - MD5_Init(&Md5Ctx); - MD5_Update(&Md5Ctx, (unsigned char *)con->uri.path->ptr, con->uri.path->used - 1); - MD5_Update(&Md5Ctx, (unsigned char *)"+", 1); - - /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */ - LI_ltostr(hh, srv->cur_ts); - MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); - MD5_Update(&Md5Ctx, (unsigned char *)srv->entropy, sizeof(srv->entropy)); - LI_ltostr(hh, rand()); - MD5_Update(&Md5Ctx, (unsigned char *)hh, strlen(hh)); - - MD5_Final(h, &Md5Ctx); - - buffer_append_string_encoded(ds->value, (char *)h, 16, ENCODING_HEX); - buffer_append_string_len(ds->value, CONST_STR_LEN("; Path=/")); - buffer_append_string_len(ds->value, CONST_STR_LEN("; Version=1")); - - if (!buffer_is_empty(p->conf.cookie_domain)) { - buffer_append_string_len(ds->value, CONST_STR_LEN("; Domain=")); - buffer_append_string_encoded(ds->value, CONST_BUF_LEN(p->conf.cookie_domain), ENCODING_REL_URI); - } - - if (p->conf.cookie_max_age) { - buffer_append_string_len(ds->value, CONST_STR_LEN("; max-age=")); - buffer_append_long(ds->value, p->conf.cookie_max_age); - } - - array_insert_unique(con->response.headers, (data_unset *)ds); - - return HANDLER_GO_ON; -} - -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_usertrack_plugin_init(plugin *p); -LI_EXPORT 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/src/mod_webdav.c b/src/mod_webdav.c deleted file mode 100644 index 49de74a5..00000000 --- a/src/mod_webdav.c +++ /dev/null @@ -1,2688 +0,0 @@ -#include <sys/types.h> -#include <sys/stat.h> -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <assert.h> - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#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_H) -#define USE_LOCKS -#include <uuid/uuid.h> -#endif - -#include "base.h" -#include "log.h" -#include "buffer.h" -#include "response.h" - -#include "plugin.h" - -#include "stream.h" -#include "stat_cache.h" - -#include "sys-files.h" -#include "sys-mmap.h" -#include "sys-strings.h" - -/** - * 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 - * - */ -#ifdef _WIN32 -#define WEBDAV_FILE_MODE _S_IREAD | _S_IWRITE -#define WEBDAV_DIR_MODE _S_IREAD | _S_IWRITE -#else -#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 -#endif - -/* 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; - -/* init the plugin data */ -INIT_FUNC(mod_webdav_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->tmp_buf = buffer_init(); - - p->uri.scheme = buffer_init(); - p->uri.path_raw = 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 (!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_raw); - 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(specific_config *)); - - for (i = 0; i < srv->config_context->used; 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, ((data_config *)srv->config_context->data[i])->value, cv)) { - return HANDLER_ERROR; - } - - if (!buffer_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 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_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 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_exec(s->sql, - "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," - " 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("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 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 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; -} - -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; - - if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; - - mod_webdav_patch_connection(srv, con, p); - - if (!p->conf.enabled) { - if (con->conf.log_request_handling) { - TRACE("-- skipping %s in mod_webdav, not enabled", - SAFE_BUF_STR(con->uri.path)); - } - - return HANDLER_GO_ON; - } - - if (con->conf.log_request_handling) { - TRACE("-- handling request in mod_webdav: %s", - SAFE_BUF_STR(con->uri.path)); - } - - 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 */ - response_header_overwrite(srv, con, CONST_STR_LEN("DAV"), CONST_STR_LEN("1,2")); - response_header_overwrite(srv, con, CONST_STR_LEN("MS-Author-Via"), CONST_STR_LEN("DAV")); - - if (p->conf.is_readonly) { - response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("PROPFIND")); - } else { - response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("PROPFIND, DELETE, MKCOL, PUT, MOVE, COPY, PROPPATCH, LOCK, UNLOCK")); - } - - if (con->conf.log_request_handling) { - TRACE("sending OPTIONS response for: %s", - SAFE_BUF_STR(con->uri.path)); - } - - break; - default: - break; - } - - return HANDLER_GO_ON; -} - -PHYSICALPATH_FUNC(mod_webdav_physical_handler) { - plugin_data *p = p_d; - - if (con->mode != DIRECT) return HANDLER_GO_ON; - - mod_webdav_patch_connection(srv, con, p); - - if (!p->conf.enabled) { - if (con->conf.log_request_handling) { - TRACE("-- skipping %s in mod_webdav, not enabled", - SAFE_BUF_STR(con->uri.path)); - } - - return HANDLER_GO_ON; - } - - if (con->conf.log_request_handling) { - TRACE("-- handling request in mod_webdav: %s", - SAFE_BUF_STR(con->uri.path)); - } - - /* physical path is setup */ - if (con->physical.path->used == 0) { - TRACE("-- missing con->physical.path: %s", - SAFE_BUF_STR(con->uri.path)); - return HANDLER_GO_ON; - } - - switch (con->request.http_method) { - case HTTP_METHOD_OPTIONS: - /* already handled */ - break; - case HTTP_METHOD_PROPFIND: - case HTTP_METHOD_MKCOL: - case HTTP_METHOD_DELETE: - case HTTP_METHOD_PUT: - case HTTP_METHOD_MOVE: - case HTTP_METHOD_COPY: - case HTTP_METHOD_PROPPATCH: - case HTTP_METHOD_LOCK: - case HTTP_METHOD_UNLOCK: - con->mode = p->id; - return HANDLER_FINISHED; - default: break; - } - - 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 ")); - } - buffer_append_long(b, status); - buffer_append_string_len(b, CONST_STR_LEN(" ")); - buffer_append_string(b, get_http_status_name(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, plugin_data *p, physical *dst, buffer *b) { - int status = 0; - - UNUSED(p); - - /* 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 = p->conf.stmt_delete_uri; - - if (stmt) { - sqlite3_reset(stmt); - - /* bind the values to the insert */ - - sqlite3_bind_text(stmt, 1, - dst->rel_path->ptr, - dst->rel_path->used - 1, - SQLITE_TRANSIENT); - - if (SQLITE_DONE != sqlite3_step(stmt)) { - /* */ - } - } -#endif - } - - return (status != 0); -} - -static int webdav_delete_dir(server *srv, connection *con, plugin_data *p, 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; - int status = 0; - - 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 */ - } - - buffer_copy_string_buffer(d.path, dst->path); - PATHNAME_APPEND_SLASH(d.path); - buffer_append_string(d.path, de->d_name); - - buffer_copy_string_buffer(d.rel_path, dst->rel_path); - PATHNAME_APPEND_SLASH(d.rel_path); - buffer_append_string(d.rel_path, de->d_name); - - /* 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, p, &d, b); - - /* try to unlink it */ - if (-1 == rmdir(d.path->ptr)) { - 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 = p->conf.stmt_delete_uri; - - status = 0; - - if (stmt) { - sqlite3_reset(stmt); - - /* bind the values to the insert */ - - sqlite3_bind_text(stmt, 1, - d.rel_path->ptr, - d.rel_path->used - 1, - SQLITE_TRANSIENT); - - if (SQLITE_DONE != sqlite3_step(stmt)) { - /* */ - } - } -#endif - } - } else { - have_multi_status = webdav_delete_file(srv, con, p, &d, b); - } - } - closedir(dir); - - buffer_free(d.path); - buffer_free(d.rel_path); - } - - return have_multi_status; -} - -static int webdav_copy_file(server *srv, connection *con, plugin_data *p, physical *src, physical *dst, int overwrite) { - stream s; - int status = 0, ofd; - - UNUSED(con); - UNUSED(srv); - UNUSED(p); - - if (stream_open(&s, src->path)) { - 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; - } - stream_close(&s); - return status; - } - - if (-1 == write(ofd, s.start, s.size)) { - switch(errno) { - case ENOSPC: - status = 507; - break; - default: - status = 403; - break; - } - } - - stream_close(&s); - close(ofd); - -#ifdef USE_PROPPATCH - if (0 == status) { - /* copy worked fine, copy connected properties */ - sqlite3_stmt *stmt = p->conf.stmt_copy_uri; - - if (stmt) { - sqlite3_reset(stmt); - - /* bind the values to the insert */ - sqlite3_bind_text(stmt, 1, - dst->rel_path->ptr, - dst->rel_path->used - 1, - SQLITE_TRANSIENT); - - sqlite3_bind_text(stmt, 2, - src->rel_path->ptr, - src->rel_path->used - 1, - SQLITE_TRANSIENT); - - if (SQLITE_DONE != sqlite3_step(stmt)) { - /* */ - } - } - } -#endif - return status; -} - -static int webdav_copy_dir(server *srv, connection *con, plugin_data *p, 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; - - if ((de->d_name[0] == '.' && de->d_name[1] == '\0') || - (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0')) { - continue; - } - - buffer_copy_string_buffer(s.path, src->path); - PATHNAME_APPEND_SLASH(s.path); - buffer_append_string(s.path, de->d_name); - - buffer_copy_string_buffer(d.path, dst->path); - PATHNAME_APPEND_SLASH(d.path); - buffer_append_string(d.path, de->d_name); - - buffer_copy_string_buffer(s.rel_path, src->rel_path); - PATHNAME_APPEND_SLASH(s.rel_path); - buffer_append_string(s.rel_path, de->d_name); - - buffer_copy_string_buffer(d.rel_path, dst->rel_path); - PATHNAME_APPEND_SLASH(d.rel_path); - buffer_append_string(d.rel_path, de->d_name); - - 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 = p->conf.stmt_copy_uri; - - if (0 != (status = webdav_copy_dir(srv, con, p, &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, - dst->rel_path->ptr, - dst->rel_path->used - 1, - SQLITE_TRANSIENT); - - sqlite3_bind_text(stmt, 2, - src->rel_path->ptr, - src->rel_path->used - 1, - 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, p, &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; -} - -static int webdav_get_live_property(server *srv, connection *con, plugin_data *p, physical *dst, char *prop_name, buffer *b) { - stat_cache_entry *sce = NULL; - int found = 0; - - UNUSED(p); - - 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"; - size_t k; - - 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>")); - 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)) { - for (k = 0; k < con->conf.mimetypes->used; k++) { - data_string *ds = (data_string *)con->conf.mimetypes->data[k]; - - if (ds->key->used == 0) continue; - - if (buffer_is_equal_right_len(dst->path, ds->key, ds->key->used - 1)) { - buffer_append_string_len(b,CONST_STR_LEN("<D:getcontenttype>")); - buffer_append_string_buffer(b, ds->value); - buffer_append_string_len(b, CONST_STR_LEN("</D:getcontenttype>")); - found = 1; - - break; - } - } - } - } 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_off_t(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; - } - } - - return found ? 0 : -1; -} - -static int webdav_get_property(server *srv, connection *con, plugin_data *p, 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, p, dst, prop_name, b); - } else { - int found = 0; -#ifdef USE_PROPPATCH - sqlite3_stmt *stmt = p->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, - dst->rel_path->ptr, - dst->rel_path->used - 1, - 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; - -webdav_property live_properties[] = { - { "DAV:", "creationdate" }, - { "DAV:", "displayname" }, - { "DAV:", "getcontentlanguage" }, - { "DAV:", "getcontentlength" }, - { "DAV:", "getcontenttype" }, - { "DAV:", "getetag" }, - { "DAV:", "getlastmodified" }, - { "DAV:", "resourcetype" }, - { "DAV:", "lockdiscovery" }, - { "DAV:", "source" }, - { "DAV:", "supportedlock" }, - - { NULL, NULL } -}; - -typedef struct { - webdav_property **ptr; - - size_t used; - size_t size; -} webdav_properties; - -static int webdav_get_props(server *srv, connection *con, plugin_data *p, physical *dst, webdav_properties *props, buffer *b_200, buffer *b_404) { - size_t i; - - if (props) { - for (i = 0; i < props->used; i++) { - webdav_property *prop; - - prop = props->ptr[i]; - - if (0 != webdav_get_property(srv, con, p, - 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, p, dst, live_properties[i].prop, b_200); - } - } - - return 0; -} - -#ifdef USE_PROPPATCH -static int webdav_parse_chunkqueue(server *srv, connection *con, plugin_data *p, chunkqueue *cq, xmlDoc **ret_xml) { - xmlParserCtxtPtr ctxt; - xmlDoc *xml; - int res = 0; - 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; - - 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 */ - if (c->file.mmap.start == MAP_FAILED) { - if (-1 == c->file.fd && /* open the file if not already open */ - -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { - ERROR("open(%s) failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - - return -1; - } - - if (MAP_FAILED == (c->file.mmap.start = mmap(0, c->file.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) { - ERROR("mmap(%s) failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - - return -1; - } - - close(c->file.fd); - c->file.fd = -1; - - c->file.mmap.length = c->file.length; - - /* chunk_reset() or chunk_free() will cleanup for us */ - } - - if (XML_ERR_OK != (err = xmlParseChunk(ctxt, c->file.mmap.start + c->offset, weHave, 0))) { - ERROR("xmlParseChunk() failed: %d", err); - log_error_write(srv, __FILE__, __LINE__, "sddd", "xmlParseChunk failed at:", cq->bytes_out, weHave, err); - } - - c->offset += weHave; - cq->bytes_out += weHave; - - break; - case MEM_CHUNK: - /* append to the buffer */ - weHave = c->mem->used - 1 - c->offset; - - if (weHave > weWant) weHave = weWant; - - if (p->conf.log_xml) { - TRACE("XML-request-body: %s", c->mem->ptr + c->offset); - } - - if (XML_ERR_OK != (err = xmlParseChunk(ctxt, c->mem->ptr + c->offset, weHave, 0))) { - ERROR("xmlParseChunk(%s) failed: %d", c->mem->ptr + c->offset, err); - } - - c->offset += weHave; - cq->bytes_out += weHave; - - break; - case UNUSED_CHUNK: - break; - } - chunkqueue_remove_finished_chunks(cq); - } - - switch ((err = xmlParseChunk(ctxt, 0, 0, 1))) { - case XML_ERR_DOCUMENT_END: - case XML_ERR_OK: - break; - default: - ERROR("xmlParseChunk() failed: %d", 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(server *srv, connection *con, - buffer *locktoken, const char *lockscope, const char *locktype, int depth) { - - buffer *b; - - response_header_overwrite(srv, con, CONST_STR_LEN("Lock-Token"), CONST_BUF_LEN(locktoken)); - - response_header_overwrite(srv, con, - CONST_STR_LEN("Content-Type"), - CONST_STR_LEN("text/xml; charset=\"utf-8\"")); - - b = chunkqueue_get_append_buffer(con->send); - - 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")); - 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-600")); - 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")); - buffer_append_string_len(b,CONST_STR_LEN("</D:lockdiscovery>\n")); - buffer_append_string_len(b,CONST_STR_LEN("</D:prop>\n")); - - con->send->bytes_in += b->used-1; - - return 0; -} -#endif - -/** - * check if resource is having the right locks to access to resource - * - * - * - */ -static int webdav_has_lock(server *srv, connection *con, plugin_data *p, buffer *uri) { - int has_lock = 1; - -#ifdef USE_LOCKS - data_string *ds; - - 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 != (ds = (data_string *)array_get_element(con->request.headers, 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 = p->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(uri); - UNUSED(con); - UNUSED(p); -#endif - - return has_lock; -} - -URIHANDLER_FUNC(mod_webdav_subrequest_handler) { - plugin_data *p = p_d; - buffer *b; - DIR *dir; - data_string *ds; - int depth = -1; - struct stat st; - buffer *prop_200; - buffer *prop_404; - webdav_properties *req_props; - stat_cache_entry *sce = NULL; - - if (con->conf.log_request_handling) { - TRACE("-- handling request in mod_webdav: %s", - SAFE_BUF_STR(con->uri.path)); - } - - /* PROPFIND need them */ - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Depth")))) { - depth = strtol(ds->value->ptr, NULL, 10); - } - - if (con->conf.log_request_handling) { - TRACE("depth for %s: %d", - SAFE_BUF_STR(con->uri.path), depth); - } - - if (con->conf.log_request_handling) { - TRACE("method for %s: %s", - SAFE_BUF_STR(con->uri.path), get_http_method_name(con->request.http_method)); - } - - switch (con->request.http_method) { - case HTTP_METHOD_PROPFIND: - /* they want to know the properties of the directory */ - req_props = NULL; - - /* is there a content-body ? */ - if (con->conf.log_request_handling) { - TRACE("path-name: %s", - SAFE_BUF_STR(con->physical.path)); - } - - switch (stat_cache_get_entry(srv, con, con->physical.path, &sce)) { - case HANDLER_ERROR: - if (errno == ENOENT) { - if (con->conf.log_request_handling) { - TRACE("path-name: %s ... not found", - SAFE_BUF_STR(con->physical.path)); - } - - con->http_status = 404; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - break; - default: - break; - } - - -#ifdef USE_PROPPATCH - /* any special requests or just allprop ? */ - if (p->conf.sql && con->request.content_length > 0) { - xmlDocPtr xml; - - if (1 == webdav_parse_chunkqueue(srv, con, p, con->recv, &xml)) { - xmlNode *rootnode = xmlDocGetRootElement(xml); - - 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; - - ERROR("no name space for: %s", 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; - con->mode = DIRECT; - 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, - con->uri.path->ptr, - con->uri.path->used - 1, - SQLITE_TRANSIENT); - - if (SQLITE_DONE != sqlite3_step(stmt)) { - } - } - } else if (0 == xmlStrcmp(cmd->name, BAD_CAST "allprop")) { - /* get all properties (EMPTY) */ - } - } - } - - xmlFreeDoc(xml); - } else { - ERROR("webdav_parse_chunkqueue() failed: %s", ""); - - con->http_status = 400; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - } -#endif - con->http_status = 207; - - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml; charset=\"utf-8\"")); - - b = chunkqueue_get_append_buffer(con->send); - - 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(); - - switch(depth) { - case 0: - /* Depth: 0 */ - webdav_get_props(srv, con, p, &(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_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_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")); - - break; - case 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))) { - if (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == '\0') { - continue; - /* ignore the parent dir */ - } - - buffer_copy_string_buffer(d.path, dst->path); - PATHNAME_APPEND_SLASH(d.path); - - buffer_copy_string_buffer(d.rel_path, dst->rel_path); - PATHNAME_APPEND_SLASH(d.rel_path); - - if (de->d_name[0] == '.' && de->d_name[1] == '\0') { - /* don't append the . */ - } else { - buffer_append_string(d.path, de->d_name); - buffer_append_string(d.rel_path, de->d_name); - } - - buffer_reset(prop_200); - buffer_reset(prop_404); - - webdav_get_props(srv, con, p, &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); - buffer_append_string_len(b,CONST_STR_LEN("</D:href>\n")); - - if (!buffer_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_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); - } - break; - } - - 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 (con->conf.log_request_handling) { - TRACE("sending XML: %s", - SAFE_BUF_STR(b)); - } - - if (p->conf.log_xml) { - TRACE("XML-response-body: %s", SAFE_BUF_STR(b)); - } - con->send->bytes_in += b->used-1; - con->send->is_closed = 1; - - return HANDLER_FINISHED; - case HTTP_METHOD_MKCOL: - if (p->conf.is_readonly) { - con->http_status = 403; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - - if (con->request.content_length > 0) { - /* we don't support MKCOL with a body */ - con->http_status = 415; - con->mode = DIRECT; - - 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; - } - con->mode = DIRECT; - } else { - con->http_status = 201; - con->send->is_closed = 1; - } - - return HANDLER_FINISHED; - case HTTP_METHOD_DELETE: - if (p->conf.is_readonly) { - con->http_status = 403; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - - /* does the client have a lock for this connection ? */ - if (!webdav_has_lock(srv, con, p, con->uri.path)) { - con->http_status = 423; - con->mode = DIRECT; - 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; - } - con->mode = DIRECT; - } else if (S_ISDIR(st.st_mode)) { - buffer *multi_status_resp = buffer_init(); - - if (webdav_delete_dir(srv, con, p, &(con->physical), multi_status_resp)) { - /* we got an error somewhere in between, build a 207 */ - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/xml; charset=\"utf-8\"")); - - b = chunkqueue_get_append_buffer(con->send); - - 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); - } - - con->http_status = 207; - con->send->bytes_in += b->used-1; - con->send->is_closed = 1; - } else { - /* everything went fine, remove the directory */ - - if (-1 == rmdir(con->physical.path->ptr)) { - switch(errno) { - case ENOENT: - con->http_status = 404; - break; - default: - con->http_status = 501; - break; - } - con->mode = DIRECT; - } else { - con->http_status = 204; - con->send->is_closed = 1; - } - } - - 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; - } - con->mode = DIRECT; - } else { - con->http_status = 204; - con->send->is_closed = 1; - } - return HANDLER_FINISHED; - case HTTP_METHOD_PUT: { - int fd; - chunkqueue *cq = con->recv; - chunk *c; - data_string *ds_range; - - if (p->conf.is_readonly) { - con->http_status = 403; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - - /* is a exclusive lock set on the source */ - if (!webdav_has_lock(srv, con, p, con->uri.path)) { - con->http_status = 423; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - - - if (chunkqueue_length(cq) != (off_t)con->request.content_length) { - ERROR("%s", "chunkqueue_length didn't match request.content_length"); - con->http_status = 500; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - - /* 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 != (ds_range = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Content-Range")))) { - const char *num = ds_range->value->ptr; - off_t offset; - char *err = NULL; - - if (0 != strncmp(num, "bytes ", 6)) { - con->http_status = 501; /* not implemented */ - con->mode = DIRECT; - - 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 */ - con->mode = DIRECT; - - return HANDLER_FINISHED; - } - - offset = str_to_off_t(num, &err, 10); - - if (offset == STR_OFF_T_MAX || - offset == STR_OFF_T_MIN) { - /** - * conversion did a over- or underrun - */ - con->http_status = 501; /* not implemented */ - con->mode = DIRECT; - - return HANDLER_FINISHED; - } - - if (*err != '-' || offset < 0) { - con->http_status = 501; /* not implemented */ - con->mode = DIRECT; - - 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; - } - con->mode = DIRECT; - return HANDLER_FINISHED; - } - - if (-1 == lseek(fd, offset, SEEK_SET)) { - con->http_status = 501; /* not implemented */ - con->mode = DIRECT; - - 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; - con->mode = DIRECT; - - return HANDLER_FINISHED; - } else { - con->http_status = 201; /* created */ - } - } else { - con->http_status = 200; /* modified */ - } - } - - con->send->is_closed = 1; - - for (c = cq->first; c; c = cq->first) { - int r = 0; - - /* copy all chunks */ - switch(c->type) { - case FILE_CHUNK: - - if (c->file.mmap.start == MAP_FAILED) { - if (-1 == c->file.fd && /* open the file if not already open */ - -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); - - return HANDLER_ERROR; - } - - if (MAP_FAILED == (c->file.mmap.start = mmap(0, c->file.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ", - strerror(errno), c->file.name, c->file.fd); - - return HANDLER_ERROR; - } - - c->file.mmap.length = c->file.length; - - close(c->file.fd); - c->file.fd = -1; - - /* chunk_reset() or chunk_free() will cleanup for us */ - } - - if ((r = write(fd, c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) { - switch(errno) { - case ENOSPC: - con->http_status = 507; - - break; - default: - con->http_status = 403; - break; - } - } - break; - case MEM_CHUNK: - if ((r = write(fd, c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) { - switch(errno) { - case ENOSPC: - con->http_status = 507; - - break; - default: - con->http_status = 403; - break; - } - } - break; - case UNUSED_CHUNK: - break; - } - - if (r > 0) { - c->offset += r; - cq->bytes_out += r; - } else { - break; - } - chunkqueue_remove_finished_chunks(cq); - } - close(fd); - - return HANDLER_FINISHED; - } - case HTTP_METHOD_MOVE: - case HTTP_METHOD_COPY: { - buffer *destination = NULL; - char *sep, *start; - int overwrite = 1; - - if (p->conf.is_readonly) { - con->http_status = 403; - con->mode = DIRECT; - 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, p, con->uri.path)) { - con->http_status = 423; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - } - - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Destination")))) { - destination = ds->value; - } else { - con->http_status = 400; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Overwrite")))) { - if (ds->value->used != 2 || - (ds->value->ptr[0] != 'F' && - ds->value->ptr[0] != 'T') ) { - con->http_status = 400; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - overwrite = (ds->value->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 - * */ - - buffer_reset(p->uri.scheme); - buffer_reset(p->uri.path_raw); - buffer_reset(p->uri.authority); - - start = destination->ptr; - - if (NULL == (sep = strstr(start, "://"))) { - con->http_status = 400; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - buffer_copy_string_len(p->uri.scheme, start, sep - start); - - start = sep + 3; - - if (NULL == (sep = strchr(start, '/'))) { - con->http_status = 400; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - 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_raw, start); - } else { - buffer_copy_string_len(p->uri.path_raw, start, sep - start); - } - - if (!buffer_is_equal(p->uri.authority, con->uri.authority)) { - /* not the same host */ - con->http_status = 502; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - - buffer_copy_string_buffer(p->tmp_buf, p->uri.path_raw); - buffer_urldecode_path(p->tmp_buf); - buffer_path_simplify(p->uri.path, p->tmp_buf); - - /* we now have a URI which is clean. transform it into a physical path */ - buffer_copy_string_buffer(p->physical.doc_root, con->physical.doc_root); - buffer_copy_string_buffer(p->physical.rel_path, p->uri.path); - - if (con->conf.force_lowercase_filenames) { - buffer_to_lower(p->physical.rel_path); - } - - buffer_copy_string_buffer(p->physical.path, p->physical.doc_root); - PATHNAME_APPEND_SLASH(p->physical.path); - buffer_copy_string_buffer(p->physical.basedir, p->physical.path); - - /* don't add a second / */ - if (p->physical.rel_path->ptr[0] == '/') { - buffer_append_string_len(p->physical.path, p->physical.rel_path->ptr + 1, p->physical.rel_path->used - 2); - } else { - buffer_append_string_buffer(p->physical.path, 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; - } - con->mode = DIRECT; - } else if (S_ISDIR(st.st_mode)) { - int r; - /* src is a directory */ - - con->http_status = 204; - con->send->is_closed = 1; - if (-1 == stat(p->physical.path->ptr, &st)) { - if (-1 == mkdir(p->physical.path->ptr, WEBDAV_DIR_MODE)) { - con->http_status = 403; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - con->http_status = 201; - } else if (!S_ISDIR(st.st_mode)) { - if (overwrite == 0) { - /* copying into a non-dir ? */ - con->http_status = 409; - con->mode = DIRECT; - return HANDLER_FINISHED; - } else { - unlink(p->physical.path->ptr); - if (-1 == mkdir(p->physical.path->ptr, WEBDAV_DIR_MODE)) { - con->http_status = 403; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - } - } - - /* copy the content of src to dest */ - if (0 != (r = webdav_copy_dir(srv, con, p, &(con->physical), &(p->physical), overwrite))) { - con->http_status = r; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - if (con->request.http_method == HTTP_METHOD_MOVE) { - b = buffer_init(); - webdav_delete_dir(srv, con, p, &(con->physical), b); /* content */ - buffer_free(b); - - rmdir(con->physical.path->ptr); - } - } else { - /* it is just a file, good */ - int r; - - /* does the client have a lock for this connection ? */ - if (!webdav_has_lock(srv, con, p, p->uri.path)) { - con->http_status = 423; - con->mode = DIRECT; - 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 */ - - 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 = 201; /* we will create a new one */ - con->send->is_closed = 1; - - switch(errno) { - case ENOTDIR: - con->http_status = 409; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - } else if (overwrite == 0) { - /* destination exists, but overwrite is not set */ - con->http_status = 412; - con->mode = DIRECT; - return HANDLER_FINISHED; - } else { - con->http_status = 204; /* resource already existed */ - con->send->is_closed = 1; - } - - 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 = p->conf.stmt_move_uri; - - if (stmt) { - - sqlite3_reset(stmt); - - /* bind the values to the insert */ - sqlite3_bind_text(stmt, 1, - p->uri.path->ptr, - p->uri.path->used - 1, - SQLITE_TRANSIENT); - - sqlite3_bind_text(stmt, 2, - con->uri.path->ptr, - con->uri.path->used - 1, - 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, p, &(con->physical), &(p->physical), overwrite))) { - con->http_status = r; - con->mode = DIRECT; - - return HANDLER_FINISHED; - } - - if (con->request.http_method == HTTP_METHOD_MOVE) { - b = buffer_init(); - webdav_delete_file(srv, con, p, &(con->physical), b); - buffer_free(b); - } - } - - return HANDLER_FINISHED; - } - case HTTP_METHOD_PROPPATCH: - if (p->conf.is_readonly) { - con->http_status = 403; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - - if (!webdav_has_lock(srv, con, p, con->uri.path)) { - con->http_status = 423; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - - /* check if destination exists */ - if (-1 == stat(con->physical.path->ptr, &st)) { - switch(errno) { - case ENOENT: - con->http_status = 404; - con->mode = DIRECT; - break; - } - } - -#ifdef USE_PROPPATCH - if (p->conf.sql && con->request.content_length > 0) { - xmlDocPtr xml; - - if (1 == webdav_parse_chunkqueue(srv, con, p, con->recv, &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; - 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, - con->uri.path->ptr, - con->uri.path->used - 1, - 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) { - sqlite3_bind_text(stmt, 4, - (char *)xmlNodeGetContent(prop), - strlen((char *)xmlNodeGetContent(prop)), - 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 (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; - con->mode = DIRECT; - } 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->send->is_closed = 1; - - return HANDLER_FINISHED; - } - -propmatch_cleanup: - - xmlFreeDoc(xml); - } else { - con->http_status = 400; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - } -#endif - con->http_status = 501; - con->mode = DIRECT; - return HANDLER_FINISHED; - case HTTP_METHOD_LOCK: - /** - * 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 - */ - - if (depth != 0 && depth != -1) { - con->http_status = 400; - con->mode = DIRECT; - - return HANDLER_FINISHED; - } - -#ifdef USE_LOCKS - if (p->conf.sql && con->request.content_length > 0) { - xmlDocPtr xml; - buffer *hdr_if = NULL; - - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("If")))) { - hdr_if = ds->value; - } - - /* we don't support Depth: Infinity on locks */ - if (hdr_if == NULL && depth == -1) { - con->http_status = 409; /* Conflict */ - con->mode = DIRECT; - - return HANDLER_FINISHED; - } - - if (1 == webdav_parse_chunkqueue(srv, con, p, con->recv, &xml)) { - xmlNode *rootnode = xmlDocGetRootElement(xml); - - assert(rootnode); - - if (0 == xmlStrcmp(rootnode->name, BAD_CAST "lockinfo")) { - xmlNode *lockinfo; - const xmlChar *lockscope = NULL, *locktype = NULL; /* unused variable: , *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; - con->mode = DIRECT; - - 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; - con->mode = DIRECT; - - 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, - p->uri.path->ptr, - p->uri.path->used - 1, - 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; - con->mode = DIRECT; - } else if (0 == xmlStrcmp(lockscope, BAD_CAST "exclusive")) { - /* resourse is locked with a shared lock - * client wants exclusive */ - con->http_status = 423; - con->mode = DIRECT; - } - } - 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(srv, con, p->tmp_buf, (const char *)lockscope, (const char *)locktype, depth); - - con->http_status = 201; - con->send->is_closed = 1; - } - } - } - - xmlFreeDoc(xml); - return HANDLER_FINISHED; - } else { - con->http_status = 400; - con->mode = DIRECT; - return HANDLER_FINISHED; - } - } else { - - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("If")))) { - buffer *locktoken = ds->value; - sqlite3_stmt *stmt = p->conf.stmt_refresh_lock; - - /* remove the < > around the token */ - if (locktoken->used < 6) { - con->http_status = 400; - con->mode = DIRECT; - - return HANDLER_FINISHED; - } - - buffer_copy_string_len(p->tmp_buf, locktoken->ptr + 2, locktoken->used - 5); - - sqlite3_reset(stmt); - - sqlite3_bind_text(stmt, 1, - CONST_BUF_LEN(p->tmp_buf), - SQLITE_TRANSIENT); - - if (SQLITE_DONE != sqlite3_step(stmt)) { - TRACE("refresh lock failed: %s", sqlite3_errmsg(p->conf.sql)); - } - - webdav_lockdiscovery(srv, con, p->tmp_buf, "exclusive", "write", 0); - - con->http_status = 200; - con->send->is_closed = 1; - return HANDLER_FINISHED; - } else { - /* we need a lock-token to refresh */ - con->http_status = 400; - con->mode = DIRECT; - - return HANDLER_FINISHED; - } - } - break; -#else - con->http_status = 501; - con->mode = DIRECT; - return HANDLER_FINISHED; -#endif - case HTTP_METHOD_UNLOCK: -#ifdef USE_LOCKS - if (NULL != (ds = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("Lock-Token")))) { - buffer *locktoken = ds->value; - sqlite3_stmt *stmt = p->conf.stmt_remove_lock; - - /* remove the < > around the token */ - if (locktoken->used < 4) { - con->http_status = 400; - con->mode = DIRECT; - - 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, locktoken->used - 3); - - 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); - - 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; - con->mode = DIRECT; - } else { - con->http_status = 204; - con->send->is_closed = 1; - } - return HANDLER_FINISHED; - } else { - /* we need a lock-token to unlock */ - con->http_status = 400; - con->mode = DIRECT; - - return HANDLER_FINISHED; - } - break; -#else - con->http_status = 501; - con->mode = DIRECT; - return HANDLER_FINISHED; -#endif - default: - break; - } - - /* not found */ - return HANDLER_GO_ON; -} - -/** - * calls the request-handler if we have received all the content - */ -CONNECTION_FUNC(mod_webdav_recv_request_content) { - chunkqueue *in = con->recv; - plugin_data *p = p_d; - handler_t res; - - /** - * is the content for webdav - */ - if (con->mode != p->id) return HANDLER_GO_ON; - - if (!in->is_closed) return HANDLER_GO_ON; - - /* we received all the content, let's call the webdav handler */ - - res = mod_webdav_subrequest_handler(srv, con, p_d); - - /* mark body as read */ - con->recv->bytes_out += chunkqueue_skip(con->recv, con->recv->bytes_in - con->recv->bytes_out); - chunkqueue_remove_finished_chunks(con->recv); - - return res; -} - -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_webdav_plugin_init(plugin *p); -LI_EXPORT 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; /* check if we handle this URL */ -#if 0 - /* will get called when the content is received */ -#endif - p->handle_physical = mod_webdav_physical_handler; - p->handle_send_request_content = mod_webdav_recv_request_content; /* check if we received all the content */ - p->set_defaults = mod_webdav_set_defaults; - p->cleanup = mod_webdav_free; - - p->data = NULL; - - return 0; -} diff --git a/src/network.c b/src/network.c deleted file mode 100644 index 988dc940..00000000 --- a/src/network.c +++ /dev/null @@ -1,891 +0,0 @@ -#include <sys/types.h> -#include <sys/stat.h> - -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <assert.h> - -#include <stdio.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "connections.h" -#include "plugin.h" -#include "joblist.h" - -#include "network_backends.h" -#include "sys-mmap.h" -#include "sys-socket.h" -#include "sys-files.h" - -#ifdef USE_OPENSSL -# include <openssl/ssl.h> -# include <openssl/err.h> -# include <openssl/rand.h> -#endif - -#define BACKEND_HANDLERS(read, write) network_read_chunkqueue_##read, network_write_chunkqueue_##write -static network_backend_info_t network_backends[] = { - /* lowest id wins */ - { - NETWORK_BACKEND_LINUX_SENDFILE, - "linux-sendfile", - NULL, -#if defined USE_WRITE && defined USE_LINUX_SENDFILE - BACKEND_HANDLERS(read, linuxsendfile) -#else - NULL, NULL -#endif - }, - { - NETWORK_BACKEND_LINUX_AIO_SENDFILE, - "linux-aio-sendfile", - NULL, -#if defined USE_WRITE && defined USE_LINUX_AIO_SENDFILE - BACKEND_HANDLERS(read, linuxaiosendfile) -#else - NULL, NULL -#endif - }, - { - NETWORK_BACKEND_FREEBSD_SENDFILE, - "freebsd-sendfile", - NULL, -#if defined USE_WRITE && defined USE_FREEBSD_SENDFILE - BACKEND_HANDLERS(read, freebsdsendfile) -#else - NULL, NULL -#endif - }, - { - NETWORK_BACKEND_SOLARIS_SENDFILEV, - "solaris-sendfilev", - NULL, -#if defined USE_WRITE && defined USE_SOLARIS_SENDFILEV - BACKEND_HANDLERS(read, solarissendfilev) -#else - NULL, NULL -#endif - }, - { - NETWORK_BACKEND_POSIX_AIO, - "posix-aio", - NULL, -#if defined USE_WRITE && defined USE_POSIX_AIO - BACKEND_HANDLERS(read, posixaio) -#else - NULL, NULL -#endif - }, - - { - NETWORK_BACKEND_GTHREAD_AIO, - "gthread-aio", - NULL, -#if defined USE_WRITE && defined USE_GTHREAD_AIO - BACKEND_HANDLERS(read, gthreadaio) -#else - NULL, NULL -#endif - }, - { - NETWORK_BACKEND_GTHREAD_SENDFILE, - "gthread-sendfile", - NULL, -#if defined USE_WRITE && defined USE_GTHREAD_AIO && defined USE_GTHREAD_SENDFILE - BACKEND_HANDLERS(read, gthreadsendfile) -#else - NULL, NULL -#endif - }, - - { - NETWORK_BACKEND_GTHREAD_FREEBSD_SENDFILE, - "gthread-freebsd-sendfile", - NULL, -#if defined USE_WRITE && defined USE_GTHREAD_AIO && defined USE_GTHREAD_FREEBSD_SENDFILE - BACKEND_HANDLERS(read, gthreadfreebsdsendfile) -#else - NULL, NULL -#endif - }, - - { - NETWORK_BACKEND_WRITEV, - "writev", - NULL, -#if defined USE_WRITE && defined USE_WRITEV - BACKEND_HANDLERS(read, writev) -#else - NULL, NULL -#endif - }, - { - NETWORK_BACKEND_WRITE, - "write", - NULL, -#if defined USE_WRITE - BACKEND_HANDLERS(read, write) -#else - NULL, NULL -#endif - }, - { - NETWORK_BACKEND_WIN32_TRANSMITFILE, - "win32-transmitfile", - NULL, -#if defined USE_WIN32_TRANSMITFILE - BACKEND_HANDLERS(win32recv, win32transmitfile) -#else - NULL, NULL -#endif - }, - { - NETWORK_BACKEND_WIN32_SEND, - "win32-send", - NULL, -#if defined USE_WIN32_SEND && defined USE_WIN32_SEND - BACKEND_HANDLERS(win32recv, win32send) -#else - NULL, NULL -#endif - }, - - { - NETWORK_BACKEND_UNSET, - NULL, - NULL, - NULL, NULL - } -}; - -const network_backend_info_t *network_get_backends() { - return network_backends; -} - -const network_backend_info_t *network_get_defaultbackend() { - const network_backend_info_t *backend = network_get_backends(); - - while (backend->name) { - if (backend->write_handler) { - return backend; - } - backend ++; - } - - return NULL; -} - -const network_backend_info_t *network_get_backend_info_by_type(network_backend_t type) { - const network_backend_info_t *backend = network_get_backends(); - - while (backend->name) { - if (type == backend->type) { - return backend; - } - backend ++; - } - - return NULL; -} - -const network_backend_info_t *network_get_backend_info_by_name(const char *name) { - const network_backend_info_t *backend = network_get_backends(); - - while (backend->name) { - if (strcmp(name, backend->name) == 0) { - return backend; - } - backend ++; - } - - return NULL; -} - -handler_t network_server_handle_fdevent(void *s, void *context, int revents) { - server *srv = (server *)s; - server_socket *srv_socket = (server_socket *)context; - connection *con; - int loops = 0; - - UNUSED(context); - - if (revents != FDEVENT_IN) { - log_error_write(srv, __FILE__, __LINE__, "sdd", - "strange event for server socket", - srv_socket->sock->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++) { - joblist_append(srv, con); - } - return HANDLER_GO_ON; -} - -#if defined USE_OPENSSL && ! defined OPENSSL_NO_TLSEXT -static int network_ssl_servername_callback(SSL *ssl, int *al, server *srv) { - const char *servername; - connection *con = (connection *) SSL_get_app_data(ssl); - - UNUSED(al); - - buffer_copy_string(con->uri.scheme, "https"); - - if (NULL == (servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) { -#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; - } - buffer_copy_string(con->sock->tlsext_server_name, servername); - buffer_to_lower(con->sock->tlsext_server_name); - - config_patch_connection(srv, con, COMP_SERVER_SOCKET); - config_patch_connection(srv, con, COMP_HTTP_SCHEME); - config_patch_connection(srv, con, COMP_HTTP_HOST); - - if (NULL == con->conf.ssl_ctx) { - /* ssl_ctx <=> pemfile was set <=> ssl_ctx got patched: so this should never happen */ - log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", - "null SSL_CTX for TLS server name", con->sock->tlsext_server_name); - return SSL_TLSEXT_ERR_ALERT_FATAL; - } - - /* switch to new SSL_CTX in reaction to a client's server_name extension */ - if (con->conf.ssl_ctx != SSL_set_SSL_CTX(ssl, con->conf.ssl_ctx)) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", - "failed to set SSL_CTX for TLS server name", con->sock->tlsext_server_name); - return SSL_TLSEXT_ERR_ALERT_FATAL; - } - - return SSL_TLSEXT_ERR_OK; -} -#endif - -static int network_server_init(server *srv, buffer *host_token, specific_config *s) { - int val; - socklen_t addr_len; - server_socket *srv_socket; - char *sp; - unsigned int port = 0; - const char *host; - buffer *b; - int is_unix_domain_socket = 0; - int fd; - -#ifdef SO_ACCEPTFILTER - struct accept_filter_arg afa; -#endif - -#ifdef _WIN32 - WORD wVersionRequested; - WSADATA wsaData; - int err; - - wVersionRequested = MAKEWORD( 2, 2 ); - - err = WSAStartup( wVersionRequested, &wsaData ); - if ( err != 0 ) { - /* Tell the user that we could not find a usable */ - /* WinSock DLL. */ - return -1; - } -#endif - - srv_socket = calloc(1, sizeof(*srv_socket)); - srv_socket->sock = iosocket_init(); - - srv_socket->srv_token = buffer_init(); - buffer_copy_string_buffer(srv_socket->srv_token, host_token); - - b = buffer_init(); - buffer_copy_string_buffer(b, host_token); - - /* ipv4:port - * [ipv6]:port - */ - if (NULL == (sp = strrchr(b->ptr, ':'))) { - log_error_write(srv, __FILE__, __LINE__, "sb", "value of $SERVER[\"socket\"] has to be \"ip:port\".", b); - - goto error_free_socket; - } - - host = b->ptr; - - /* check for [ and ] */ - if (b->ptr[0] == '[' && *(sp-1) == ']') { - *(sp-1) = '\0'; - host++; - - s->use_ipv6 = 1; - } - - *(sp++) = '\0'; - - port = strtol(sp, NULL, 10); - - if (host[0] == '/') { - /* host is a unix-domain-socket */ - is_unix_domain_socket = 1; - } else if (port == 0 || port > 65535) { - log_error_write(srv, __FILE__, __LINE__, "sd", "port out of range:", port); - - goto error_free_socket; - } - - if (*host == '\0') host = NULL; - - if (is_unix_domain_socket) { -#ifdef HAVE_SYS_UN_H - - srv_socket->addr.plain.sa_family = AF_UNIX; - - if (-1 == (srv_socket->sock->fd = socket(srv_socket->addr.plain.sa_family, SOCK_STREAM, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno)); - goto error_free_socket; - } -#else - log_error_write(srv, __FILE__, __LINE__, "s", - "ERROR: Unix Domain sockets are not supported."); - goto error_free_socket; -#endif - } - -#ifdef HAVE_IPV6 - if (s->use_ipv6) { - srv_socket->addr.plain.sa_family = AF_INET6; - - if (-1 == (srv_socket->sock->fd = socket(srv_socket->addr.plain.sa_family, SOCK_STREAM, IPPROTO_TCP))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno)); - goto error_free_socket; - } - srv_socket->use_ipv6 = 1; - } -#endif - - if (srv_socket->sock->fd == -1) { - srv_socket->addr.plain.sa_family = AF_INET; - if (-1 == (srv_socket->sock->fd = socket(srv_socket->addr.plain.sa_family, SOCK_STREAM, IPPROTO_TCP))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "socket failed:", strerror(errno)); - goto error_free_socket; - } - } - - val = 1; - if (setsockopt(srv_socket->sock->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ss", "socketsockopt failed:", strerror(errno)); - goto error_free_socket; - } - - switch(srv_socket->addr.plain.sa_family) { -#ifdef HAVE_IPV6 - case AF_INET6: - memset(&srv_socket->addr, 0, sizeof(struct sockaddr_in6)); - srv_socket->addr.ipv6.sin6_family = AF_INET6; - if (host == NULL) { - srv_socket->addr.ipv6.sin6_addr = in6addr_any; - } 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(host, NULL, &hints, &res))) { - log_error_write(srv, __FILE__, __LINE__, - "sssss", "getaddrinfo failed: ", - gai_strerror(r), "'", host, "'"); - - goto error_free_socket; - } - - memcpy(&(srv_socket->addr), res->ai_addr, res->ai_addrlen); - - freeaddrinfo(res); - } - srv_socket->addr.ipv6.sin6_port = htons(port); - addr_len = sizeof(struct sockaddr_in6); - break; -#endif - case AF_INET: - memset(&srv_socket->addr, 0, sizeof(struct sockaddr_in)); - srv_socket->addr.ipv4.sin_family = AF_INET; - if (host == NULL) { - srv_socket->addr.ipv4.sin_addr.s_addr = htonl(INADDR_ANY); - } else { - struct hostent *he; - if (NULL == (he = gethostbyname(host))) { - log_error_write(srv, __FILE__, __LINE__, - "sds", "gethostbyname failed: ", - h_errno, host); - goto error_free_socket; - } - - if (he->h_addrtype != AF_INET) { - log_error_write(srv, __FILE__, __LINE__, "sd", "addr-type != AF_INET: ", he->h_addrtype); - goto error_free_socket; - } - - if (he->h_length != sizeof(struct in_addr)) { - log_error_write(srv, __FILE__, __LINE__, "sd", "addr-length != sizeof(in_addr): ", he->h_length); - goto error_free_socket; - } - - memcpy(&(srv_socket->addr.ipv4.sin_addr.s_addr), he->h_addr_list[0], he->h_length); - } - srv_socket->addr.ipv4.sin_port = htons(port); - - addr_len = sizeof(struct sockaddr_in); - - break; -#ifndef _WIN32 - case AF_UNIX: - srv_socket->addr.un.sun_family = AF_UNIX; - strcpy(srv_socket->addr.un.sun_path, host); - -#ifdef SUN_LEN - addr_len = SUN_LEN(&srv_socket->addr.un); -#else - /* stevens says: */ - addr_len = strlen(host) + 1 + sizeof(srv_socket->addr.un.sun_family); -#endif - - /* check if the socket exists and try to connect to it. */ - if (-1 != (fd = connect(srv_socket->sock->fd, (struct sockaddr *) &(srv_socket->addr), addr_len))) { - close(fd); - - log_error_write(srv, __FILE__, __LINE__, "ss", - "server socket is still in use:", - host); - - - goto error_free_socket; - } - - /* 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)); - - goto error_free_socket; - } - - break; -#endif - default: - addr_len = 0; - - goto error_free_socket; - } - - if (0 != bind(srv_socket->sock->fd, (struct sockaddr *) &(srv_socket->addr), addr_len)) { - switch(srv_socket->addr.plain.sa_family) { - case AF_UNIX: - log_error_write(srv, __FILE__, __LINE__, "sds", - "can't bind to socket:", - host, strerror(errno)); - break; - default: - log_error_write(srv, __FILE__, __LINE__, "ssds", - "can't bind to port:", - host, port, strerror(errno)); - break; - } - goto error_free_socket; - } - - if (-1 == listen(srv_socket->sock->fd, 128 * 8)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "listen failed: ", strerror(errno)); - goto error_free_socket; - } - - if (s->is_ssl) { -#ifdef USE_OPENSSL - if (NULL == (srv_socket->ssl_ctx = s->ssl_ctx)) { - log_error_write(srv, __FILE__, __LINE__, "s", "ssl.pemfile has to be set"); - goto error_free_socket; - } -#else - - buffer_free(srv_socket->srv_token); - free(srv_socket); - - buffer_free(b); - - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - "ssl requested but openssl support is not compiled in"); - - goto error_free_socket; -#endif - } else { -#ifdef SO_ACCEPTFILTER - /* - * FreeBSD accf_http filter - * - */ - memset(&afa, 0, sizeof(afa)); - strcpy(afa.af_name, "httpready"); - if (setsockopt(srv_socket->sock->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa)) < 0) { - if (errno != ENOENT) { - log_error_write(srv, __FILE__, __LINE__, "ss", "can't set accept-filter 'httpready': ", strerror(errno)); - } - } -#endif - } - -#ifdef FD_CLOEXEC - /* set FD_CLOEXEC now, fdevent_fcntl_set is called later; needed for pipe-logger forks */ - fcntl(srv_socket->sock->fd, F_SETFD, FD_CLOEXEC); -#endif - - srv_socket->is_ssl = s->is_ssl; - - 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)); - } 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)); - } - - srv->srv_sockets.ptr[srv->srv_sockets.used++] = srv_socket; - buffer_free(b); - - return 0; - -error_free_socket: - iosocket_free(srv_socket->sock); - buffer_free(srv_socket->srv_token); - free(srv_socket); - - return -1; -} - -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->sock->fd != -1) { - /* check if server fd are already registered */ - if (srv_socket->sock->fde_ndx != -1) { - fdevent_event_del(srv->ev, srv_socket->sock); - fdevent_unregister(srv->ev, srv_socket->sock); - } - } - - iosocket_free(srv_socket->sock); - - buffer_free(srv_socket->srv_token); - - free(srv_socket); - } - -#ifdef USE_OPENSSL - ERR_free_strings(); -#endif - free(srv->srv_sockets.ptr); - - return 0; -} - -int network_init(server *srv) { - buffer *b; - size_t i; - const network_backend_info_t *backend; - -#ifdef USE_OPENSSL - /* load SSL certificates */ - for (i = 0; i < srv->config_context->used; i++) { - specific_config *s = srv->config_storage[i]; - - if (buffer_is_empty(s->ssl_pemfile)) continue; - -#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 (srv->ssl_is_init == 0) { - SSL_load_error_strings(); - SSL_library_init(); - srv->ssl_is_init = 1; - - if (0 == RAND_status()) { - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - "not enough entropy in the pool"); - return -1; - } - } - - if (NULL == (s->ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - ERR_error_string(ERR_get_error(), NULL)); - return -1; - } - - if (!s->ssl_use_sslv2) { - /* disable SSLv2 */ - if (!(SSL_OP_NO_SSLv2 & SSL_CTX_set_options(s->ssl_ctx, SSL_OP_NO_SSLv2))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - ERR_error_string(ERR_get_error(), NULL)); - return -1; - } - } - - if (!buffer_is_empty(s->ssl_cipher_list)) { - 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 (!buffer_is_empty(s->ssl_ca_file)) { - if (1 != SSL_CTX_load_verify_locations(s->ssl_ctx, s->ssl_ca_file->ptr, NULL)) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", - ERR_error_string(ERR_get_error(), NULL), s->ssl_ca_file); - return -1; - } - if (s->ssl_verifyclient) { - STACK_OF(X509_NAME) *certs = SSL_load_client_CA_file(s->ssl_ca_file->ptr); - if (!certs) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", - ERR_error_string(ERR_get_error(), NULL), s->ssl_ca_file); - } - if (SSL_CTX_set_session_id_context(s->ssl_ctx, (void*) &srv, sizeof(srv)) != 1) { - log_error_write(srv, __FILE__, __LINE__, "ss", "SSL:", - ERR_error_string(ERR_get_error(), NULL)); - return -1; - } - SSL_CTX_set_client_CA_list(s->ssl_ctx, certs); - SSL_CTX_set_verify( - s->ssl_ctx, - SSL_VERIFY_PEER | (s->ssl_verifyclient_enforce ? SSL_VERIFY_FAIL_IF_NO_PEER_CERT : 0), - NULL - ); - SSL_CTX_set_verify_depth(s->ssl_ctx, s->ssl_verifyclient_depth); - } - } else if (s->ssl_verifyclient) { - log_error_write( - srv, __FILE__, __LINE__, "s", - "SSL: You specified ssl.verifyclient.activate but no ca_file" - ); - } - - if (SSL_CTX_use_certificate_file(s->ssl_ctx, s->ssl_pemfile->ptr, SSL_FILETYPE_PEM) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", - ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); - return -1; - } - - if (SSL_CTX_use_PrivateKey_file (s->ssl_ctx, s->ssl_pemfile->ptr, SSL_FILETYPE_PEM) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ssb", "SSL:", - ERR_error_string(ERR_get_error(), NULL), s->ssl_pemfile); - return -1; - } - - if (SSL_CTX_check_private_key(s->ssl_ctx) != 1) { - log_error_write(srv, __FILE__, __LINE__, "sssb", "SSL:", - "Private key does not match the certificate public key, reason:", - ERR_error_string(ERR_get_error(), NULL), - s->ssl_pemfile); - return -1; - } - -#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; - } -#endif - } -#endif - - b = buffer_init(); - - buffer_copy_string_buffer(b, srv->srvconf.bindhost); - buffer_append_string_len(b, CONST_STR_LEN(":")); - buffer_append_long(b, srv->srvconf.port); - - if (0 != network_server_init(srv, b, srv->config_storage[0])) { - return -1; - } - buffer_free(b); - -#ifdef USE_OPENSSL - srv->network_ssl_backend_write = network_write_chunkqueue_openssl; -#endif - - backend = network_get_backend_info_by_type(srv->network_backend); - assert(backend && backend->read_handler); - srv->network_backend_read = backend->read_handler; - srv->network_backend_write = backend->write_handler; - -#ifdef USE_OPENSSL - srv->network_ssl_backend_write = network_write_chunkqueue_openssl; - srv->network_ssl_backend_read = network_read_chunkqueue_openssl; -#endif - - /* check for $SERVER["socket"] */ - 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]; - size_t j; - - /* not our stage */ - if (COMP_SERVER_SOCKET != dc->comp) continue; - - if (dc->cond != CONFIG_COND_EQ) continue; - - /* check if we already know this socket, - * if yes, don't init it */ - for (j = 0; j < srv->srv_sockets.used; j++) { - if (buffer_is_equal(srv->srv_sockets.ptr[j]->srv_token, dc->string)) { - break; - } - } - - if (j == srv->srv_sockets.used) { - if (0 != network_server_init(srv, dc->string, s)) return -1; - } - } - - return 0; -} - -int network_register_fdevents(server *srv) { - size_t i; - if (-1 == fdevent_reset(srv->ev)) { - return -1; - } - /* 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->sock, network_server_handle_fdevent, srv_socket); - fdevent_event_add(srv->ev, srv_socket->sock, FDEVENT_IN); - } - return 0; -} - -network_status_t network_read(server *srv, connection *con, iosocket *sock, chunkqueue *cq) { - server_socket *srv_socket = con->srv_socket; - network_status_t ret = NETWORK_STATUS_UNSET; - off_t start_bytes_in = cq->bytes_in; - - if (srv_socket->is_ssl) { -#ifdef USE_OPENSSL - ret = srv->network_ssl_backend_read(srv, con, sock, cq); -#else - ret = NETWORK_STATUS_FATAL_ERROR; -#endif - } else { - ret = srv->network_backend_read(srv, con, sock, cq); - } - - con->bytes_read += cq->bytes_in - start_bytes_in; - - return ret; -} - -network_status_t network_write_chunkqueue(server *srv, connection *con, chunkqueue *cq) { - network_status_t ret = NETWORK_STATUS_UNSET; - off_t written = 0; -#ifdef TCP_CORK - int corked = 0; -#endif - server_socket *srv_socket = con->srv_socket; - - if (con->conf.global_kbytes_per_second && - *(con->conf.global_bytes_per_second_cnt_ptr) > con->conf.global_kbytes_per_second * 1024) { - /* we reached the global traffic limit */ - - con->traffic_limit_reached = 1; - joblist_append(srv, con); - - return NETWORK_STATUS_WAIT_FOR_AIO_EVENT; - } - - written = cq->bytes_out; - -#ifdef TCP_CORK - /* Linux: put a cork into the socket as we want to combine the write() calls - * but only if we really have multiple chunks - */ - if (cq->first && cq->first->next) { - corked = 1; - setsockopt(con->sock->fd, IPPROTO_TCP, TCP_CORK, &corked, sizeof(corked)); - } -#endif - - if (srv_socket->is_ssl) { -#ifdef USE_OPENSSL - ret = srv->network_ssl_backend_write(srv, con, con->sock, cq); -#endif - } else { - ret = srv->network_backend_write(srv, con, con->sock, cq); - } - - switch (ret) { - case NETWORK_STATUS_WAIT_FOR_FD: - case NETWORK_STATUS_WAIT_FOR_AIO_EVENT: - case NETWORK_STATUS_WAIT_FOR_EVENT: - case NETWORK_STATUS_SUCCESS: - chunkqueue_remove_finished_chunks(cq); - - break; - default: - break; - } - -#ifdef TCP_CORK - if (corked) { - corked = 0; - setsockopt(con->sock->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; - - if (con->conf.kbytes_per_second && - (con->bytes_written_cur_second > con->conf.kbytes_per_second * 1024)) { - /* we reached the traffic limit */ - - con->traffic_limit_reached = 1; - joblist_append(srv, con); - } - return ret; -} diff --git a/src/network.h b/src/network.h deleted file mode 100644 index 1e3016f3..00000000 --- a/src/network.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _NETWORK_H_ -#define _NETWORK_H_ - -#include "settings.h" -#include "server.h" - -LI_API network_status_t network_write_chunkqueue(server *srv, connection *con, chunkqueue *c); -LI_API network_status_t network_read(server *srv, connection *con, iosocket *sock, chunkqueue *c); - -LI_API int network_init(server *srv); -LI_API int network_close(server *srv); - -LI_API int network_register_fdevents(server *srv); -LI_API handler_t network_server_handle_fdevent(void *s, void *context, int revents); - -#endif diff --git a/src/network_backends.h b/src/network_backends.h deleted file mode 100644 index 4f99a663..00000000 --- a/src/network_backends.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef _NETWORK_BACKENDS_H_ -#define _NETWORK_BACKENDS_H_ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <sys/types.h> - -#include "settings.h" -#include "base.h" -#include "network.h" - -#define NETWORK_BACKEND_WRITE_CHUNK(x) \ - network_status_t network_write_chunkqueue_##x(server *srv, connection *con, iosocket *sock, chunkqueue *cq, chunk *c) - -#define NETWORK_BACKEND_WRITE(x) \ - network_status_t network_write_chunkqueue_##x(server *srv, connection *con, iosocket *sock, chunkqueue *cq) -#define NETWORK_BACKEND_READ(x) \ - network_status_t network_read_chunkqueue_##x(server *srv, connection *con, iosocket *sock, chunkqueue *cq) - -LI_API NETWORK_BACKEND_WRITE_CHUNK(writev_mem); - -LI_API NETWORK_BACKEND_WRITE(write); -LI_API NETWORK_BACKEND_WRITE(writev); -LI_API NETWORK_BACKEND_WRITE(linuxsendfile); -LI_API NETWORK_BACKEND_WRITE(linuxaiosendfile); -LI_API NETWORK_BACKEND_WRITE(posixaio); -LI_API NETWORK_BACKEND_WRITE(gthreadaio); -LI_API NETWORK_BACKEND_WRITE(gthreadsendfile); -LI_API NETWORK_BACKEND_WRITE(gthreadfreebsdsendfile); -LI_API NETWORK_BACKEND_WRITE(freebsdsendfile); -LI_API NETWORK_BACKEND_WRITE(solarissendfilev); - -LI_API NETWORK_BACKEND_WRITE(win32transmitfile); -LI_API NETWORK_BACKEND_WRITE(win32send); - -LI_API NETWORK_BACKEND_READ(read); -LI_API NETWORK_BACKEND_READ(win32recv); - -#ifdef USE_OPENSSL -LI_API NETWORK_BACKEND_WRITE(openssl); -LI_API NETWORK_BACKEND_READ(openssl); -#endif - -typedef struct { - network_backend_t type; - const char *name; - const char *description; - network_status_t (*read_handler)(server *srv, connection *con, iosocket *sock, chunkqueue *cq); - network_status_t (*write_handler)(server *srv, connection *con, iosocket *sock, chunkqueue *cq); -} network_backend_info_t; - -LI_API const network_backend_info_t *network_get_backends(); -LI_API const network_backend_info_t *network_get_defaultbackend(); -LI_API const network_backend_info_t *network_get_backend_info_by_type(network_backend_t type); -LI_API const network_backend_info_t *network_get_backend_info_by_name(const char *name); - -#endif diff --git a/src/network_freebsd_sendfile.c b/src/network_freebsd_sendfile.c deleted file mode 100644 index 446e1fe7..00000000 --- a/src/network_freebsd_sendfile.c +++ /dev/null @@ -1,157 +0,0 @@ -#include "network_backends.h" - -#ifdef USE_FREEBSD_SENDFILE - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/resource.h> - -#include <netinet/in.h> -#include <netinet/tcp.h> - -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> -#include <netdb.h> -#include <string.h> -#include <stdlib.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" - - -#ifndef UIO_MAXIOV -# if defined(__FreeBSD__) || defined(__DragonFly__) -/* FreeBSD 4.7, 4.9 defined it in sys/uio.h only if _KERNEL is specified */ -# define UIO_MAXIOV 1024 -# endif -#endif - -NETWORK_BACKEND_WRITE(freebsdsendfile) { - chunk *c; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next, chunks_written++) { - chunk *tc; - int chunk_finished = 0; - network_status_t ret; - - switch(c->type) { - case MEM_CHUNK: - ret = network_write_chunkqueue_writev_mem(srv, con, sock, cq, c); - - /* check which chunks are finished now */ - for (tc = c; tc; tc = tc->next) { - /* finished the chunk */ - if (tc->offset == tc->mem->used - 1) { - /* skip the first c->next as that will be done by the c = c->next in the other for()-loop */ - if (chunk_finished) { - c = c->next; - } else { - chunk_finished = 1; - } - } else { - break; - } - } - - if (ret != NETWORK_STATUS_SUCCESS) { - return ret; - } - - break; - case FILE_CHUNK: { - off_t offset, r; - size_t toSend; - stat_cache_entry *sce = NULL; - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { - log_error_write(srv, __FILE__, __LINE__, "sb", - strerror(errno), c->file.name); - return NETWORK_STATUS_FATAL_ERROR; - } - - offset = c->file.start + c->offset; - /* limit the toSend to 2^31-1 bytes in a chunk */ - toSend = c->file.length - c->offset > ((1 << 30) - 1) ? - ((1 << 30) - 1) : c->file.length - c->offset; - - if (-1 == c->file.fd) { - if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) { - switch (errno) { - case EMFILE: - return NETWORK_STATUS_WAIT_FOR_FD; - default: - ERROR("opening '%s' failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } - - return NETWORK_STATUS_FATAL_ERROR; - } - } - - r = 0; - - /* FreeBSD sendfile() */ - if (-1 == sendfile(c->file.fd, sock->fd, offset, toSend, NULL, &r, 0)) { - switch(errno) { - case EAGAIN: - break; - case ENOTCONN: - return NETWORK_STATUS_CONNECTION_CLOSE; - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", "sendfile: ", strerror(errno), errno); - return NETWORK_STATUS_FATAL_ERROR; - } - } - - if (r == 0) { - /* We got an event to write but we wrote nothing - * - * - the file shrinked -> error - * - the remote side closed inbetween -> remote-close */ - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { - /* file is gone ? */ - return NETWORK_STATUS_FATAL_ERROR; - } - - if (offset > sce->st.st_size) { - /* file shrinked, close the connection */ - return NETWORK_STATUS_FATAL_ERROR; - } - - return NETWORK_STATUS_CONNECTION_CLOSE; - } - - c->offset += r; - cq->bytes_out += r; - - if (c->offset == c->file.length) { - chunk_finished = 1; - } - - break; - } - default: - ERROR("chunk-type '%d' not known", c->type); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (!chunk_finished) { - /* not finished yet */ - - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - } - - return NETWORK_STATUS_SUCCESS; -} - -#endif diff --git a/src/network_gthread_aio.c b/src/network_gthread_aio.c deleted file mode 100644 index 42a2f3bd..00000000 --- a/src/network_gthread_aio.c +++ /dev/null @@ -1,481 +0,0 @@ -/** - * - MAP_ANON needs _BSD_SOURCE | _SVID_SOURCE - * - pread() needs _XOPEN_SOURCE 500 on Linux - * - _XOPEN_SOURCE breaks the compile on FreeBSD (see #1240, #1251) - * - */ -#ifndef _GNU_SOURCE -# ifndef _BSD_SOURCE -# define _BSD_SOURCE 1 -# endif -#endif - -#ifdef linux -/* For pread()/pwrite() */ -#define _XOPEN_SOURCE 500 -#endif - -#include "settings.h" -#include "network_backends.h" -#ifdef USE_GTHREAD_AIO -#include <sys/types.h> -#include <sys/stat.h> -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif - -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> -#include <assert.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" -#include "joblist.h" -#include "timing.h" - -#include "sys-files.h" -#include "sys-socket.h" - -typedef struct { - chunk *c; - - void *con; - - int sock_fd; -} write_job; - -static write_job *write_job_init() { - write_job *wj = calloc(1, sizeof(*wj)); - - return wj; -} - -static void write_job_free(write_job *wj) { - if (!wj) return; - - free(wj); -} - -#define kByte * (1024) -#define MByte * (1024 kByte) - -/** - * log the time-stamps of the different stages - */ -static void timing_print(server *srv, connection *con) { - if (!srv->srvconf.log_timing) return; - - TRACE("write-start: %ld.%06ld " - "read-queue-wait: %ld ms " - "read-time: %ld ms " - "write-time: %ld ms ", - con->timestamps[TIME_SEND_WRITE_START].tv_sec, - con->timestamps[TIME_SEND_WRITE_START].tv_usec, - - TIME_DIFF(TIME_SEND_ASYNC_READ_START, TIME_SEND_ASYNC_READ_QUEUED), - TIME_DIFF(TIME_SEND_ASYNC_READ_END, TIME_SEND_ASYNC_READ_START), - TIME_DIFF(TIME_SEND_WRITE_END, TIME_SEND_ASYNC_READ_END_QUEUED) - ); -} - -gpointer network_gthread_aio_read_thread(gpointer _srv) { - server *srv = (server *)_srv; - - GAsyncQueue * inq; - - int fadvise_is_enosys = 0; - - g_async_queue_ref(srv->aio_write_queue); - - inq = srv->aio_write_queue; - - /* */ - while (!srv->is_shutdown) { - write_job *wj = NULL; - - if ((wj = g_async_queue_pop(inq))) { - /* let's see what we have to stat */ - ssize_t r; - off_t offset; - size_t toSend; - chunk *c; - connection *con; - off_t max_toSend = 64 kByte; /** should be larger than the send buffer */ - - int fadvise_fd = 0; - off_t fadvise_offset = 0; - off_t fadvise_len = 0; - - if(wj == (write_job *) 1) - continue; /* just notifying us that srv->is_shutdown changed */ - - c = wj->c; - con = wj->con; - -#if 0 - /* try to be adaptive */ - int snd_buf_size = 0; - socklen_t snd_buf_size_size = sizeof(snd_buf_size); - - if (0 == getsockopt(con->sock->fd, SOL_SOCKET, SO_SNDBUF, &snd_buf_size, &snd_buf_size_size)) { - /* adjust the read-data to the send-buffer */ - if (snd_buf_size * 4 > max_toSend) { - max_toSend = snd_buf_size * 4; - } - } -#endif - - offset = c->file.start + c->offset; - - toSend = c->file.length - c->offset > max_toSend ? - max_toSend : c->file.length - c->offset; - - c->file.copy.offset = 0; - c->file.copy.length = 0; - - /* open a file in /dev/shm to write to */ - if (c->file.mmap.start == MAP_FAILED) { -#if defined(HAVE_MEM_MMAP_ZERO) - int mmap_fd; - if (-1 == (mmap_fd = open("/dev/zero", O_RDWR))) { - if (errno != EMFILE) { - TRACE("open(/dev/zero) returned: %d (%s)", - errno, strerror(errno)); - c->async.ret_val = NETWORK_STATUS_FATAL_ERROR; - } else { - c->async.ret_val = NETWORK_STATUS_WAIT_FOR_FD; - } - } else { - c->file.mmap.offset = 0; - c->file.mmap.length = toSend; - - c->file.mmap.start = mmap(0, c->file.mmap.length, - PROT_READ | PROT_WRITE, MAP_SHARED, mmap_fd, 0); - if (c->file.mmap.start == MAP_FAILED) { - TRACE("mmap(/dev/zero) returned: %d (%s)", - errno, strerror(errno)); - c->async.ret_val = NETWORK_STATUS_FATAL_ERROR; - } - - close(mmap_fd); - mmap_fd = -1; - } -#elif defined(HAVE_MEM_MMAP_ANON) - c->file.mmap.offset = 0; - c->file.mmap.length = toSend; /* align to page-size */ - c->file.mmap.start = mmap(0, c->file.mmap.length, - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); - - if (c->file.mmap.start == MAP_FAILED) { - TRACE("mmap(MAP_ANON) returned: %d (%s)", - errno, strerror(errno)); - c->async.ret_val = NETWORK_STATUS_FATAL_ERROR; - } -#else -#error hmm, does your system support mmap(/dev/zero) or mmap(MAP_ANON) -#endif - } - if (c->file.mmap.start != MAP_FAILED) { - timing_log(srv, con, TIME_SEND_ASYNC_READ_START); - - if (-1 == (r = pread(c->file.fd, c->file.mmap.start, toSend, c->file.start + c->offset))) { - switch(errno) { - default: - ERROR("reading file failed: %d (%s)", errno, strerror(errno)); - - c->async.ret_val = NETWORK_STATUS_FATAL_ERROR; - } - } else if (r == 0) { - ERROR("pread(%s) returned 0 ... not good", SAFE_BUF_STR(c->file.name)); - - c->async.ret_val = NETWORK_STATUS_FATAL_ERROR; - } else { - timing_log(srv, con, TIME_SEND_ASYNC_READ_END); - c->file.copy.length = r; - } - } - - if (c->file.copy.length && !fadvise_is_enosys) { - fadvise_fd = c->file.fd; - fadvise_offset = c->file.start + c->offset + c->file.copy.length; - fadvise_len = c->file.length - c->offset - c->file.copy.length; - - if (fadvise_len > max_toSend) { - fadvise_len = max_toSend; - } - } - timing_log(srv, con, TIME_SEND_ASYNC_READ_END_QUEUED); - /* read async, write as usual */ - joblist_async_append(srv, wj->con); - -#if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_WILLNEED) - /* read ahead */ - if (c->file.copy.length && !fadvise_is_enosys && fadvise_len) { - /* let's hope that the fd is still valid when we try to read ahead */ - if (-1 == posix_fadvise(fadvise_fd, fadvise_offset, fadvise_len, POSIX_FADV_WILLNEED)) { - if (ENOSYS != errno) { - ERROR("posix_fadvise(%d) failed: %s (%d)", fadvise_fd, strerror(errno), errno); - } else { - /* don't try again as we don't support it */ - fadvise_is_enosys = 1; - } - } - } -#endif - write_job_free(wj); - } - } - - g_async_queue_unref(srv->aio_write_queue); - - return NULL; - -} - - -NETWORK_BACKEND_WRITE(gthreadaio) { - chunk *c, *tc; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next, chunks_written++) { - int chunk_finished = 0; - network_status_t ret; - - switch(c->type) { - case MEM_CHUNK: - ret = network_write_chunkqueue_writev_mem(srv, con, sock, cq, c); - - /* check which chunks are finished now */ - for (tc = c; tc && chunk_is_done(tc); tc = tc->next) { - /* skip the first c->next as that will be done by the c = c->next in the other for()-loop */ - if (chunk_finished) { - c = c->next; - } else { - chunk_finished = 1; - } - } - - if (ret != NETWORK_STATUS_SUCCESS) { - return ret; - } - - break; - case FILE_CHUNK: { - ssize_t r; - - /* we might be on our way back from the async request and have a status-code */ - if (c->async.ret_val != NETWORK_STATUS_UNSET) { - ret = c->async.ret_val; - - c->async.ret_val = NETWORK_STATUS_UNSET; - - ERROR("thread returned: %d", ret); - - return ret; - } - - /* open file if not already opened */ - if (-1 == c->file.fd) { - if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY /* | O_DIRECT */ | (srv->srvconf.use_noatime ? O_NOATIME : 0)))) { - ERROR("opening '%s' failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } -#ifdef FD_CLOEXEC - fcntl(c->file.fd, F_SETFD, FD_CLOEXEC); -#endif -#if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_SEQUENTIAL) - /* tell the kernel that we want to stream the file - * - * - POSIX_FADV_SEQUENTIAL doubles the read-ahead on Linux - * - * */ - if (-1 == posix_fadvise(c->file.fd, c->file.start, c->file.length, POSIX_FADV_SEQUENTIAL)) { - if (ENOSYS != errno) { - ERROR("posix_fadvise(%s) failed: %s (%d)", c->file.name->ptr, strerror(errno), errno); - } - } -#endif - } - /* check if we have content */ - if (c->file.copy.length == 0) { - const off_t max_toSend = 64 kByte; /** should be larger than the send buffer */ - size_t toSend; - off_t offset; - - /* start to write a block out the to net work */ - timing_log(srv, con, TIME_SEND_WRITE_START); - - offset = c->file.start + c->offset; - - toSend = c->file.length - c->offset > max_toSend ? - max_toSend : c->file.length - c->offset; - - /* we small files don't take the overhead of a full async-loop */ - if (toSend < 4 * 1024) { - - c->file.copy.offset = 0; - c->file.copy.length = toSend; - - /* open a file in /dev/shm to write to */ - if (c->file.mmap.start == MAP_FAILED) { -#if defined(HAVE_MEM_MMAP_ZERO) - int mmap_fd; - - if (-1 == (mmap_fd = open("/dev/zero", O_RDWR))) { - if (errno != EMFILE) { - TRACE("open(/dev/zero) returned: %d (%s), open fds: %d", - errno, strerror(errno)); - return NETWORK_STATUS_FATAL_ERROR; - } else { - return NETWORK_STATUS_WAIT_FOR_FD; - } - } else { - c->file.mmap.offset = 0; - c->file.mmap.length = c->file.copy.length; /* align to page-size */ - - c->file.mmap.start = mmap(0, c->file.mmap.length, - PROT_READ | PROT_WRITE, MAP_SHARED, mmap_fd, 0); - if (c->file.mmap.start == MAP_FAILED) { - ERROR("mmap(%s) failed: %s (%d)", c->file.name->ptr, strerror(errno), errno); - - return NETWORK_STATUS_FATAL_ERROR; - } - - close(mmap_fd); - mmap_fd = -1; - } -#elif defined(HAVE_MEM_MMAP_ANON) - c->file.mmap.offset = 0; - c->file.mmap.length = c->file.copy.length; /* align to page-size */ - c->file.mmap.start = mmap(0, c->file.mmap.length, - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); - - if (c->file.mmap.start == MAP_FAILED) { - TRACE("mmap(MAP_ANON) returned: %d (%s)", - errno, strerror(errno)); - return NETWORK_STATUS_FATAL_ERROR; - } -#else -#error hmm, does your system support mmap(/dev/zero) or mmap(MAP_ANON) -#endif - - } - - if (c->file.mmap.start != MAP_FAILED) { - lseek(c->file.fd, c->file.start + c->offset, SEEK_SET); - - if (-1 == (r = read(c->file.fd, c->file.mmap.start, c->file.copy.length))) { - switch(errno) { - default: - ERROR("reading file failed: %d (%s)", errno, strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } - } else if (r == 0) { - ERROR("read() returned 0 ... not good: %s", ""); - - return NETWORK_STATUS_FATAL_ERROR; - } else if (r != c->file.copy.length) { - ERROR("read() returned %zd instead of %jd", r, (intmax_t) c->file.copy.length); - - return NETWORK_STATUS_FATAL_ERROR; - } - } else { - return NETWORK_STATUS_FATAL_ERROR; - } - } else { - write_job *wj; - - wj = write_job_init(); - wj->c = c; - wj->con = con; - wj->sock_fd = sock->fd; - - c->async.written = -1; - c->async.ret_val = NETWORK_STATUS_UNSET; - - g_async_queue_push(srv->aio_write_queue, wj); - - timing_log(srv, con, TIME_SEND_ASYNC_READ_QUEUED); - - return NETWORK_STATUS_WAIT_FOR_AIO_EVENT; - } - } - - if (-1 == (r = write(sock->fd, c->file.mmap.start + c->file.copy.offset, c->file.copy.length - c->file.copy.offset))) { - switch (errno) { - case EINTR: - case EAGAIN: - return NETWORK_STATUS_WAIT_FOR_EVENT; - case EPIPE: - case ECONNRESET: - return NETWORK_STATUS_CONNECTION_CLOSE; - default: - ERROR("write failed: %d (%s) [%jd, %p, %jd]", - errno, strerror(errno), (intmax_t) c->file.copy.length, - c->file.mmap.start, (intmax_t) c->file.copy.offset); - return NETWORK_STATUS_FATAL_ERROR; - } - } - - if (r == 0) { - return NETWORK_STATUS_CONNECTION_CLOSE; - } - - c->file.copy.offset += r; /* offset in the copy-chunk */ - - c->offset += r; /* global offset in the file */ - cq->bytes_out += r; - - if (c->file.copy.offset == (off_t) c->file.mmap.length) { - /* this block is sent, get a new one */ - timing_log(srv, con, TIME_SEND_WRITE_END); - - timing_print(srv, con); - - c->file.copy.length = 0; - } - - if (c->offset == c->file.length) { - chunk_finished = 1; - - munmap(c->file.mmap.start, c->file.mmap.length); - c->file.mmap.start = MAP_FAILED; - - if (c->file.copy.fd != -1) { - close(c->file.copy.fd); - c->file.copy.fd = -1; - } - - if (c->file.fd != -1) { - close(c->file.fd); - c->file.fd = -1; - } - } - - break; - } - default: - - log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (!chunk_finished) { - /* not finished yet */ - - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - } - - return NETWORK_STATUS_SUCCESS; -} - -#endif diff --git a/src/network_gthread_freebsd_sendfile.c b/src/network_gthread_freebsd_sendfile.c deleted file mode 100644 index 639eda67..00000000 --- a/src/network_gthread_freebsd_sendfile.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * make sure _GNU_SOURCE is defined - */ -#include "settings.h" -#include "network_backends.h" -#if defined(USE_GTHREAD_FREEBSD_SENDFILE) -#include <sys/types.h> -#include <sys/stat.h> -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif - -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> -#include <assert.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" -#include "joblist.h" -#include "timing.h" - -#include "sys-files.h" -#include "sys-socket.h" - -typedef struct { - chunk *c; - - void *con; - - int sock_fd; -} write_job; - -static write_job *write_job_init() { - write_job *wj = calloc(1, sizeof(*wj)); - - return wj; -} - -static void write_job_free(write_job *wj) { - if (!wj) return; - - free(wj); -} - -#define kByte * (1024) -#define MByte * (1024 kByte) - -/** - * log the time-stamps of the different stages - */ -static void timing_print(server *srv, connection *con) { - if (!srv->srvconf.log_timing) return; - - TRACE("write-start: %ld.%06ld " - "read-queue-wait: %ld ms " - "read-time: %ld ms " - "write-time: %ld ms ", - con->timestamps[TIME_SEND_WRITE_START].tv_sec, - con->timestamps[TIME_SEND_WRITE_START].tv_usec, - - TIME_DIFF(TIME_SEND_ASYNC_READ_START, TIME_SEND_ASYNC_READ_QUEUED), - TIME_DIFF(TIME_SEND_ASYNC_READ_END, TIME_SEND_ASYNC_READ_START), - TIME_DIFF(TIME_SEND_WRITE_END, TIME_SEND_ASYNC_READ_END_QUEUED) - ); -} - -/** - * a backend which calls sendfile() in a thread - */ - -gpointer network_gthread_freebsd_sendfile_read_thread(gpointer _srv) { - server *srv = (server *)_srv; - - GAsyncQueue * inq; - - g_async_queue_ref(srv->aio_write_queue); - - inq = srv->aio_write_queue; - - /* */ - while (!srv->is_shutdown) { - write_job *wj = NULL; - - if ((wj = g_async_queue_pop(inq))) { - /* let's see what we have to stat */ - off_t r; - off_t offset; - size_t toSend; - chunk *c; - connection *con; - off_t max_toSend = 512 kByte; /** should be larger than the send buffer */ - - if(wj == (write_job *) 1) - continue; /* just notifying us that srv->is_shutdown changed */ - - c = wj->c; - con = wj->con; - offset = c->file.start + c->offset; - - toSend = c->file.length - c->offset > max_toSend ? - max_toSend : c->file.length - c->offset; - - timing_log(srv, con, TIME_SEND_ASYNC_READ_START); - - r = 0; - if (-1 == sendfile(c->file.fd, wj->sock_fd, offset, toSend, NULL, &r, 0)) { - switch (errno) { - case EAGAIN: - case EINTR: - c->async.ret_val = NETWORK_STATUS_WAIT_FOR_EVENT; - break; - case ENOTCONN: - c->async.ret_val = NETWORK_STATUS_CONNECTION_CLOSE; - break; - default: - ERROR("sendfile(%s) failed: %s (%d)", - SAFE_BUF_STR(c->file.name), - strerror(errno), errno); - c->async.ret_val = NETWORK_STATUS_FATAL_ERROR; - break; - } - } else if (r == 0) { - c->async.ret_val = NETWORK_STATUS_CONNECTION_CLOSE; - } else { - c->async.written = r; - } - - timing_log(srv, con, TIME_SEND_ASYNC_READ_END); - timing_log(srv, con, TIME_SEND_ASYNC_READ_END_QUEUED); - - /* read async, write as usual */ - joblist_async_append(srv, wj->con); - - write_job_free(wj); - } - } - - g_async_queue_unref(srv->aio_write_queue); - - return NULL; - -} - - -NETWORK_BACKEND_WRITE(gthreadfreebsdsendfile) { - chunk *c, *tc; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next, chunks_written++) { - int chunk_finished = 0; - network_status_t ret; - - switch(c->type) { - case MEM_CHUNK: - ret = network_write_chunkqueue_writev_mem(srv, con, sock, cq, c); - - /* check which chunks are finished now */ - for (tc = c; tc && chunk_is_done(tc); tc = tc->next) { - /* skip the first c->next as that will be done by the c = c->next in the other for()-loop */ - if (chunk_finished) { - c = c->next; - } else { - chunk_finished = 1; - } - } - - if (ret != NETWORK_STATUS_SUCCESS) { - return ret; - } - - break; - case FILE_CHUNK: { - /* we might be on our way back from the async request and have a status-code */ - if (c->async.ret_val != NETWORK_STATUS_UNSET) { - ret = c->async.ret_val; - - c->async.ret_val = NETWORK_STATUS_UNSET; - - return ret; - } - - /* open file if not already opened */ - if (-1 == c->file.fd) { - if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY /* | O_DIRECT */ | (srv->srvconf.use_noatime ? O_NOATIME : 0)))) { - ERROR("opening '%s' failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } -#ifdef FD_CLOEXEC - fcntl(c->file.fd, F_SETFD, FD_CLOEXEC); -#endif -#if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_SEQUENTIAL) - /* tell the kernel that we want to stream the file */ - if (-1 == posix_fadvise(c->file.fd, c->file.start, c->file.length, POSIX_FADV_SEQUENTIAL)) { - if (ENOSYS != errno) { - ERROR("posix_fadvise(%s) failed: %s (%d)", c->file.name->ptr, strerror(errno), errno); - } - } -#endif - } - - if (c->async.written > 0) { - /* the backend has written something */ - - c->offset += c->async.written; /* global offset in the file */ - cq->bytes_out += c->async.written; - - /* this block is sent, get a new one */ - timing_log(srv, con, TIME_SEND_WRITE_END); - - timing_print(srv, con); - - c->async.written = -1; - } - - if (c->offset == c->file.length) { - chunk_finished = 1; - - if (c->file.fd != -1) { - close(c->file.fd); - c->file.fd = -1; - } - } else { - /* start this write */ - write_job *wj; - - timing_log(srv, con, TIME_SEND_WRITE_START); - wj = write_job_init(); - wj->c = c; - wj->con = con; - wj->sock_fd = sock->fd; - - c->async.written = -1; - c->async.ret_val = NETWORK_STATUS_UNSET; - - g_async_queue_push(srv->aio_write_queue, wj); - - timing_log(srv, con, TIME_SEND_ASYNC_READ_QUEUED); - - return NETWORK_STATUS_WAIT_FOR_AIO_EVENT; - } - - break; - } - case UNUSED_CHUNK: - continue; - } - - if (!chunk_finished) { - /* not finished yet */ - - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - } - - return NETWORK_STATUS_SUCCESS; -} - -#endif - diff --git a/src/network_gthread_sendfile.c b/src/network_gthread_sendfile.c deleted file mode 100644 index b39a082d..00000000 --- a/src/network_gthread_sendfile.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * make sure _GNU_SOURCE is defined - */ -#include "settings.h" -#include "network_backends.h" -#if defined(USE_GTHREAD_SENDFILE) -#include <sys/types.h> -#include <sys/stat.h> -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#endif - -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> -#include <assert.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" -#include "joblist.h" -#include "timing.h" - -#include "sys-files.h" -#include "sys-socket.h" - -typedef struct { - chunk *c; - - void *con; - - int sock_fd; -} write_job; - -static write_job *write_job_init() { - write_job *wj = calloc(1, sizeof(*wj)); - - return wj; -} - -static void write_job_free(write_job *wj) { - if (!wj) return; - - free(wj); -} - -#define kByte * (1024) -#define MByte * (1024 kByte) - -/** - * log the time-stamps of the different stages - */ -static void timing_print(server *srv, connection *con) { - if (!srv->srvconf.log_timing) return; - - TRACE("write-start: %ld.%06ld " - "read-queue-wait: %ld ms " - "read-time: %ld ms " - "write-time: %ld ms ", - con->timestamps[TIME_SEND_WRITE_START].tv_sec, - con->timestamps[TIME_SEND_WRITE_START].tv_usec, - - TIME_DIFF(TIME_SEND_ASYNC_READ_START, TIME_SEND_ASYNC_READ_QUEUED), - TIME_DIFF(TIME_SEND_ASYNC_READ_END, TIME_SEND_ASYNC_READ_START), - TIME_DIFF(TIME_SEND_WRITE_END, TIME_SEND_ASYNC_READ_END_QUEUED) - ); -} - -/** - * a backend which calls sendfile() in a thread - */ - -gpointer network_gthread_sendfile_read_thread(gpointer _srv) { - server *srv = (server *)_srv; - - GAsyncQueue * inq; - - g_async_queue_ref(srv->aio_write_queue); - - inq = srv->aio_write_queue; - - /* */ - while (!srv->is_shutdown) { - write_job *wj = NULL; - - if ((wj = g_async_queue_pop(inq))) { - /* let's see what we have to stat */ - ssize_t r; - off_t offset; - size_t toSend; - chunk *c; - connection *con; - off_t max_toSend = 512 kByte; /** should be larger than the send buffer */ - - if(wj == (write_job *) 1) - continue; /* just notifying us that srv->is_shutdown changed */ - - c = wj->c; - con = wj->con; - offset = c->file.start + c->offset; - - toSend = c->file.length - c->offset > max_toSend ? - max_toSend : c->file.length - c->offset; - - timing_log(srv, con, TIME_SEND_ASYNC_READ_START); - - if (-1 == (r = sendfile(wj->sock_fd, c->file.fd, &offset, toSend))) { - switch (errno) { - case EAGAIN: - case EINTR: - c->async.ret_val = NETWORK_STATUS_WAIT_FOR_EVENT; - break; - case EPIPE: - case ECONNRESET: - c->async.ret_val = NETWORK_STATUS_CONNECTION_CLOSE; - break; - case ENOSYS: - ERROR("sendfile(%s) is not implemented, use server.network-backend = \"writev\"", - SAFE_BUF_STR(c->file.name)); - c->async.ret_val = NETWORK_STATUS_FATAL_ERROR; - break; - default: - ERROR("sendfile(%s) failed: %s (%d)", - SAFE_BUF_STR(c->file.name), - strerror(errno), errno); - c->async.ret_val = NETWORK_STATUS_FATAL_ERROR; - break; - } - } else if (r == 0) { - c->async.ret_val = NETWORK_STATUS_CONNECTION_CLOSE; - } else { - c->async.written = r; - } - - timing_log(srv, con, TIME_SEND_ASYNC_READ_END); - timing_log(srv, con, TIME_SEND_ASYNC_READ_END_QUEUED); - - /* read async, write as usual */ - joblist_async_append(srv, wj->con); - - write_job_free(wj); - } - } - - g_async_queue_unref(srv->aio_write_queue); - - return NULL; - -} - - -NETWORK_BACKEND_WRITE(gthreadsendfile) { - chunk *c, *tc; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next, chunks_written++) { - int chunk_finished = 0; - network_status_t ret; - - switch(c->type) { - case MEM_CHUNK: - ret = network_write_chunkqueue_writev_mem(srv, con, sock, cq, c); - - /* check which chunks are finished now */ - for (tc = c; tc && chunk_is_done(tc); tc = tc->next) { - /* skip the first c->next as that will be done by the c = c->next in the other for()-loop */ - if (chunk_finished) { - c = c->next; - } else { - chunk_finished = 1; - } - } - - if (ret != NETWORK_STATUS_SUCCESS) { - return ret; - } - - break; - case FILE_CHUNK: { - /* we might be on our way back from the async request and have a status-code */ - if (c->async.ret_val != NETWORK_STATUS_UNSET) { - ret = c->async.ret_val; - - c->async.ret_val = NETWORK_STATUS_UNSET; - - return ret; - } - - /* open file if not already opened */ - if (-1 == c->file.fd) { - if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY /* | O_DIRECT */ | (srv->srvconf.use_noatime ? O_NOATIME : 0)))) { - ERROR("opening '%s' failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } -#ifdef FD_CLOEXEC - fcntl(c->file.fd, F_SETFD, FD_CLOEXEC); -#endif -#if defined(HAVE_POSIX_FADVISE) && defined(POSIX_FADV_SEQUENTIAL) - /* tell the kernel that we want to stream the file */ - if (-1 == posix_fadvise(c->file.fd, c->file.start, c->file.length, POSIX_FADV_SEQUENTIAL)) { - if (ENOSYS != errno) { - ERROR("posix_fadvise(%s) failed: %s (%d)", c->file.name->ptr, strerror(errno), errno); - } - } -#endif - } - - if (c->async.written > 0) { - /* the backend has written something */ - - c->offset += c->async.written; /* global offset in the file */ - cq->bytes_out += c->async.written; - - /* this block is sent, get a new one */ - timing_log(srv, con, TIME_SEND_WRITE_END); - - timing_print(srv, con); - - c->async.written = -1; - } - - if (c->offset == c->file.length) { - chunk_finished = 1; - - if (c->file.fd != -1) { - close(c->file.fd); - c->file.fd = -1; - } - } else { - /* start this write */ - write_job *wj; - - timing_log(srv, con, TIME_SEND_WRITE_START); - wj = write_job_init(); - wj->c = c; - wj->con = con; - wj->sock_fd = sock->fd; - - c->async.written = -1; - c->async.ret_val = NETWORK_STATUS_UNSET; - - g_async_queue_push(srv->aio_write_queue, wj); - - timing_log(srv, con, TIME_SEND_ASYNC_READ_QUEUED); - - return NETWORK_STATUS_WAIT_FOR_AIO_EVENT; - } - - break; - } - default: - - log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (!chunk_finished) { - /* not finished yet */ - - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - } - - return NETWORK_STATUS_SUCCESS; -} - -#endif diff --git a/src/network_linux_aio.c b/src/network_linux_aio.c deleted file mode 100644 index 23f64160..00000000 --- a/src/network_linux_aio.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * make sure _GNU_SOURCE is defined - */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE /* we need O_DIRECT */ -#endif - -#include "network_backends.h" - -#ifdef USE_LINUX_AIO_SENDFILE -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/resource.h> - -#include <netinet/in.h> -#include <netinet/tcp.h> - -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> -#include <netdb.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> -#include <assert.h> - -#include <libaio.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" -#include "joblist.h" -#include "status_counter.h" - -#include "sys-files.h" - -/* the completion handler */ -gpointer linux_aio_read_thread(gpointer _srv) { - server *srv = (server *)_srv; - - /* */ - while (!srv->is_shutdown) { - /* let's see what we have to stat */ - struct io_event event[16]; - struct timespec io_ts; - int res; - - io_ts.tv_sec = 1; - io_ts.tv_nsec = 0; - - if ((res = io_getevents(srv->linux_io_ctx, 1, 16, event, &io_ts)) > 0) { - int i; - for (i = 0; i < res; i++) { - connection *con = event[i].data; - - if ((long)event[i].res <= 0) { - TRACE("async-read failed with %d (%s), was asked for %s (fd = %d)", - (int) event[i].res, strerror(-event[i].res), SAFE_BUF_STR(con->uri.path), con->sock->fd); - } - - /* free the iocb */ - event[i].obj->data = NULL; - - joblist_async_append(srv, con); - } - } else if (res < 0) { - TRACE("getevents - failed: %d: %s", res, strerror(-res)); - } - } - - return NULL; -} - - -NETWORK_BACKEND_WRITE(linuxaiosendfile) { - chunk *c, *tc; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next, chunks_written++) { - int chunk_finished = 0; - network_status_t ret; - - switch(c->type) { - case MEM_CHUNK: - ret = network_write_chunkqueue_writev_mem(srv, con, sock, cq, c); - - /* check which chunks are finished now */ - for (tc = c; tc; tc = tc->next) { - /* finished the chunk */ - if (tc->offset == (off_t) tc->mem->used - 1) { - /* skip the first c->next as that will be done by the c = c->next in the other for()-loop */ - if (chunk_finished) { - c = c->next; - } else { - chunk_finished = 1; - } - } else { - break; - } - } - - if (ret != NETWORK_STATUS_SUCCESS) { - return ret; - } - - break; - case FILE_CHUNK: { - ssize_t r; - int rounds = 8; - - /* open file if not already opened */ - if (-1 == c->file.fd) { - mode_t mode = O_RDONLY; - /* - * Note: can't use O_DIRECT on files in "/dev/shm/". I hope all tempfiles - * will be in shared memory filesystem. Also use O_NOATIME on tempfiles - * since they will be deleted anyways. - */ - if(c->file.is_temp) { - mode |= O_NOATIME; - } else { - mode |= (O_DIRECT | (srv->srvconf.use_noatime ? O_NOATIME : 0)); - } - if (-1 == (c->file.fd = open(c->file.name->ptr, mode))) { - if (errno == EMFILE) return NETWORK_STATUS_WAIT_FOR_FD; - - ERROR("opening '%s' failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - - - return NETWORK_STATUS_FATAL_ERROR; - } - -#ifdef FD_CLOEXEC - fcntl(c->file.fd, F_SETFD, FD_CLOEXEC); -#endif - } - - do { - size_t toSend; - const off_t max_toSend = 4 * 256 * 1024; /** should be larger than the send buffer */ - int file_fd; - off_t offset; - - offset = c->file.start + c->offset; - - toSend = c->file.length - c->offset > max_toSend ? - max_toSend : c->file.length - c->offset; - - if (!c->file.is_temp && (-1 == c->file.copy.fd || 0 == c->file.copy.length)) { - long page_size = sysconf(_SC_PAGESIZE); - - int res; - int async_error = 0; - - size_t iocb_ndx; - struct iocb *iocb = NULL; - - c->file.copy.offset = 0; - c->file.copy.length = toSend - (toSend % page_size); /* align to page-size */ - - if (c->file.copy.length == 0) { - async_error = 1; - } - - /* if we reused the previous tmp-file we get overlaps - * - * 1 ... 3904 are ok - * 3905 ... 4096 are replaces by 8001 ... 8192 - * - * somehow the second read writes into the mmap() before - * the sendfile is finished which is very strange. - * - * if someone finds the reason for this, feel free to remove - * this if again and number of reduce the syscalls a bit. - */ - if (-1 != c->file.copy.fd) { - munmap(c->file.mmap.start, c->file.mmap.length); - - close(c->file.copy.fd); - c->file.copy.fd = -1; - } - - /* do we have a IOCB we can use ? */ - - for (iocb_ndx = 0; async_error == 0 && iocb_ndx < srv->srvconf.max_read_threads; iocb_ndx++) { - if (NULL == srv->linux_io_iocbs[iocb_ndx].data) { - iocb = &srv->linux_io_iocbs[iocb_ndx]; - break; - } - } - - if (iocb_ndx == srv->srvconf.max_read_threads) { - async_error = 1; - } - - - /* get mmap()ed mem-block in /dev/shm */ - if (async_error == 0 && -1 == c->file.copy.fd ) { - char tmpfile_name[sizeof("/dev/shm/l-XXXXXX")]; - - /* open a file in /dev/shm to write to */ - strcpy(tmpfile_name, "/dev/shm/l-XXXXXX"); - if (-1 == (c->file.copy.fd = mkstemp(tmpfile_name))) { - async_error = 1; - - if (errno != EMFILE) { - TRACE("mkstemp returned: %d (%s) for %s, falling back to sync-io", - errno, strerror(errno), tmpfile_name); - } - } - - c->file.mmap.offset = 0; - c->file.mmap.length = c->file.copy.length; /* align to page-size */ - - if (!async_error) { - unlink(tmpfile_name); /* remove the file again, we still keep it open */ - if (0 != ftruncate(c->file.copy.fd, c->file.mmap.length)) { - /* disk full ... */ - async_error = 1; - - TRACE("ftruncate returned: %d (%s), falling back to sync-io", - errno, strerror(errno)); - } - } - - if (!async_error) { - - c->file.mmap.start = mmap(0, c->file.mmap.length, - PROT_READ | PROT_WRITE, MAP_SHARED, c->file.copy.fd, 0); - if (c->file.mmap.start == MAP_FAILED) { - async_error = 1; - } - } - } - - /* can we be sure that offset is always aligned ? */ - if (async_error == 0 && - (((intptr_t)c->file.mmap.start) % page_size != 0 || - c->file.copy.length % page_size != 0 || - ((intptr_t)(c->file.start + c->offset)) % page_size != 0)) { - /** - * after a fallback to sendfile() the offset might be unaligned - */ - - async_error = 1; - } - - - /* looks like we couldn't get a temp-file [disk-full] */ - if (async_error == 0 && -1 != c->file.copy.fd) { - struct iocb *iocbs[] = { iocb }; - - assert(c->file.copy.length > 0); - - io_prep_pread(iocb, c->file.fd, c->file.mmap.start, c->file.copy.length, c->file.start + c->offset); - iocb->data = con; - - if (1 == (res = io_submit(srv->linux_io_ctx, 1, iocbs))) { - status_counter_inc(CONST_STR_LEN("server.io.linux-aio.async-read")); - return NETWORK_STATUS_WAIT_FOR_AIO_EVENT; - } else { - iocb->data = NULL; - - if (-res != EAGAIN) { - TRACE("io_submit returned: %d (%s), falling back to sync-io", - res, strerror(-res)); - } else { - TRACE("io_submit returned EAGAIN on (%d - %d), -> sync-io", c->file.fd, c->file.copy.fd); - } - } - } - - /* oops, looks like the IO-queue is full - * - * fall-back to blocking sendfile() - */ - - if (c->file.mmap.start != MAP_FAILED) { - munmap(c->file.mmap.start, c->file.mmap.length); - c->file.mmap.start = MAP_FAILED; - } - if (c->file.copy.fd != -1) { - close(c->file.copy.fd); - c->file.copy.fd = -1; - } - - c->file.copy.length = 0; - c->file.copy.offset = 0; - } else if (c->file.copy.offset == 0) { - /* we are finished */ - } - - if (c->file.copy.fd == -1) { - status_counter_inc(CONST_STR_LEN("server.io.linux-aio.sync-read")); - - file_fd = c->file.fd; - } else { - file_fd = c->file.copy.fd; - - offset = c->file.copy.offset; - toSend = c->file.copy.length - offset; - } - - /* send the tmp-file from /dev/shm/ */ - if (-1 == (r = sendfile(sock->fd, file_fd, &offset, toSend))) { - switch (errno) { - case EAGAIN: - case EINTR: - return NETWORK_STATUS_WAIT_FOR_EVENT; - case EPIPE: - case ECONNRESET: - return NETWORK_STATUS_CONNECTION_CLOSE; - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", - "sendfile failed:", strerror(errno), sock->fd); - return NETWORK_STATUS_FATAL_ERROR; - } - } - - if (r == 0) { - /* ... ooops */ - return NETWORK_STATUS_CONNECTION_CLOSE; - } - - c->offset += r; /* global offset in the file */ - cq->bytes_out += r; - - if (c->file.copy.fd == -1) { - /* ... */ - } else { - c->file.copy.offset += r; /* local offset in the mmap file */ - - if (c->file.copy.offset == c->file.copy.length) { - c->file.copy.length = 0; /* reset the length and let the next round fetch a new item */ - } - } - - if (c->offset == c->file.length) { - chunk_finished = 1; - - if (c->file.copy.fd != -1) { - close(c->file.copy.fd); - c->file.copy.fd = -1; - } - - if (c->file.fd != -1) { - close(c->file.fd); - c->file.fd = -1; - } - } - - /* the chunk is larger and the current snippet is finished */ - } while (c->file.copy.length == 0 && chunk_finished == 0 && rounds-- > 0); - - break; - } - default: - - log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (!chunk_finished) { - /* not finished yet */ - - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - } - - return NETWORK_STATUS_SUCCESS; -} - -#endif -#if 0 -network_linuxsendfile_init(void) { - p->write = network_linuxsendfile_write_chunkset; -} -#endif diff --git a/src/network_linux_sendfile.c b/src/network_linux_sendfile.c deleted file mode 100644 index 290f0304..00000000 --- a/src/network_linux_sendfile.c +++ /dev/null @@ -1,192 +0,0 @@ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE /* we need O_DIRECT */ -#endif - -#include "network_backends.h" - -#ifdef USE_LINUX_SENDFILE -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/resource.h> - -#include <netinet/in.h> -#include <netinet/tcp.h> - -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> -#include <netdb.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" -#include "sys-files.h" - -/* on linux 2.4.29 + debian/ubuntu we have crashes if this is enabled */ -#undef HAVE_POSIX_FADVISE - -NETWORK_BACKEND_WRITE(linuxsendfile) { - chunk *c, *tc; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next, chunks_written++) { - int chunk_finished = 0; - network_status_t ret; - - switch(c->type) { - case MEM_CHUNK: - ret = network_write_chunkqueue_writev_mem(srv, con, sock, cq, c); - - /* check which chunks are finished now */ - for (tc = c; tc && chunk_is_done(tc); tc = tc->next) { - /* skip the first c->next as that will be done by the c = c->next in the other for()-loop */ - if (chunk_finished) { - c = c->next; - } else { - chunk_finished = 1; - } - } - - if (ret != NETWORK_STATUS_SUCCESS) { - return ret; - } - - break; - case FILE_CHUNK: { - ssize_t r; - off_t offset; - size_t toSend; - stat_cache_entry *sce = NULL; - - offset = c->file.start + c->offset; - /* limit the toSend to 2^31-1 bytes in a chunk */ - toSend = c->file.length - c->offset > ((1 << 30) - 1) ? - ((1 << 30) - 1) : c->file.length - c->offset; - - /* open file if not already opened */ - if (-1 == c->file.fd) { - if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY | (srv->srvconf.use_noatime ? O_NOATIME : 0)))) { - switch (errno) { - case EMFILE: - return NETWORK_STATUS_WAIT_FOR_FD; - default: - ERROR("opening '%s' failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } - - return -1; - } -#ifdef FD_CLOEXEC - fcntl(c->file.fd, F_SETFD, FD_CLOEXEC); -#endif -#ifdef HAVE_POSIX_FADVISE - /* tell the kernel that we want to stream the file */ - if (-1 == posix_fadvise(c->file.fd, 0, 0, POSIX_FADV_SEQUENTIAL)) { - if (ENOSYS != errno) { - log_error_write(srv, __FILE__, __LINE__, "ssd", - "posix_fadvise failed:", strerror(errno), c->file.fd); - } - } -#endif - } - - if (-1 == (r = sendfile(sock->fd, c->file.fd, &offset, toSend))) { - switch (errno) { - case EAGAIN: - case EINTR: - return NETWORK_STATUS_WAIT_FOR_EVENT; - case EPIPE: - case ECONNRESET: - return NETWORK_STATUS_CONNECTION_CLOSE; - case ENOSYS: - ERROR("sendfile(%s) is not implemented, use server.network-backend = \"writev\"", - SAFE_BUF_STR(c->file.name)); - return NETWORK_STATUS_FATAL_ERROR; - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", - "sendfile failed:", strerror(errno), sock->fd); - return NETWORK_STATUS_FATAL_ERROR; - } - } - - if (r == 0) { - /* We got an event to write but we wrote nothing - * - * - the file shrinked -> error - * - the remote side closed inbetween -> remote-close */ - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { - /* file is gone ? */ - return NETWORK_STATUS_FATAL_ERROR; - } - - if (offset > sce->st.st_size) { - /* file shrinked, close the connection */ - return NETWORK_STATUS_FATAL_ERROR; - } - - return NETWORK_STATUS_CONNECTION_CLOSE; - } - -#ifdef HAVE_POSIX_FADVISE -#if 0 -#define K * 1024 -#define M * 1024 K -#define READ_AHEAD 4 M - /* check if we need a new chunk */ - if ((c->offset & ~(READ_AHEAD - 1)) != ((c->offset + r) & ~(READ_AHEAD - 1))) { - /* tell the kernel that we want to stream the file */ - if (-1 == posix_fadvise(c->file.fd, (c->offset + r) & ~(READ_AHEAD - 1), READ_AHEAD, POSIX_FADV_NOREUSE)) { - log_error_write(srv, __FILE__, __LINE__, "ssd", - "posix_fadvise failed:", strerror(errno), c->file.fd); - } - } -#endif -#endif - - c->offset += r; - cq->bytes_out += r; - - if (c->offset == c->file.length) { - chunk_finished = 1; - - /* chunk_free() / chunk_reset() will cleanup for us but it is a ok to be faster :) */ - - if (c->file.fd != -1) { - close(c->file.fd); - c->file.fd = -1; - } - } - - break; - } - default: - - log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (!chunk_finished) { - /* not finished yet */ - - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - } - - return NETWORK_STATUS_SUCCESS; -} - -#endif -#if 0 -network_linuxsendfile_init(void) { - p->write = network_linuxsendfile_write_chunkset; -} -#endif diff --git a/src/network_openssl.c b/src/network_openssl.c deleted file mode 100644 index 7ffc8c47..00000000 --- a/src/network_openssl.c +++ /dev/null @@ -1,402 +0,0 @@ -#include "network_backends.h" - -#ifdef USE_OPENSSL -#include <sys/types.h> -#include "sys-socket.h" -#include <sys/stat.h> -#include <sys/time.h> -#ifndef _WIN32 -#include <sys/resource.h> - -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <unistd.h> -#include <netdb.h> -#else -#include <sys/types.h> -#endif - -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <assert.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" - -# include <openssl/ssl.h> -# include <openssl/err.h> - -NETWORK_BACKEND_READ(openssl) { - buffer *b; - off_t len; - int read_something = 0; - off_t max_read = 256 * 1024; - off_t start_bytes_in = cq->bytes_in; - network_status_t res; - int toread, read_offset; - - UNUSED(srv); - UNUSED(con); - - /* use a chunk-size of 4k, append to last buffer if it has >= 1kb free */ -#define TOREAD 4096 - - do { - int oerrno; - - b = (cq->last) ? cq->last->mem : NULL; - - if (NULL == b || b->size - b->used < 1024) { - b = chunkqueue_get_append_buffer(cq); - buffer_prepare_copy(b, TOREAD+1); - } - - read_offset = (b->used == 0) ? 0 : b->used - 1; - toread = b->size - 1 - read_offset; - - ERR_clear_error(); - len = SSL_read(sock->ssl, b->ptr + read_offset, toread); - - /** - * man SSL_read: - * - * >0 is success - * 0 is connection close - * <0 is error - */ - if (len <= 0) { - int r, ssl_err; - - oerrno = errno; /* store the errno for SSL_ERROR_SYSCALL */ - chunkqueue_remove_empty_last_chunk(cq); - - switch ((r = SSL_get_error(sock->ssl, len))) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - return read_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT; - 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 */ - ERROR("ssl-errors: %s", ERR_error_string(ssl_err, NULL)); - } - - if (len == 0) { - return NETWORK_STATUS_CONNECTION_CLOSE; - } else { - switch(oerrno) { - case EPIPE: - case ECONNRESET: - return NETWORK_STATUS_CONNECTION_CLOSE; - default: - ERROR("last-errno: (%d) %s", oerrno, strerror(oerrno)); - break; - } - } - - return NETWORK_STATUS_FATAL_ERROR; - case SSL_ERROR_ZERO_RETURN: - if (len == 0) { - /* clean shutdown on the remote side */ - return NETWORK_STATUS_CONNECTION_CLOSE; - } - /* fall through otherwise */ - default: - res = NETWORK_STATUS_CONNECTION_CLOSE; - while((ssl_err = ERR_get_error())) { - switch (ERR_GET_REASON(ssl_err)) { - case SSL_R_SSL_HANDSHAKE_FAILURE: - case SSL_R_TLSV1_ALERT_UNKNOWN_CA: - case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN: - case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE: - if (!con->conf.log_ssl_noise) continue; - break; - default: - res = NETWORK_STATUS_FATAL_ERROR; - break; - } - /* get all errors from the error-queue */ - ERROR("ssl-errors: %s", ERR_error_string(ssl_err, NULL)); - } - - return res; - } - } else { - if (b->used > 0) b->used--; - b->used += len; - b->ptr[b->used++] = '\0'; - - read_something = 1; - cq->bytes_in += len; - } - - if (cq->bytes_in - start_bytes_in > max_read) break; - } while (len == toread); - - return NETWORK_STATUS_SUCCESS; -} - - -NETWORK_BACKEND_WRITE(openssl) { - int ssl_r; - chunk *c; - - /* this is a 64k sendbuffer - * - * 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 - * - * the buffer is allocated once, is NOT realloced and is NOT freed at shutdown - * -> we expect a 64k block to 'leak' in valgrind - * - * - * In reality we would like to use mmap() but we don't have a guarantee that - * we get the same mmap() address for each call. On openbsd the mmap() address - * even randomized. - * That means either we keep the mmap() open or we do a read() into a - * constant buffer - * */ -#define LOCAL_SEND_BUFSIZE (64 * 1024) - static char *local_send_buffer = NULL; - - /* the remote side closed the connection before without shutdown request - * - IE - * - wget - * if keep-alive is disabled */ - - if (con->keep_alive == 0) { - SSL_set_shutdown(sock->ssl, SSL_RECEIVED_SHUTDOWN); - } - - for(c = cq->first; c; c = c->next) { - int chunk_finished = 0; - - switch(c->type) { - case MEM_CHUNK: { - char * offset; - size_t toSend; - ssize_t r = 0; - - if (c->mem->used == 0) { - chunk_finished = 1; - break; - } - - offset = c->mem->ptr + c->offset; - toSend = c->mem->used - 1 - c->offset; - - /** - * 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. - * - * SSL_write(..., 0) return 0 which is handle as an error (Success) - * checking toSend and not calling SSL_write() is simpler - */ - - ERR_clear_error(); - if (toSend != 0 && (r = SSL_write(sock->ssl, offset, toSend)) <= 0) { - unsigned long err; - - switch ((ssl_r = SSL_get_error(sock->ssl, r))) { - case SSL_ERROR_WANT_WRITE: - break; - case SSL_ERROR_SYSCALL: - /* perhaps we have error waiting in our error-queue */ - if (0 != (err = ERR_get_error())) { - do { - ERROR("SSL_write(): SSL_get_error() = %d, SSL_write() = %zd, msg = %s", - 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 NETWORK_STATUS_CONNECTION_CLOSE; - default: - ERROR("SSL_write(): SSL_get_error() = %d, SSL_write() = %zd, errmsg = %s (%d)", - ssl_r, r, - strerror(errno), errno); - break; - } - } else { - /* neither error-queue nor errno ? */ - ERROR("SSL_write(): SSL_get_error() = %d, SSL_write() = %zd, errmsg = %s (%d)", - ssl_r, r, - strerror(errno), errno); - } - - return NETWORK_STATUS_FATAL_ERROR; - case SSL_ERROR_ZERO_RETURN: - /* clean shutdown on the remote side */ - - if (r == 0) return NETWORK_STATUS_CONNECTION_CLOSE; - - /* fall through */ - default: - while((err = ERR_get_error())) { - ERROR("SSL_write(): SSL_get_error() = %d, SSL_write() = %zd, msg = %s", - ssl_r, r, - ERR_error_string(err, NULL)); - } - - return NETWORK_STATUS_FATAL_ERROR; - } - } else { - c->offset += r; - cq->bytes_out += r; - } - - if (c->offset == (off_t)c->mem->used - 1) { - chunk_finished = 1; - } - - break; - } - case FILE_CHUNK: { - char *s; - ssize_t r; - stat_cache_entry *sce = NULL; - int ifd; - int write_wait = 0; - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { - ERROR("stat_cache_get_entry(%s) failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (NULL == local_send_buffer) { - local_send_buffer = malloc(LOCAL_SEND_BUFSIZE); - assert(local_send_buffer); - } - - do { - off_t offset = c->file.start + c->offset; - off_t toSend = c->file.length - c->offset; - - if (toSend > LOCAL_SEND_BUFSIZE) toSend = LOCAL_SEND_BUFSIZE; - - if (-1 == (ifd = open(BUF_STR(c->file.name), O_RDONLY))) { - ERROR("open(%s) failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } - - - lseek(ifd, offset, SEEK_SET); - if (-1 == (toSend = read(ifd, local_send_buffer, toSend))) { - close(ifd); - - ERROR("read(%s) failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } - - s = local_send_buffer; - - close(ifd); - - ERR_clear_error(); - if ((r = SSL_write(sock->ssl, s, toSend)) <= 0) { - unsigned long err; - - switch ((ssl_r = SSL_get_error(sock->ssl, r))) { - case SSL_ERROR_WANT_WRITE: - write_wait = 1; - break; - case SSL_ERROR_SYSCALL: - /* perhaps we have error waiting in our error-queue */ - if (0 != (err = ERR_get_error())) { - do { - ERROR("SSL_write(): ssl-error: %d (ret = %zd): %s", - 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 NETWORK_STATUS_CONNECTION_CLOSE; - default: - ERROR("SSL_write(): ssl-error: %d (ret = %zd). errno=%d, %s", - ssl_r, r, errno, - strerror(errno)); - break; - } - } else { - ERROR("SSL_write(): ssl-error: %d (ret = %zd). errno=%d, %s", - ssl_r, r, errno, - strerror(errno)); - } - - return NETWORK_STATUS_FATAL_ERROR; - case SSL_ERROR_ZERO_RETURN: - /* clean shutdown on the remote side */ - - if (r == 0) return NETWORK_STATUS_CONNECTION_CLOSE; - - /* fall thourgh */ - default: - while((err = ERR_get_error())) { - ERROR("SSL_write(): ssl-error: %d (ret = %zd), %s", - ssl_r, r, - ERR_error_string(err, NULL)); - } - - return NETWORK_STATUS_FATAL_ERROR; - } - } else { - c->offset += r; - cq->bytes_out += r; - } - - if (c->offset == c->file.length) { - chunk_finished = 1; - } - } while(!chunk_finished && !write_wait); - - break; - } - default: - ERROR("type not known: %d", c->type); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (!chunk_finished) { - /* not finished yet */ - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - } - - return NETWORK_STATUS_SUCCESS; -} -#endif - -#if 0 -network_openssl_init(void) { - p->write_ssl = network_openssl_write_chunkset; -} -#endif diff --git a/src/network_posix_aio.c b/src/network_posix_aio.c deleted file mode 100644 index b77abb22..00000000 --- a/src/network_posix_aio.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - * make sure _GNU_SOURCE is defined - */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE /* we need O_DIRECT */ -#endif - -#include "network_backends.h" - -#ifdef USE_POSIX_AIO -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/resource.h> - -#include <netinet/in.h> -#include <netinet/tcp.h> - -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> -#include <netdb.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> -#include <assert.h> - -#include <aio.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" -#include "joblist.h" - -#include "sys-files.h" -#include "status_counter.h" - -typedef struct { - server *srv; - connection *con; - - struct aiocb *iocb; - - chunk *c; -} write_job; - -static write_job *write_job_init() { - write_job *wj = calloc(1, sizeof(*wj)); - - return wj; -} - -static void write_job_free(write_job *wj) { - if (!wj) return; - - free(wj); -} - -#if (defined(__FreeBSD__) || defined(__DragonFly__)) -/* someone is wrong here, both (MacOS X and FreeBSD) reference POSIX 1003.1b but have - * different names (in /usr/include/sys/signal.h) */ -#define sival_ptr sigval_ptr -#endif - -/** - * handle the completion of a AIO-read() operation - * - * Linux has 'union sigval' and 'sigval_t' - * MacOS X and FreeBSD only 'union sigval' - * */ -static void posix_aio_completion_handler(union sigval foo) { - write_job *wj = (write_job *)foo.sival_ptr; - server *srv = wj->srv; - connection *con = wj->con; - struct aiocb *iocb = wj->iocb; - chunk *c = wj->c; - int res; - - if (srv->is_shutdown) { - write_job_free(wj); - - return; - } - - res = aio_error(iocb); - - if (res != EINPROGRESS) { - switch (res) { - case ECANCELED: - TRACE("aio-op was canceled, was asked for %s (fd = %d)", - SAFE_BUF_STR(con->uri.path), con->sock->fd); - c->async.ret_val = NETWORK_STATUS_FATAL_ERROR; - break; - case 0: - break; - default: - TRACE("aio-op failed with %d (%s), was asked for %s (fd = %d)", - res, strerror(res), SAFE_BUF_STR(con->uri.path), con->sock->fd); - c->async.ret_val = NETWORK_STATUS_FATAL_ERROR; - break; - } - - if ((res = aio_return(iocb)) < 0) { - /* we have an error */ - - TRACE("aio-return returned %d (%s), was asked for %s (fd = %d)", - res, strerror(res), SAFE_BUF_STR(con->uri.path), con->sock->fd); - - c->async.ret_val = NETWORK_STATUS_FATAL_ERROR; - } - - joblist_async_append(srv, con); - - iocb->aio_nbytes = 0; /* mark the entry as unused */ - } - - write_job_free(wj); -} - -NETWORK_BACKEND_WRITE(posixaio) { - chunk *c, *tc; - - for(c = cq->first; c; c = c->next) { - int chunk_finished = 0; - network_status_t ret; - - switch(c->type) { - case MEM_CHUNK: - ret = network_write_chunkqueue_writev_mem(srv, con, sock, cq, c); - - /* check which chunks are finished now */ - for (tc = c; tc && chunk_is_done(tc); tc = tc->next) { - /* skip the first c->next as that will be done by the c = c->next in the other for()-loop */ - if (chunk_finished) { - c = c->next; - } else { - chunk_finished = 1; - } - } - - if (ret != NETWORK_STATUS_SUCCESS) { - return ret; - } - - break; - case FILE_CHUNK: { - ssize_t r; - int rounds = 8; - - /* open file if not already opened */ - if (-1 == c->file.fd) { - if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY | /* O_DIRECT | */ (srv->srvconf.use_noatime ? O_NOATIME : 0)))) { - if (errno == EMFILE) return NETWORK_STATUS_WAIT_FOR_FD; - - ERROR("opening '%s' failed: %s", SAFE_BUF_STR(c->file.name), strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } -#ifdef FD_CLOEXEC - fcntl(c->file.fd, F_SETFD, FD_CLOEXEC); -#endif - } - - do { - size_t toSend; - const off_t max_toSend = 4 * 256 * 1024; /** should be larger than the send buffer */ - off_t offset; - - offset = c->file.start + c->offset; - - toSend = c->file.length - c->offset > max_toSend ? - max_toSend : c->file.length - c->offset; - - if (0 == c->file.copy.length) { - int async_error = 0; - - size_t iocb_ndx; - - c->file.copy.offset = 0; - c->file.copy.length = 0; - - /* if we reused the previous tmp-file we get overlaps - * - * 1 ... 3904 are ok - * 3905 ... 4096 are replaces by 8001 ... 8192 - * - * somehow the second read writes into the mmap() before - * the sendfile is finished which is very strange. - * - * if someone finds the reason for this, feel free to remove - * this if again and number of reduce the syscalls a bit. - */ - if (c->file.mmap.start) { - munmap(c->file.mmap.start, c->file.mmap.length); - c->file.mmap.start = MAP_FAILED; - } - - if (-1 != c->file.copy.fd) { - close(c->file.copy.fd); - c->file.copy.fd = -1; - } - - /* do we have a IOCB we can use ? */ - - for (iocb_ndx = 0; async_error == 0 && iocb_ndx < srv->srvconf.max_read_threads; iocb_ndx++) { - if (0 == srv->posix_aio_iocbs[iocb_ndx].aio_nbytes) { - break; - } - } - - if (iocb_ndx == srv->srvconf.max_read_threads) { - async_error = 1; - } - - - /* get mmap()ed mem-block in /dev/shm - * - * in case we don't have a iocb available, we still need the mmap() for the blocking - * read() - * */ -#if defined(HAVE_MEM_MMAP_ZERO) - if (-1 == c->file.copy.fd ) { - int mmap_fd = -1; - - /* open a file in /dev/shm to write to */ - if (-1 == (mmap_fd = open("/dev/zero", O_RDWR))) { - async_error = 1; - - if (errno != EMFILE) { - TRACE("open(/dev/zero) returned: %d (%s), falling back to sync-io", - errno, strerror(errno)); - } else { - return NETWORK_STATUS_WAIT_FOR_FD; - } - } else { - c->file.mmap.offset = 0; - c->file.mmap.length = toSend; - - c->file.mmap.start = mmap(0, c->file.mmap.length, - PROT_READ | PROT_WRITE, MAP_SHARED, mmap_fd, 0); - if (c->file.mmap.start == MAP_FAILED) { - async_error = 1; - } else { - c->file.copy.length = toSend; - } - - close(mmap_fd); - mmap_fd = -1; - - } - } -#elif defined(HAVE_MEM_MMAP_ANON) - c->file.mmap.offset = 0; - c->file.mmap.length = c->file.copy.length; /* align to page-size */ - c->file.mmap.start = mmap(0, c->file.mmap.length, - PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); - - if (c->file.mmap.start == MAP_FAILED) { - async_error = 1; - } -#else -#error hmm, does your system support mmap(/dev/zero) or mmap(MAP_ANON) -#endif - - /* looks like we couldn't get a temp-file [disk-full] - * - * if we only have 4k to send we can fall back to sync-read as either the read-ahead - * or the stat() has put the data into the fs-buffers - * - * the 4kbyte are guessed ... someone should benchmark it. - * - * */ - if (async_error == 0 && c->file.mmap.start != MAP_FAILED && c->file.length > 4 * 1024) { - struct aiocb *iocb = NULL; - write_job *wj; - - assert(c->file.copy.length > 0); - - iocb = &srv->posix_aio_iocbs[iocb_ndx]; - - memset(iocb, 0, sizeof(*iocb)); - - iocb->aio_fildes = c->file.fd; - iocb->aio_buf = c->file.mmap.start; - iocb->aio_nbytes = c->file.copy.length; - iocb->aio_offset = c->file.start + c->offset; - - wj = write_job_init(); - wj->srv = srv; - wj->con = con; - wj->iocb = iocb; - - iocb->aio_sigevent.sigev_notify_function = posix_aio_completion_handler; - iocb->aio_sigevent.sigev_notify_attributes = NULL; - iocb->aio_sigevent.sigev_value.sival_ptr = wj; - iocb->aio_sigevent.sigev_notify = SIGEV_THREAD; - - if (0 == aio_read(iocb)) { - status_counter_inc(CONST_STR_LEN("server.io.posix-aio.async-read")); - return NETWORK_STATUS_WAIT_FOR_AIO_EVENT; - } else { - if (errno != EAGAIN) { - TRACE("aio_read returned: %d (%s), falling back to sync-io", - errno, strerror(errno)); - } else { - TRACE("aio_read returned EAGAIN on (%d - %d), -> sync-io", c->file.fd, c->file.copy.fd); - } - } - } - - /* fall back to a blocking read */ - - if (c->file.mmap.start != MAP_FAILED) { - status_counter_inc(CONST_STR_LEN("server.io.posix-aio.sync-read")); - - assert(c->file.copy.length > 0); - - lseek(c->file.fd, c->file.start + c->offset, SEEK_SET); - - if (-1 == (r = read(c->file.fd, c->file.mmap.start, c->file.copy.length))) { - switch(errno) { - default: - ERROR("reading file failed: %d (%s)", errno, strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } - } - - if (r == 0) { - ERROR("read() returned 0 ... not good: %s", ""); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (r != c->file.copy.length) { - ERROR("read() returned %zd instead of %jd", r, (intmax_t) c->file.copy.length); - - return NETWORK_STATUS_FATAL_ERROR; - } - } else { - ERROR("the mmap() failed, no way for a fallback: %s", ""); - - return NETWORK_STATUS_FATAL_ERROR; - } - - } else if (c->file.copy.offset == 0) { -#if 0 - /** - * aio_write only creates extra-trouble - * - * instead we use the classic non-blocking-io write() call on the socket - */ - size_t iocb_ndx; - struct aiocb *iocb = NULL; - - /* the aio_read() is finished, send it */ - - /* do we have a IOCB we can use ? */ - - for (iocb_ndx = 0; iocb_ndx < POSIX_AIO_MAX_IOCBS; iocb_ndx++) { - if (NULL == srv->posix_aio_data[iocb_ndx]) { - break; - } - } - - assert(iocb_ndx != POSIX_AIO_MAX_IOCBS); - - iocb = &srv->posix_aio_iocbs[iocb_ndx]; - memset(iocb, 0, sizeof(*iocb)); - - iocb->aio_fildes = sock->fd; - iocb->aio_buf = c->file.mmap.start; - iocb->aio_nbytes = c->file.copy.length; - iocb->aio_offset = 0; - - /* the write should only return when it is finished */ - fcntl(sock->fd, F_SETFL, fcntl(sock->fd, F_GETFL) & ~O_NONBLOCK); - - if (0 != aio_write(iocb)) { - TRACE("aio-write failed: %d (%s)", errno, strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } - - srv->have_aio_waiting++; - - srv->posix_aio_iocbs_watch[iocb_ndx] = iocb; - srv->posix_aio_data[iocb_ndx] = con; - - /* in case we come back: we have written everything */ - c->file.copy.offset = c->file.copy.length; - - return NETWORK_STATUS_WAIT_FOR_AIO_EVENT; -#endif - } - - if (-1 == (r = write(sock->fd, c->file.mmap.start + c->file.copy.offset, c->file.copy.length - c->file.copy.offset))) { - switch (errno) { - case EINTR: - case EAGAIN: - return NETWORK_STATUS_WAIT_FOR_EVENT; - case EPIPE: - case ECONNRESET: - return NETWORK_STATUS_CONNECTION_CLOSE; - default: - ERROR("write failed: %d (%s) [%jd, %p, %jd]", - errno, strerror(errno), (intmax_t) c->file.copy.length, - c->file.mmap.start, (intmax_t) c->file.copy.offset); - return NETWORK_STATUS_FATAL_ERROR; - } - } - - if (r == 0) { - return NETWORK_STATUS_CONNECTION_CLOSE; - } - - c->file.copy.offset += r; /* offset in the copy-chunk */ - - c->offset += r; /* global offset in the file */ - cq->bytes_out += r; - - if ((off_t) c->file.mmap.length == c->file.copy.offset) { - munmap(c->file.mmap.start, c->file.mmap.length); - c->file.mmap.start = MAP_FAILED; - c->file.copy.length = 0; - } - - if (c->offset == c->file.length) { - chunk_finished = 1; - - if (c->file.copy.fd != -1) { - close(c->file.copy.fd); - c->file.copy.fd = -1; - } - - if (c->file.fd != -1) { - close(c->file.fd); - c->file.fd = -1; - } - } - - /* the chunk is larger and the current snippet is finished */ - } while (c->file.copy.length == 0 && chunk_finished == 0 && rounds-- > 0); - - break; - } - default: - - log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (!chunk_finished) { - /* not finished yet */ - - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - } - - return NETWORK_STATUS_SUCCESS; -} - -#endif - diff --git a/src/network_solaris_sendfilev.c b/src/network_solaris_sendfilev.c deleted file mode 100644 index 1a0aa965..00000000 --- a/src/network_solaris_sendfilev.c +++ /dev/null @@ -1,149 +0,0 @@ -#include "network_backends.h" - -#ifdef USE_SOLARIS_SENDFILEV - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/resource.h> - -#include <netinet/in.h> -#include <netinet/tcp.h> - -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> -#include <netdb.h> -#include <string.h> -#include <stdlib.h> -#include <limits.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" - -#ifndef UIO_MAXIOV -#define UIO_MAXIOV IOV_MAX -#endif - -/** - * a very simple sendfilev() interface for solaris which can be optimised a lot more - * as solaris sendfilev() supports 'sending everythin in one syscall()' - * - * If you want such an interface and need the performance, just give me an account on - * a solaris box. - * - jan@kneschke.de - */ - - -NETWORK_BACKEND_WRITE(solarissendfilev) { - chunk *c, *tc; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next, chunks_written++) { - int chunk_finished = 0; - network_status_t ret; - - switch(c->type) { - case MEM_CHUNK: - ret = network_write_chunkqueue_writev_mem(srv, con, sock, cq, c); - - /* check which chunks are finished now */ - for (tc = c; tc; tc = tc->next) { - /* finished the chunk */ - if (tc->offset == tc->mem->used - 1) { - /* skip the first c->next as that will be done by the c = c->next in the other for()-loop */ - if (chunk_finished) { - c = c->next; - } else { - chunk_finished = 1; - } - } else { - break; - } - } - - if (ret != NETWORK_STATUS_SUCCESS) { - return ret; - } - - break; - case FILE_CHUNK: { - ssize_t r; - off_t offset; - size_t toSend, written = 0; - sendfilevec_t fvec; - stat_cache_entry *sce = NULL; - int ifd; - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { - log_error_write(srv, __FILE__, __LINE__, "sb", - strerror(errno), c->file.name); - return -1; - } - - offset = c->file.start + c->offset; - toSend = c->file.length - c->offset; - - if (offset > sce->st.st_size) { - log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); - - return -1; - } - - if (-1 == (ifd = open(c->file.name->ptr, O_RDONLY))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); - - return -1; - } - - fvec.sfv_fd = ifd; - fvec.sfv_flag = 0; - fvec.sfv_off = offset; - fvec.sfv_len = toSend; - - /* Solaris sendfilev() */ - if (-1 == (r = sendfilev(sock->fd, &fvec, 1, &written))) { - switch (errno) { - case EAGAIN: - case EINTR: - break; - default: - ERROR("sendfilev() failed: %s (errno=%d)", strerror(errno), errno); - - close(ifd); - return NETWORK_STATUS_FATAL_ERROR; - } - - r = 0; - } - - close(ifd); - c->offset += written; - cq->bytes_out += written; - - if (c->offset == c->file.length) { - chunk_finished = 1; - } - - break; - } - default: - ERROR("chunk-type '%s' is not known", c->type); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (!chunk_finished) { - /* not finished yet */ - - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - } - - return NETWORK_STATUS_SUCCESS; -} - -#endif diff --git a/src/network_win32_send.c b/src/network_win32_send.c deleted file mode 100644 index 0ee3f4aa..00000000 --- a/src/network_win32_send.c +++ /dev/null @@ -1,265 +0,0 @@ -#include <sys/types.h> -#include <sys/stat.h> - -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <assert.h> -#include <stdio.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" - -#include "sys-socket.h" -#include "sys-files.h" - -#include "network_backends.h" - -#ifdef USE_WIN32_SEND -/** -* fill the chunkqueue will all the data that we can get -* -* this might be optimized into a readv() which uses the chunks -* as vectors -* -* - This is pretty much a copy of the generic network_read receiver. -* -*/ -NETWORK_BACKEND_READ(win32recv) -{ - int toread, read_offset; - buffer *b; - off_t r, start_bytes_in; - off_t max_read = 256 * 1024; - - /** - * a EAGAIN is a successful read if we already read something to the chunkqueue - */ - int read_something = 0; - - UNUSED(srv); - UNUSED(con); - - start_bytes_in = cq->bytes_in; - - /* use a chunk-size of 4k, append to last buffer if it has >= 1kb free */ -#define TOREAD 4096 - - do { - b = (cq->last) ? cq->last->mem : NULL; - - if (NULL == b || b->size - b->used < 1024) { - b = chunkqueue_get_append_buffer(cq); - buffer_prepare_copy(b, TOREAD+1); - } - - read_offset = (b->used == 0) ? 0 : b->used - 1; - toread = b->size - 1 - read_offset; - - if (-1 == (r = recv(sock->fd, b->ptr + read_offset, toread, 0))) { - switch (light_sock_errno()) { - case EAGAIN: - case EWOULDBLOCK: - /* remove the last chunk from the chunkqueue */ - chunkqueue_remove_empty_last_chunk(cq); - return read_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT; - case ECONNRESET: - return NETWORK_STATUS_CONNECTION_CLOSE; - default: - ERROR("oops, read from fd=%d failed: %s (%d)", sock->fd, strerror(errno), errno ); - - return NETWORK_STATUS_FATAL_ERROR; - } - } - - if (r == 0) { - chunkqueue_remove_empty_last_chunk(cq); - return read_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_CONNECTION_CLOSE; - } - - read_something = 1; - - if (b->used > 0) b->used--; - b->used += r; - b->ptr[b->used++] = '\0'; - - cq->bytes_in += r; - - if (cq->bytes_in - start_bytes_in > max_read) break; - } while (r == toread); - - return NETWORK_STATUS_SUCCESS; -} - -NETWORK_BACKEND_WRITE(win32send) -{ - chunk *c; - size_t chunks_written = 0; - - for(c = cq->first; c; c = c->next) - { - int chunk_finished = 0; - - switch(c->type) - { - case MEM_CHUNK: - { - char * offset; - size_t toSend; - ssize_t r; - - if (c->mem->used == 0) { - chunk_finished = 1; - break; - } - - offset = c->mem->ptr + c->offset; - toSend = c->mem->used - 1 - c->offset; - - if ((r = send(sock->fd, offset, toSend, 0)) < 0) - { - errno = WSAGetLastError(); - - switch(errno) - { - case WSAEWOULDBLOCK: - return NETWORK_STATUS_WAIT_FOR_EVENT; - case WSAECONNABORTED: - case WSAECONNRESET: - return NETWORK_STATUS_CONNECTION_CLOSE; - default: - log_error_write(srv, __FILE__, __LINE__, "sdd", "send to socket:", errno, sock->fd); - return NETWORK_STATUS_FATAL_ERROR; - } - - return NETWORK_STATUS_FATAL_ERROR; - } - - c->offset += r; - cq->bytes_out += r; - - if (c->offset == (off_t)c->mem->used - 1) { - chunk_finished = 1; - } - - break; - } - case FILE_CHUNK: - { - ssize_t r; - off_t offset; - - stat_cache_entry *sce = NULL; - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) - { - log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), c->file.name); - return NETWORK_STATUS_FATAL_ERROR; - } - - offset = c->file.start + c->offset; - - if (offset > sce->st.st_size) - { - log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); - return NETWORK_STATUS_FATAL_ERROR; - } - - - if (-1 == c->file.fd) - { - if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY|O_BINARY|O_SEQUENTIAL))) - { - log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } - } - - if (-1 == lseek(c->file.fd, offset, SEEK_SET)) - { - log_error_write(srv, __FILE__, __LINE__, "ss", "lseek failed: ", strerror(errno)); - } - - while(1) - { - off_t haveRead = 0; - int finished = 0; - int toSend; - - /* only send 64k blocks */ - toSend = c->file.length - c->offset > 256 * 1024 ? 256 * 1024 : c->file.length - c->offset; - - /* BMH. Not 100% sure about this */ - if ( toSend == 0 ) - break; - - buffer_prepare_copy(srv->tmp_buf, toSend); - - if (-1 == (haveRead = read(c->file.fd, srv->tmp_buf->ptr, toSend))) - { - log_error_write(srv, __FILE__, __LINE__, "ss", "read from file: ", strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (-1 == (r = send(sock->fd, srv->tmp_buf->ptr, haveRead, 0))) - { - errno = WSAGetLastError(); - - switch(errno) - { - case WSAEWOULDBLOCK: - return NETWORK_STATUS_WAIT_FOR_EVENT; - case WSAECONNABORTED: - case WSAECONNRESET: - return NETWORK_STATUS_CONNECTION_CLOSE; - default: - log_error_write(srv, __FILE__, __LINE__, "sd", "send to socket:", errno); - - return NETWORK_STATUS_FATAL_ERROR; - } - } - - c->offset += r; - cq->bytes_out += r; - - /* BMH: I don't understand this */ - if (r != haveRead) - { - break; - } - } - - if (c->offset == c->file.length) - { - chunk_finished = 1; - } - - break; - } - default: - - log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (!chunk_finished) - { - /* not finished yet */ - - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - - chunks_written++; - } - /* fprintf(stderr, "%s.%d: chunks_written: %d\r\n", __FILE__, __LINE__, chunks_written); */ - - return NETWORK_STATUS_SUCCESS; -} - -#endif diff --git a/src/network_write.c b/src/network_write.c deleted file mode 100644 index 5de91927..00000000 --- a/src/network_write.c +++ /dev/null @@ -1,227 +0,0 @@ -#include <sys/types.h> -#include <sys/stat.h> - -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <stdlib.h> -#include <assert.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" - -#include "sys-socket.h" -#include "sys-files.h" - -#include "network_backends.h" - -#ifdef USE_WRITE - -#ifdef HAVE_SYS_FILIO_H -# include <sys/filio.h> -#endif - -#ifdef HAVE_SYS_RESOURCE_H -#include <sys/resource.h> -#endif - - -/** -* fill the chunkqueue will all the data that we can get -* -* this might be optimized into a readv() which uses the chunks -* as vectors -*/ -NETWORK_BACKEND_READ(read) { - int toread, read_offset; - buffer *b; - off_t r, start_bytes_in; - off_t max_read = 256 * 1024; - - /** - * a EAGAIN is a successful read if we already read something to the chunkqueue - */ - int read_something = 0; - - UNUSED(srv); - UNUSED(con); - - start_bytes_in = cq->bytes_in; - - /* use a chunk-size of 4k, append to last buffer if it has >= 1kb free */ -#define TOREAD 4096 - - do { - b = (cq->last) ? cq->last->mem : NULL; - - if (NULL == b || b->size - b->used < 1024) { - b = chunkqueue_get_append_buffer(cq); - buffer_prepare_copy(b, TOREAD+1); - } - - read_offset = (b->used == 0) ? 0 : b->used - 1; - toread = b->size - 1 - read_offset; - - if (-1 == (r = read(sock->fd, b->ptr + read_offset, toread))) { - switch (errno) { - case EAGAIN: - /* remove the last chunk from the chunkqueue */ - chunkqueue_remove_empty_last_chunk(cq); - return read_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_WAIT_FOR_EVENT; - case ECONNRESET: - return NETWORK_STATUS_CONNECTION_CLOSE; - default: - ERROR("oops, read from fd=%d failed: %s (%d)", sock->fd, strerror(errno), errno ); - - return NETWORK_STATUS_FATAL_ERROR; - } - } - - if (r == 0) { - chunkqueue_remove_empty_last_chunk(cq); - return read_something ? NETWORK_STATUS_SUCCESS : NETWORK_STATUS_CONNECTION_CLOSE; - } - - read_something = 1; - - if (b->used > 0) b->used--; - b->used += r; - b->ptr[b->used++] = '\0'; - - cq->bytes_in += r; - - if (cq->bytes_in - start_bytes_in > max_read) break; - } while (r == toread); - - return NETWORK_STATUS_SUCCESS; -} - -NETWORK_BACKEND_WRITE(write) { - chunk *c; - - for(c = cq->first; c; c = c->next) { - int chunk_finished = 0; - - switch(c->type) { - case MEM_CHUNK: { - char * offset; - size_t toSend; - ssize_t r; - - if (c->mem->used == 0) { - chunk_finished = 1; - break; - } - - offset = c->mem->ptr + c->offset; - toSend = c->mem->used - 1 - c->offset; - - if ((r = write(sock->fd, offset, toSend)) < 0) { - log_error_write(srv, __FILE__, __LINE__, "ssd", "write failed: ", strerror(errno), sock->fd); - - return NETWORK_STATUS_FATAL_ERROR; - } - - c->offset += r; - cq->bytes_out += r; - - if (c->offset == (off_t)c->mem->used - 1) { - chunk_finished = 1; - } - - break; - } - case FILE_CHUNK: { -#ifdef USE_MMAP - char *p = NULL; -#endif - ssize_t r; - off_t offset; - size_t toSend; - stat_cache_entry *sce = NULL; - int ifd; - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { - log_error_write(srv, __FILE__, __LINE__, "sb", - strerror(errno), c->file.name); - return NETWORK_STATUS_FATAL_ERROR; - } - - offset = c->file.start + c->offset; - toSend = c->file.length - c->offset; - - if (offset > sce->st.st_size) { - log_error_write(srv, __FILE__, __LINE__, "sb", "file was shrinked:", c->file.name); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (-1 == (ifd = open(c->file.name->ptr, O_RDONLY))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } - -#if defined USE_MMAP - if (MAP_FAILED == (p = mmap(0, sce->st.st_size, PROT_READ, MAP_SHARED, ifd, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "mmap failed: ", strerror(errno)); - - close(ifd); - - return NETWORK_STATUS_FATAL_ERROR; - } - close(ifd); - - if ((r = write(sock->fd, p + offset, toSend)) <= 0) { - log_error_write(srv, __FILE__, __LINE__, "ss", "write failed: ", strerror(errno)); - munmap(p, sce->st.st_size); - return NETWORK_STATUS_FATAL_ERROR; - } - - munmap(p, sce->st.st_size); -#else - buffer_prepare_copy(srv->tmp_buf, toSend); - - lseek(ifd, offset, SEEK_SET); - if (-1 == (toSend = read(ifd, srv->tmp_buf->ptr, toSend))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "read: ", strerror(errno)); - close(ifd); - - return NETWORK_STATUS_FATAL_ERROR; - } - close(ifd); - - if (-1 == (r = send(sock->fd, srv->tmp_buf->ptr, toSend, 0))) { - log_error_write(srv, __FILE__, __LINE__, "ss", "write: ", strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } -#endif - c->offset += r; - cq->bytes_out += r; - - if (c->offset == c->file.length) { - chunk_finished = 1; - } - - break; - } - default: - - log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (!chunk_finished) { - /* not finished yet */ - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - } - - return NETWORK_STATUS_SUCCESS; -} - -#endif diff --git a/src/network_writev.c b/src/network_writev.c deleted file mode 100644 index 63c5dd46..00000000 --- a/src/network_writev.c +++ /dev/null @@ -1,356 +0,0 @@ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE /* we need O_DIRECT */ -#endif - -#include "network_backends.h" - -#ifdef USE_WRITEV - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/uio.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/resource.h> -#include <netinet/in.h> -#include <netinet/tcp.h> - -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> -#include <netdb.h> -#include <string.h> -#include <stdlib.h> -#include <limits.h> -#include <stdio.h> -#include <assert.h> - -#include "network.h" -#include "fdevent.h" -#include "log.h" -#include "stat_cache.h" -#include "sys-files.h" - -#ifndef UIO_MAXIOV -# if defined(__FreeBSD__) || defined(__APPLE__) || defined(__NetBSD__) -/* FreeBSD 4.7 defines it in sys/uio.h only if _KERNEL is specified */ -# define UIO_MAXIOV 1024 -# elif defined(__sgi) -/* IRIX 6.5 has sysconf(_SC_IOV_MAX) which might return 512 or bigger */ -# define UIO_MAXIOV 512 -# elif defined(__sun) -/* Solaris (and SunOS?) defines IOV_MAX instead */ -# ifndef IOV_MAX -# define UIO_MAXIOV 16 -# else -# define UIO_MAXIOV IOV_MAX -# endif -# elif defined(IOV_MAX) -# define UIO_MAXIOV IOV_MAX -# else -# error UIO_MAXIOV nor IOV_MAX are defined -# endif -#endif - -#if 0 -#define LOCAL_BUFFERING 1 -#endif - -NETWORK_BACKEND_WRITE_CHUNK(writev_mem) { - char * offset; - size_t toSend; - ssize_t r; - - size_t num_chunks, i; - struct iovec chunks[UIO_MAXIOV]; - chunk *tc; /* transfer chunks */ - size_t num_bytes = 0; - - UNUSED(con); - /* we can't send more then SSIZE_MAX bytes in one chunk */ - - /* build writev list - * - * 1. limit: num_chunks < UIO_MAXIOV - * 2. limit: num_bytes < SSIZE_MAX - */ - for(num_chunks = 0, tc = c; tc && tc->type == MEM_CHUNK && num_chunks < UIO_MAXIOV; num_chunks++, tc = tc->next); - - for(tc = c, i = 0; i < num_chunks; tc = tc->next, i++) { - if (tc->mem->used == 0) { - chunks[i].iov_base = tc->mem->ptr; - chunks[i].iov_len = 0; - } else { - offset = tc->mem->ptr + tc->offset; - toSend = tc->mem->used - 1 - tc->offset; - - chunks[i].iov_base = offset; - - /* protect the return value of writev() */ - if (toSend > SSIZE_MAX || - num_bytes + toSend > SSIZE_MAX) { - chunks[i].iov_len = SSIZE_MAX - num_bytes; - - num_chunks = i + 1; - break; - } else { - chunks[i].iov_len = toSend; - } - - num_bytes += toSend; - } - } - - if ((r = writev(sock->fd, chunks, num_chunks)) < 0) { - switch (errno) { - case EAGAIN: - return NETWORK_STATUS_WAIT_FOR_EVENT; - case EINTR: - return NETWORK_STATUS_INTERRUPTED; - case EPIPE: - case ECONNRESET: - return NETWORK_STATUS_CONNECTION_CLOSE; - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", - "writev failed:", strerror(errno), sock->fd); - - return NETWORK_STATUS_FATAL_ERROR; - } - } - - cq->bytes_out += r; - - /* check which chunks have been written */ - - for(i = 0, tc = c; i < num_chunks; i++, tc = tc->next) { - if (r >= (ssize_t)chunks[i].iov_len) { - /* written */ - r -= chunks[i].iov_len; - tc->offset += chunks[i].iov_len; - } else { - /* partially written */ - - tc->offset += r; - - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - } - - /* all chunks have been pushed out */ - return NETWORK_STATUS_SUCCESS; -} - -NETWORK_BACKEND_WRITE(writev) { - chunk *c, *tc; - - for(c = cq->first; c; c = c->next) { - int chunk_finished = 0; - network_status_t ret; - - switch(c->type) { - case MEM_CHUNK: - ret = network_write_chunkqueue_writev_mem(srv, con, sock, cq, c); - - /* check which chunks are finished now */ - for (tc = c; tc && chunk_is_done(tc); tc = tc->next) { - /* skip the first c->next as that will be done by the c = c->next in the other for()-loop */ - if (chunk_finished) { - c = c->next; - } else { - chunk_finished = 1; - } - } - - if (ret != NETWORK_STATUS_SUCCESS) { - return ret; - } - - break; - case FILE_CHUNK: { - ssize_t r; - off_t abs_offset; - off_t toSend; - stat_cache_entry *sce = NULL; - -#define KByte * 1024 -#define MByte * 1024 KByte -#define GByte * 1024 MByte - const off_t we_want_to_mmap = 512 KByte; - char *start = NULL; - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, c->file.name, &sce)) { - log_error_write(srv, __FILE__, __LINE__, "sb", - strerror(errno), c->file.name); - return NETWORK_STATUS_FATAL_ERROR; - } - - abs_offset = c->file.start + c->offset; - - if (abs_offset > sce->st.st_size) { - log_error_write(srv, __FILE__, __LINE__, "sb", - "file was shrinked:", c->file.name); - - return NETWORK_STATUS_FATAL_ERROR; - } - - /* 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 we_want_to_send; - size_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 */ - we_want_to_send = c->file.length - c->offset; - to_mmap = (c->file.start + c->file.length) - c->file.mmap.offset; - - /* we have more to send than we can mmap() at once */ - if (abs_offset + we_want_to_send > c->file.mmap.offset + we_want_to_mmap) { - we_want_to_send = (c->file.mmap.offset + we_want_to_mmap) - abs_offset; - to_mmap = we_want_to_mmap; - } - - if (-1 == c->file.fd) { /* open the file if not already open */ - if (-1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY | (srv->srvconf.use_noatime ? O_NOATIME : 0) ))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", "open failed for:", c->file.name, strerror(errno)); - - return NETWORK_STATUS_FATAL_ERROR; - } -#ifdef FD_CLOEXEC - fcntl(c->file.fd, F_SETFD, FD_CLOEXEC); -#endif - } - - if (MAP_FAILED == (c->file.mmap.start = mmap(0, to_mmap, PROT_READ, MAP_SHARED, c->file.fd, c->file.mmap.offset))) { - /* close it here, otherwise we'd have to set FD_CLOEXEC */ - - log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed:", - strerror(errno), c->file.name, c->file.fd); - - return NETWORK_STATUS_FATAL_ERROR; - } - - c->file.mmap.length = to_mmap; -#ifdef LOCAL_BUFFERING - buffer_copy_string_len(c->mem, c->file.mmap.start, c->file.mmap.length); -#else -#ifdef HAVE_MADVISE - /* don't advise files < 64Kb */ - if (c->file.mmap.length > (64 KByte)) { - /* darwin 7 is returning EINVAL all the time and I don't know how to - * detect this at runtime.i - * - * ignore the return value for now */ - madvise(c->file.mmap.start, c->file.mmap.length, MADV_WILLNEED); - } -#endif -#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 < 0) { - log_error_write(srv, __FILE__, __LINE__, "soooo", - "toSend is negative:", - toSend, - c->file.mmap.length, - abs_offset, - c->file.mmap.offset); - assert(toSend < 0); - } - -#ifdef LOCAL_BUFFERING - start = c->mem->ptr; -#else - start = c->file.mmap.start; -#endif - - if ((r = write(sock->fd, start + (abs_offset - c->file.mmap.offset), toSend)) < 0) { - switch (errno) { - case EAGAIN: - case EINTR: - return NETWORK_STATUS_WAIT_FOR_EVENT; - case EPIPE: - case ECONNRESET: - return NETWORK_STATUS_CONNECTION_CLOSE; - default: - log_error_write(srv, __FILE__, __LINE__, "ssd", - "write failed:", strerror(errno), sock->fd); - - return NETWORK_STATUS_FATAL_ERROR; - } - } - - c->offset += r; - cq->bytes_out += r; - - if (c->offset == c->file.length) { - chunk_finished = 1; - - /* we don't need the mmaping anymore */ - if (c->file.mmap.start != MAP_FAILED) { - munmap(c->file.mmap.start, c->file.mmap.length); - c->file.mmap.start = MAP_FAILED; - } - } - - break; - } - default: - - log_error_write(srv, __FILE__, __LINE__, "ds", c, "type not known"); - - return NETWORK_STATUS_FATAL_ERROR; - } - - if (!chunk_finished) { - /* not finished yet */ - - return NETWORK_STATUS_WAIT_FOR_EVENT; - } - } - - return NETWORK_STATUS_SUCCESS; -} - -#endif diff --git a/src/plugin.c b/src/plugin.c deleted file mode 100644 index 98ccae82..00000000 --- a/src/plugin.c +++ /dev/null @@ -1,592 +0,0 @@ -#include <string.h> -#include <stdlib.h> - -#include <stdio.h> - -#include "plugin.h" -#include "log.h" -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "sys-files.h" - -#ifndef _WIN32 -#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_RESPONSE_DONE, - PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, - PLUGIN_FUNC_HANDLE_TRIGGER, - PLUGIN_FUNC_HANDLE_SIGHUP, - PLUGIN_FUNC_HANDLE_START_BACKEND, - PLUGIN_FUNC_HANDLE_SEND_REQUEST_CONTENT, - PLUGIN_FUNC_HANDLE_RESPONSE_HEADER, - PLUGIN_FUNC_HANDLE_READ_RESPONSE_CONTENT, - PLUGIN_FUNC_HANDLE_FILTER_RESPONSE_CONTENT, - PLUGIN_FUNC_HANDLE_JOBLIST, - 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)); - - p->required_plugins = array_init(); - - return p; -} - -static void plugin_free(plugin *p) { - int use_dlclose = 1; - - if (p->name) buffer_free(p->name); - - array_free(p->required_plugins); - - /* if we are running under valgrind, - * don't unload the plugins to keep the symbols intact */ - if (RUNNING_ON_VALGRIND) use_dlclose = 0; - -#ifndef LIGHTTPD_STATIC - if (use_dlclose && p->lib) { -#ifdef _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)); - 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)); - } - - ps = srv->plugins.ptr; - ps[srv->plugins.used++] = p; - - return 0; -} - -/** - * - * - * - */ - -#ifdef LIGHTTPD_STATIC - -#define PLUGIN_STATIC(x) int x ## _plugin_init(plugin *p) - -PLUGIN_STATIC(mod_access); -PLUGIN_STATIC(mod_accesslog); -PLUGIN_STATIC(mod_alias); -PLUGIN_STATIC(mod_auth); -PLUGIN_STATIC(mod_cgi); -PLUGIN_STATIC(mod_dirlisting); -PLUGIN_STATIC(mod_evasive); -PLUGIN_STATIC(mod_evhost); -PLUGIN_STATIC(mod_expire); -PLUGIN_STATIC(mod_deflate); -PLUGIN_STATIC(mod_compress); -PLUGIN_STATIC(mod_flv_streaming); -PLUGIN_STATIC(mod_chunked); -PLUGIN_STATIC(mod_indexfile); -PLUGIN_STATIC(mod_mysql_vhost); -PLUGIN_STATIC(mod_postgresql_vhost); -PLUGIN_STATIC(mod_proxy_backend_ajp13); -PLUGIN_STATIC(mod_proxy_backend_fastcgi); -PLUGIN_STATIC(mod_proxy_backend_http); -PLUGIN_STATIC(mod_proxy_backend_scgi); -PLUGIN_STATIC(mod_proxy_core); -PLUGIN_STATIC(mod_redirect); -PLUGIN_STATIC(mod_rewrite); -PLUGIN_STATIC(mod_secdownload); -PLUGIN_STATIC(mod_setenv); -PLUGIN_STATIC(mod_simple_vhost); -PLUGIN_STATIC(mod_sql_vhost_core); -PLUGIN_STATIC(mod_ssi); -PLUGIN_STATIC(mod_staticfile); -PLUGIN_STATIC(mod_status); -PLUGIN_STATIC(mod_trigger_b4_dl); -PLUGIN_STATIC(mod_uploadprogress); -PLUGIN_STATIC(mod_userdir); -PLUGIN_STATIC(mod_usertrack); -PLUGIN_STATIC(mod_webdav); -PLUGIN_STATIC(mod_magnet); - -#undef PLUGIN_STATIC - -#define PLUGIN_STATIC(x) { #x, x ## _plugin_init } - -struct { - const char *name; - int (*init)(plugin *pl); -} const static_plugins[] = { -PLUGIN_STATIC(mod_access), -PLUGIN_STATIC(mod_accesslog), -PLUGIN_STATIC(mod_alias), -PLUGIN_STATIC(mod_auth), -PLUGIN_STATIC(mod_cgi), -PLUGIN_STATIC(mod_dirlisting), -PLUGIN_STATIC(mod_evasive), -PLUGIN_STATIC(mod_evhost), -PLUGIN_STATIC(mod_expire), -PLUGIN_STATIC(mod_deflate), -PLUGIN_STATIC(mod_compress), -PLUGIN_STATIC(mod_flv_streaming), -PLUGIN_STATIC(mod_chunked), -PLUGIN_STATIC(mod_indexfile), -PLUGIN_STATIC(mod_mysql_vhost), -PLUGIN_STATIC(mod_postgresql_vhost), -PLUGIN_STATIC(mod_proxy_backend_ajp13), -PLUGIN_STATIC(mod_proxy_backend_fastcgi), -PLUGIN_STATIC(mod_proxy_backend_http), -PLUGIN_STATIC(mod_proxy_backend_scgi), -PLUGIN_STATIC(mod_proxy_core), -PLUGIN_STATIC(mod_redirect), -PLUGIN_STATIC(mod_rewrite), -PLUGIN_STATIC(mod_secdownload), -PLUGIN_STATIC(mod_setenv), -PLUGIN_STATIC(mod_simple_vhost), -PLUGIN_STATIC(mod_sql_vhost_core), -PLUGIN_STATIC(mod_ssi), -PLUGIN_STATIC(mod_staticfile), -PLUGIN_STATIC(mod_status), -PLUGIN_STATIC(mod_trigger_b4_dl), -PLUGIN_STATIC(mod_uploadprogress), -PLUGIN_STATIC(mod_userdir), -PLUGIN_STATIC(mod_usertrack), -PLUGIN_STATIC(mod_webdav), -PLUGIN_STATIC(mod_magnet), - - { NULL, NULL } -}; - -#undef PLUGIN_STATIC - -#endif - -int plugins_load(server *srv) { - plugin *p; - int (*init)(plugin *pl); - - const char *error; - size_t i, j, k; - - for (i = 0; i < srv->srvconf.modules->used; i++) { - data_string *d = (data_string *)srv->srvconf.modules->data[i]; - char *modules = d->value->ptr; - - p = plugin_init(); - -#ifdef LIGHTTPD_STATIC - for (j = 0; static_plugins[j].name; j++) { - if (0 == strcmp(BUF_STR(d->value), static_plugins[j].name)) { - init = static_plugins[j].init; - - break; - } - } - - if (static_plugins[j].name == NULL) { - ERROR("the plugin '%s' is not compiled in", SAFE_BUF_STR(d->value)); - return -1; - } -#else - buffer_copy_string_buffer(srv->tmp_buf, srv->srvconf.modules_dir); - - if (strlen(srv->srvconf.modules_dir->ptr) != 0) buffer_append_string(srv->tmp_buf, DIR_SEPERATOR_STR); - buffer_append_string(srv->tmp_buf, modules); -#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 - -#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_reset(srv->tmp_buf); - buffer_copy_string(srv->tmp_buf, modules); - buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("_plugin_init")); - -#ifdef _WIN32 - init = (int (*)(plugin *pl)) 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 ((error = dlerror()) != NULL) { - ERROR("dlsym(%s) failed: %s", SAFE_BUF_STR(srv->tmp_buf), error); - - plugin_free(p); - return -1; - } - -#endif -#endif - if ((*init)(p)) { - ERROR("plugin-init failed for %s", SAFE_BUF_STR(d->value)); - - plugin_free(p); - return -1; - } - - /* check if the required plugin is loaded */ - for (k = 0; k < p->required_plugins->used; k++) { - data_string *req = (data_string *)p->required_plugins->data[k]; - - for (j = 0; j < i; j++) { - data_string *mod = (data_string *)srv->srvconf.modules->data[j]; - - if (buffer_is_equal(req->value, mod->value)) break; - } - - if (j == i) { - /* not found */ - log_error_write(srv, __FILE__, __LINE__, "ssbs", modules, "failed to load. required plugin", req->value, "was not loaded" ); - - plugin_free(p); - - return -1; - } - } - plugins_register(srv, p); - } - - return 0; -} - -#define PLUGIN_TO_SLOT(x, y) \ - handler_t plugins_call_##y(server *srv, connection *con) {\ - 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, 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:\ - if (con->conf.log_request_handling) TRACE("-- plugins_call_...: plugin '%s' returns %d", SAFE_BUF_STR(p->name), r); \ - return r;\ - default:\ - ERROR("-- plugins_call_...: plugin '%s' returns %d (unexpected)", SAFE_BUF_STR(p->name), r); \ - 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_DOCROOT, handle_docroot) -PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical) -PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_START_BACKEND, handle_start_backend) -PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SEND_REQUEST_CONTENT, handle_send_request_content) -PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_HEADER, handle_response_header) -PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_READ_RESPONSE_CONTENT, handle_read_response_content) -PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_FILTER_RESPONSE_CONTENT, handle_filter_response_content) -PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close) -PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_DONE, handle_response_done) -PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST, handle_joblist) -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 - -#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)); - - 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));\ - ((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_DOCROOT, handle_docroot); - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_PHYSICAL, handle_physical); - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_START_BACKEND, handle_start_backend); - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SEND_REQUEST_CONTENT, handle_send_request_content); - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_HEADER, handle_response_header); - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_READ_RESPONSE_CONTENT, handle_read_response_content); - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_FILTER_RESPONSE_CONTENT, handle_filter_response_content) - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_RESPONSE_DONE, handle_response_done); - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_CONNECTION_CLOSE, handle_connection_close); - - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_JOBLIST, handle_joblist); - 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); - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_TRIGGER, handle_trigger); - PLUGIN_TO_SLOT(PLUGIN_FUNC_HANDLE_SIGHUP, handle_sighup); -#undef PLUGIN_TO_SLOT - - if (p->init) { - if (NULL == (p->data = p->init(srv))) { - 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; - } - } - - return HANDLER_GO_ON; -} - -/** - * get the config-storage of the named plugin - */ -void *plugin_get_config(server *srv, const char *name) { - size_t i; - - for (i = 0; i < srv->plugins.used; i++) { - plugin *p = ((plugin **)srv->plugins.ptr)[i]; - - if (buffer_is_equal_string(p->name, name, strlen(name))) { - return p->data; - } - } - - return NULL; -} - -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/src/plugin.h b/src/plugin.h deleted file mode 100644 index 112c4f7c..00000000 --- a/src/plugin.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef _PLUGIN_H_ -#define _PLUGIN_H_ - -#include "settings.h" -#include "base.h" -#include "buffer.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(server *srv) -/* - * The PATCH_OPTION() macro is used in the patch_connection() functions - * of the modules to update the config object for the current request. - */ -#define PATCH_OPTION(x) \ - p->conf.x = s->x - -#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 JOBLIST_FUNC CONNECTION_FUNC -#define PHYSICALPATH_FUNC CONNECTION_FUNC -#define REQUESTDONE_FUNC CONNECTION_FUNC -#define URIHANDLER_FUNC CONNECTION_FUNC - -#define PLUGIN_DATA size_t id - -/** - * we have 4 states on the connection: - * - read-header - * - read-content - * - write-header - * - write-content - */ - -typedef struct { - size_t version; - - buffer *name; /* name of the plugin */ - - void *(* init) (server *srv); - 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 signup */ - - handler_t (* handle_uri_raw) (server *srv, connection *con, void *p_d); /* after uri_raw is set (mod_rewrite) */ - handler_t (* handle_uri_clean) (server *srv, connection *con, void *p_d); /* after uri is set (mod_access, mod_auth) */ - handler_t (* handle_docroot) (server *srv, connection *con, void *p_d); /* getting the document-root (e.g. mod_simple_vhost) */ - handler_t (* handle_physical) (server *srv, connection *con, void *p_d); /* mapping url to physical path (e.g. mod_alias, mod_proxy_core) */ - handler_t (* handle_start_backend) (server *srv, connection *con, void *p_d); /* file exists locally (e.g. mod_staticfile) */ - handler_t (* handle_send_request_content)(server *srv, connection *con, void *p_d); /* a handler for the request content */ - handler_t (* handle_response_header) (server *srv, connection *con, void *p_d); /* a handler for the request content */ - handler_t (* handle_read_response_content)(server *srv, connection *con, void *p_d); /* read the content from the source and push it to the next queue */ - handler_t (* handle_filter_response_content)(server *srv, connection *con, void *p_d); /* apply filters to response content (compression/chunk encoding/ etc..) */ - handler_t (* handle_response_done) (server *srv, connection *con, void *p_d); /* after the response is sent (e.g. mod_accesslog) */ - handler_t (* connection_reset) (server *srv, connection *con, void *p_d); /* end of a request-response cycle (mod_acceslog, mod_proxy_core) */ - handler_t (* handle_connection_close)(server *srv, connection *con, void *p_d); /* at the end of a connection [remove-me ?] */ - handler_t (* handle_joblist) (server *srv, connection *con, void *p_d); /* after all events are handled [remove-me ?] */ - - void *data; - - /* dlopen handle */ - void *lib; - - array *required_plugins; -} plugin; - -LI_EXPORT int plugins_load(server *srv); -LI_EXPORT void plugins_free(server *srv); - -LI_EXPORT handler_t plugins_call_handle_uri_raw(server *srv, connection *con); -LI_EXPORT handler_t plugins_call_handle_uri_clean(server *srv, connection *con); -LI_EXPORT handler_t plugins_call_handle_docroot(server *srv, connection *con); -LI_EXPORT handler_t plugins_call_handle_physical(server *srv, connection *con); -LI_EXPORT handler_t plugins_call_handle_start_backend(server *srv, connection *con); -LI_EXPORT handler_t plugins_call_handle_send_request_content(server *srv, connection *con); -LI_EXPORT handler_t plugins_call_handle_response_header(server *srv, connection *con); -LI_EXPORT handler_t plugins_call_handle_read_response_content(server *srv, connection *con); -LI_EXPORT handler_t plugins_call_handle_filter_response_content(server *srv, connection *con); -LI_EXPORT handler_t plugins_call_handle_response_done(server *srv, connection *con); -LI_EXPORT handler_t plugins_call_handle_connection_close(server *srv, connection *con); -LI_EXPORT handler_t plugins_call_handle_joblist(server *srv, connection *con); -LI_EXPORT handler_t plugins_call_connection_reset(server *srv, connection *con); - -LI_EXPORT handler_t plugins_call_handle_trigger(server *srv); -LI_EXPORT handler_t plugins_call_handle_sighup(server *srv); - -LI_EXPORT handler_t plugins_call_init(server *srv); -LI_EXPORT handler_t plugins_call_set_defaults(server *srv); -LI_EXPORT handler_t plugins_call_cleanup(server *srv); - -LI_EXPORT int config_insert_values_global(server *srv, array *ca, const config_values_t *cv); -LI_EXPORT int config_insert_values_internal(server *srv, array *ca, const config_values_t *cv); -LI_EXPORT int config_setup_connection(server *srv, connection *con); -LI_EXPORT int config_patch_connection(server *srv, connection *con, comp_key_t comp); -LI_EXPORT int config_check_cond(server *srv, connection *con, data_config *dc); -LI_EXPORT int config_append_cond_match_buffer(connection *con, data_config *dc, buffer *buf, int n); -LI_EXPORT int config_exec_pcre_keyvalue_buffer(connection *con, pcre_keyvalue_buffer *kvb, data_config *context, buffer *match_buf, buffer *result); - -LI_EXPORT void* plugin_get_config(server *srv, const char *name); - -#endif diff --git a/src/proc_open.c b/src/proc_open.c deleted file mode 100644 index 7e010922..00000000 --- a/src/proc_open.c +++ /dev/null @@ -1,395 +0,0 @@ -#include <stdlib.h> -#include <stdio.h> -#include <ctype.h> -#include <errno.h> -#include "proc_open.h" - -#ifdef _WIN32 -#include <io.h> -#include <fcntl.h> -#else -#include <sys/wait.h> -#include <unistd.h> -#endif - - -#ifdef _WIN32 -/* {{{ win32 stuff */ -# define SHELLENV "ComSpec" -# define SECURITY_DC , SECURITY_ATTRIBUTES *security -# define SECURITY_CC , security -# define pipe(pair) (CreatePipe(&pair[0], &pair[1], security, 2048L) ? 0 : -1) -static HANDLE dup_handle(HANDLE src, BOOL inherit, BOOL closeorig) -{ - HANDLE copy, self = GetCurrentProcess(); - - if (!DuplicateHandle(self, src, self, ©, 0, inherit, DUPLICATE_SAME_ACCESS | - (closeorig ? DUPLICATE_CLOSE_SOURCE : 0))) - return NULL; - return copy; -} -# define close_descriptor(fd) CloseHandle(fd) -static void pipe_close_parent(pipe_t *p) { - /* don't let the child inherit the parent side of the pipe */ - p->parent = dup_handle(p->parent, FALSE, TRUE); -} -static void pipe_close_child(pipe_t *p) { - close_descriptor(p->child); - p->fd = _open_osfhandle((long)p->parent, - (p->fd == 0 ? O_RDONLY : O_WRONLY)|O_BINARY); -} -/* }}} */ -#else /* _WIN32 */ -/* {{{ unix way */ -# define SHELLENV "SHELL" -# define SECURITY_DC -# define SECURITY_CC -# define close_descriptor(fd) close(fd) -static void pipe_close_parent(pipe_t *p) { - /* don't close stdin */ - close_descriptor(p->parent); - if (dup2(p->child, p->fd) != p->fd) { - perror("pipe_child dup2"); - } else { - close_descriptor(p->child); - p->child = p->fd; - } -} -static void pipe_close_child(pipe_t *p) { - close_descriptor(p->child); - p->fd = p->parent; -} -/* }}} */ -#endif /* _WIN32 */ - -/* {{{ pipe_close */ -static void pipe_close(pipe_t *p) { - close_descriptor(p->parent); - close_descriptor(p->child); -#ifdef _WIN32 - close(p->fd); -#endif -} -/* }}} */ -/* {{{ pipe_open */ -static int pipe_open(pipe_t *p, int fd SECURITY_DC) { - descriptor_t newpipe[2]; - - if (0 != pipe(newpipe)) { - fprintf(stderr, "can't open pipe"); - return -1; - } - if (0 == fd) { - p->parent = newpipe[1]; /* write */ - p->child = newpipe[0]; /* read */ - } else { - p->parent = newpipe[0]; /* read */ - p->child = newpipe[1]; /* write */ - } - p->fd = fd; - - return 0; -} -/* }}} */ - -/* {{{ proc_open_pipes */ -static int proc_open_pipes(proc_handler_t *proc SECURITY_DC) { - if (pipe_open(&(proc->in), 0 SECURITY_CC) != 0) { - return -1; - } - if (pipe_open(&(proc->out), 1 SECURITY_CC) != 0) { - return -1; - } - if (pipe_open(&(proc->err), 2 SECURITY_CC) != 0) { - return -1; - } - return 0; -} -/* }}} */ -/* {{{ proc_close_pipes */ -static void proc_close_pipes(proc_handler_t *proc) { - pipe_close(&proc->in); - pipe_close(&proc->out); - pipe_close(&proc->err); -} -/* }}} */ -/* {{{ proc_close_parents */ -static void proc_close_parents(proc_handler_t *proc) { - pipe_close_parent(&proc->in); - pipe_close_parent(&proc->out); - pipe_close_parent(&proc->err); -} -/* }}} */ -/* {{{ proc_close_childs */ -static void proc_close_childs(proc_handler_t *proc) { - pipe_close_child(&proc->in); - pipe_close_child(&proc->out); - pipe_close_child(&proc->err); -} -/* }}} */ - -#ifdef _WIN32 -/* {{{ proc_close */ -int proc_close(proc_handler_t *proc) { - proc_pid_t child = proc->child; - DWORD wstatus; - - proc_close_pipes(proc); - WaitForSingleObject(child, INFINITE); - GetExitCodeProcess(child, &wstatus); - CloseHandle(child); - - return wstatus; -} -/* }}} */ -/* {{{ proc_open */ -int proc_open(proc_handler_t *proc, const char *command) { - PROCESS_INFORMATION pi; - STARTUPINFO si; - BOOL procok; - SECURITY_ATTRIBUTES security; - const char *shell = NULL; - const char *windir = NULL; - buffer *cmdline; - - if (NULL == (shell = getenv(SHELLENV)) && - NULL == (windir = getenv("SystemRoot")) && - NULL == (windir = getenv("windir"))) { - fprintf(stderr, "One of %s,%%SystemRoot,%%windir is required", SHELLENV); - return -1; - } - - /* we use this to allow the child to inherit handles */ - memset(&security, 0, sizeof(security)); - security.nLength = sizeof(security); - security.bInheritHandle = TRUE; - security.lpSecurityDescriptor = NULL; - - if (proc_open_pipes(proc, &security) != 0) { - return -1; - } - proc_close_parents(proc); - - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - si.dwFlags = STARTF_USESTDHANDLES; - si.hStdInput = proc->in.child; - si.hStdOutput = proc->out.child; - si.hStdError = proc->err.child; - - memset(&pi, 0, sizeof(pi)); - - cmdline = buffer_init(); - if (shell) { - buffer_append_string(cmdline, shell); - } else { - buffer_append_string(cmdline, windir); - buffer_append_string_len(cmdline, CONST_STR_LEN("\\system32\\cmd.exe")); - } - buffer_append_string_len(cmdline, CONST_STR_LEN(" /c ")); - buffer_append_string(cmdline, command); - procok = CreateProcess(NULL, cmdline->ptr, &security, &security, TRUE, - NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); - - if (FALSE == procok) { - fprintf(stderr, "failed to CreateProcess: %s", cmdline->ptr); - buffer_free(cmdline); - return -1; - } - buffer_free(cmdline); - - proc->child = pi.hProcess; - CloseHandle(pi.hThread); - - proc_close_childs(proc); - - return 0; -} -/* }}} */ -#else /* _WIN32 */ -/* {{{ proc_close */ -int proc_close(proc_handler_t *proc) { - pid_t child = proc->child; - int wstatus; - pid_t wait_pid; - - proc_close_pipes(proc); - - do { - wait_pid = waitpid(child, &wstatus, 0); - } while (wait_pid == -1 && errno == EINTR); - - if (wait_pid == -1) { - return -1; - } else { - if (WIFEXITED(wstatus)) - wstatus = WEXITSTATUS(wstatus); - } - - return wstatus; -} -/* }}} */ -/* {{{ proc_open */ -int proc_open(proc_handler_t *proc, const char *command) { - pid_t child; - const char *shell; - - if (NULL == (shell = getenv(SHELLENV))) { - shell = "/bin/sh"; - } - - if (proc_open_pipes(proc) != 0) { - return -1; - } - - /* the unix way */ - - child = fork(); - - if (child == 0) { - /* this is the child process */ - - /* close those descriptors that we just opened for the parent stuff, - * dup new descriptors into required descriptors and close the original - * cruft - */ - proc_close_parents(proc); - - execl(shell, shell, "-c", command, (char *)NULL); - _exit(127); - - } else if (child < 0) { - fprintf(stderr, "failed to forking"); - proc_close(proc); - return -1; - - } else { - proc->child = child; - proc_close_childs(proc); - return 0; - } -} -/* }}} */ -#endif /* _WIN32 */ - -/* {{{ proc_read_fd_to_buffer */ -static void proc_read_fd_to_buffer(int fd, buffer *b) { - int s; /* win32 has not ssize_t */ - - for (;;) { - buffer_prepare_append(b, 512); - if ((s = read(fd, (void *)(b->ptr + b->used), 512 - 1)) <= 0) { - break; - } - b->used += s; - } - b->ptr[b->used] = '\0'; -} -/* }}} */ -/* {{{ proc_open_buffer */ -int proc_open_buffer(const char *command, buffer *in, buffer *out, buffer *err) { - proc_handler_t proc; - - if (proc_open(&proc, command) != 0) { - return -1; - } - - if (in) { - if (write(proc.in.fd, (void *)in->ptr, in->used) < 0) { - perror("error writing pipe"); - return -1; - } - } - pipe_close(&proc.in); - - if (out) { - proc_read_fd_to_buffer(proc.out.fd, out); - } - pipe_close(&proc.out); - - if (err) { - proc_read_fd_to_buffer(proc.err.fd, err); - } - pipe_close(&proc.err); - - proc_close(&proc); - - return 0; -} -/* }}} */ - -/* {{{ test */ -#ifdef DEBUG_PROC_OPEN -int main() { - proc_handler_t proc; - buffer *in = buffer_init(), *out = buffer_init(), *err = buffer_init(); - int wstatus; - -#define FREE() do { \ - buffer_free(in); \ - buffer_free(out); \ - buffer_free(err); \ -} while (0) - -#define RESET() do { \ - buffer_reset(in); \ - buffer_reset(out); \ - buffer_reset(err); \ - wstatus = proc_close(&proc); \ - if (0&&wstatus != 0) { \ - fprintf(stdout, "exitstatus %d\n", wstatus); \ - return __LINE__ - 200; \ - } \ -} while (0) - -#define ERROR_OUT() do { \ - fprintf(stdout, "failed opening proc\n"); \ - wstatus = proc_close(&proc); \ - fprintf(stdout, "exitstatus %d\n", wstatus); \ - FREE(); \ - return __LINE__ - 300; \ -} while (0) - -#ifdef _WIN32 -#define CMD_CAT "pause" -#else -#define CMD_CAT "cat" -#endif - - do { - fprintf(stdout, "test: echo 123 without read\n"); - if (proc_open(&proc, "echo 321") != 0) { - ERROR_OUT(); - } - close_descriptor(proc.in.parent); - close_descriptor(proc.out.parent); - close_descriptor(proc.err.parent); - RESET(); - - fprintf(stdout, "test: echo 321 with read\n"); fflush(stdout); - if (proc_open_buffer("echo 321", NULL, out, err) != 0) { - ERROR_OUT(); - } - fprintf(stdout, "result: ->%s<-\n\n", out->ptr); fflush(stdout); - RESET(); - - fprintf(stdout, "test: echo 123 | " CMD_CAT "\n"); fflush(stdout); - buffer_copy_string_len(in, CONST_STR_LEN("123\n")); - if (proc_open_buffer(CMD_CAT, in, out, err) != 0) { - ERROR_OUT(); - } - fprintf(stdout, "result: ->%s<-\n\n", out->ptr); fflush(stdout); - RESET(); - } while (0); - -#undef RESET -#undef ERROR_OUT - - fprintf(stdout, "ok\n"); - - FREE(); - return 0; -} -#endif /* DEBUG_PROC_OPEN */ -/* }}} */ - diff --git a/src/proc_open.h b/src/proc_open.h deleted file mode 100644 index 57a54b51..00000000 --- a/src/proc_open.h +++ /dev/null @@ -1,25 +0,0 @@ - -#include "buffer.h" - -#ifdef _WIN32 -#include <windows.h> -typedef HANDLE descriptor_t; -typedef HANDLE proc_pid_t; -#else -typedef int descriptor_t; -typedef pid_t proc_pid_t; -#endif - -typedef struct { - descriptor_t parent, child; - int fd; -} pipe_t; - -typedef struct { - pipe_t in, out, err; - proc_pid_t child; -} proc_handler_t; - -LI_EXPORT int proc_close(proc_handler_t *ht); -LI_EXPORT int proc_open(proc_handler_t *ht, const char *command); -LI_EXPORT int proc_open_buffer(const char *command, buffer *in, buffer *out, buffer *err); diff --git a/src/request.c b/src/request.c deleted file mode 100644 index 816b5368..00000000 --- a/src/request.c +++ /dev/null @@ -1,599 +0,0 @@ -#include <sys/stat.h> - -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <ctype.h> -#include <errno.h> - -#include "request.h" -#include "keyvalue.h" -#include "log.h" -#include "http_req.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; - 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 - */ - - /* no Host: */ - if (buffer_is_empty(host)) return 0; - if (host->used < 1) return 0; - - host_len = host->used - 1; - - /* IPv6 adress */ - if (host->ptr[0] == '[') { - char *c = host->ptr + 1; - int colon_cnt = 0; - - /* check portnumber */ - for (; *c && *c != ']'; c++) { - if (*c == ':') { - if (++colon_cnt > 7) { - return -1; - } - } else if (!light_isxdigit(*c)) { - return -1; - } - } - - /* missing ] */ - if (!*c) { - return -1; - } - - /* check port */ - if (*(c+1) == ':') { - for (c += 2; *c; c++) { - if (!light_isdigit(*c)) { - return -1; - } - } - } - return 0; - } - - 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, host->used - host_len); - else host->ptr[host_len-1] = '\0'; - host_len -= 1; - host->used -= 1; - } - - 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; -} - -#if 0 -#define DUMP_HEADER -#endif - -static int http_request_split_value(array *vals, buffer *b) { - char *s; - size_t i; - int state = 0; - /* - * parse - * - * val1, val2, val3, val4 - * - * into a array (more or less a explode() incl. striping of whitespaces - */ - - if (b->used == 0) return 0; - - s = b->ptr; - - for (i =0; i < b->used - 1; ) { - char *start = NULL, *end = NULL; - data_string *ds; - - switch (state) { - case 0: /* ws */ - - /* skip ws */ - for (; (*s == ' ' || *s == '\t') && i < b->used - 1; i++, s++); - - - state = 1; - break; - case 1: /* value */ - start = s; - - for (; *s != ',' && i < b->used - 1; i++, s++); - end = s - 1; - - for (; (*end == ' ' || *end == '\t') && end > start; end--); - - if (NULL == (ds = (data_string *)array_get_unused_element(vals, TYPE_STRING))) { - ds = data_string_init(); - } - - buffer_copy_string_len(ds->value, start, end-start+1); - array_insert_unique(vals, (data_unset *)ds); - - if (*s == ',') { - state = 0; - i++; - s++; - } else { - /* end of string */ - - state = 2; - } - break; - default: - i++; - break; - } - } - return 0; -} - -int http_request_parse(server *srv, connection *con, http_req *req) { - size_t i; - enum { HTTP_CONNECTION_UNSET, HTTP_CONNECTION_CLOSE, HTTP_CONNECTION_KEEPALIVE } keep_alive_set = HTTP_CONNECTION_UNSET; - - if (req->protocol == HTTP_VERSION_UNSET) { - con->http_status = 505; /* Version not Supported */ - return 0; - } - - if (req->method == HTTP_METHOD_UNSET) { - con->http_status = 405; /* Method not allowed */ - return 0; - } - - if (buffer_is_empty(req->uri_raw)) { - con->http_status = 400; - return 0; - } - - /* strip absolute URLs - * */ - - buffer_copy_string_buffer(con->request.orig_uri, req->uri_raw); - if (req->uri_raw->ptr[0] == '/') { - buffer_copy_string_buffer(con->request.uri, req->uri_raw); - } else if (req->uri_raw->ptr[0] == '*') { - if (req->method != HTTP_METHOD_OPTIONS) { - con->http_status = 400; - return 0; - } - buffer_copy_string_buffer(con->request.uri, req->uri_raw); - } else { - /* GET http://www.example.org/foobar */ - char *sl; - int l; - - if (0 == strncmp(BUF_STR(req->uri_raw), "https://", 8)) { - l = 8; - } else if (0 == strncmp(BUF_STR(req->uri_raw), "http://", 7)) { - l = 7; - } else { - con->http_status = 400; - return 0; - } - - if (NULL == (sl = strchr(BUF_STR(req->uri_raw) + l, '/'))) { - con->http_status = 400; - return 0; - } - - buffer_copy_string(con->request.uri, sl); - buffer_copy_string_len(con->request.http_host, BUF_STR(req->uri_raw) + l, sl - BUF_STR(req->uri_raw) - l); - - if (request_check_hostname(con->request.http_host)) { - if (srv->srvconf.log_request_header_on_error) { - TRACE("Host header is invalid (Status: 400), was %s", SAFE_BUF_STR(con->request.http_host)); - } - con->http_status = 400; - con->keep_alive = 0; - - buffer_reset(con->request.http_host); - - return 0; - } - } - - con->request.http_method = req->method; - con->request.http_version = req->protocol; - - for (i = 0; i < req->headers->used; i++) { - data_string *ds = (data_string *)req->headers->data[i]; - data_string *hdr; - int cmp; - - /* this list is sorted */ - if (0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Connection")))) { - array *vals; - size_t vi; - /* Connection: Keep-Alive, ... */ - - vals = srv->split_vals; - - array_reset(vals); - http_request_split_value(vals, ds->value); - - for (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"))) { - keep_alive_set = HTTP_CONNECTION_KEEPALIVE; - - break; - } else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("close"))) { - keep_alive_set = HTTP_CONNECTION_CLOSE; - - break; - } - } - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) { - char *err; - off_t r; - - /* ignore the header, if it is a duplicate */ - if (con->request.content_length != -1) continue; - - r = str_to_off_t(ds->value->ptr, &err, 10); - - if (*err != '\0') { - TRACE("content-length is not a number: %s (Status: 400)", err); - - con->http_status = 400; - - return 0; - } - - /** - * check if we had a over- or underrun in the string conversion - */ - if (r == STR_OFF_T_MIN || - r == STR_OFF_T_MAX) { - if (errno == ERANGE) { - con->http_status = 413; - - return 0; - } - } - - /** - * negative content-length is not supported - * and is a bad request - */ - if (r < 0) { - con->http_status = 400; - - return 0; - } - - /* don't handle more the SSIZE_MAX bytes in content-length */ - if (r > SSIZE_MAX) { - con->http_status = 413; - - ERROR("request-size too long: %s (Status: 413)", SAFE_BUF_STR(ds->value)); - - return 0; - } - - con->request.content_length = r; - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Expect")))) { - /* HTTP 2616 8.2.3 - * Expect: 100-continue - * - * -> (10.1.1) 100 (read content, process request, send final status-code) - * -> (10.4.18) 417 (close) - * - * - */ - - if (0 != buffer_caseless_compare(CONST_BUF_LEN(ds->value), CONST_STR_LEN("100-continue"))) { - /* we only support 100-continue */ - con->http_status = 417; - return 0; - } - - if (con->request.http_version != HTTP_VERSION_1_1) { - /* only HTTP/1.1 clients can send us this header */ - con->http_status = 417; - return 0; - } - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Host")))) { - if (request_check_hostname(ds->value)) { - TRACE("Host header is invalid (Status: 400), was %s", SAFE_BUF_STR(ds->value)); - con->http_status = 400; - con->keep_alive = 0; - - return 0; - } - - if (!buffer_is_empty(con->request.http_host) && !buffer_is_equal(con->request.http_host, ds->value)) { - TRACE("%s", "Host header is duplicate (Status: 400)"); - con->http_status = 400; - - return 0; - } - - buffer_copy_string_buffer(con->request.http_host, ds->value); - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) { - data_string *old; - - if (NULL != (old = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("If-Modified-Since")))) { - if (0 != buffer_caseless_compare(CONST_BUF_LEN(old->value), CONST_BUF_LEN(ds->value))) { - /* duplicate header and different timestamps */ - con->http_status = 400; - - TRACE("%s", "If-Modified-Since is duplicate (Status: 400)"); - - return 0; - } - } - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) { - data_string *old; - /* if dup, only the first one will survive */ - if (NULL != (old = (data_string *)array_get_element(con->request.headers, CONST_STR_LEN("If-None-Match")))) { - continue; - } - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Range")))) { - if (NULL != array_get_element(con->request.headers, CONST_STR_LEN("Range"))) { - /* duplicate Range header */ - - TRACE("%s", "Range: header is duplicate (Status: 400)"); - - con->http_status = 400; - con->keep_alive = 0; - - return 0; - } - } - - if (NULL == (hdr = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) { - hdr = data_string_init(); - } - - buffer_copy_string_buffer(hdr->key, ds->key); - buffer_copy_string_buffer(hdr->value, ds->value); - - array_insert_unique(con->request.headers, (data_unset *)hdr); - } - - - con->header_len = i; - - /* do some post-processing */ - - if (con->request.http_version == HTTP_VERSION_1_1) { - if (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 (buffer_is_empty(con->request.http_host)) { - con->http_status = 400; - con->response.keep_alive = 0; - con->keep_alive = 0; - - 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); - } - return 0; - } - } else { - if (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; - } - } - - switch(con->request.http_method) { - case HTTP_METHOD_GET: - case HTTP_METHOD_HEAD: - /* content-length is forbidden for those */ - if (con->request.content_length != -1) { - /* content-length is missing */ - if (srv->srvconf.log_request_header_on_error) { - ERROR("GET/HEAD with content-length: %d", 400); - } - - con->keep_alive = 0; - con->http_status = 400; - return 0; - } - break; - case HTTP_METHOD_POST: - /* content-length is required for them */ - if (con->request.content_length == -1) { - /* content-length is missing */ - if (srv->srvconf.log_request_header_on_error) { - log_error_write(srv, __FILE__, __LINE__, "s", - "POST-request, but content-length missing -> 411"); - } - - con->keep_alive = 0; - con->http_status = 411; - return 0; - - } - break; - default: - /* the may have a content-length */ - break; - } - - - /* check if we have read post data */ - if (con->request.content_length != -1) { - /* divide by 1024 as srvconf.max_request_size is in kBytes */ - if (srv->srvconf.max_request_size != 0 && - ((size_t)(con->request.content_length >> 10)) > srv->srvconf.max_request_size) { - /* the request body itself is larger then - * our our max_request_size - */ - - con->http_status = 413; - con->keep_alive = 0; - - log_error_write(srv, __FILE__, __LINE__, "sos", - "request-size too long:", con->request.content_length, "-> 413"); - return 0; - } - } - - return 0; -} - - diff --git a/src/request.h b/src/request.h deleted file mode 100644 index e5cc87c2..00000000 --- a/src/request.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _REQUEST_H_ -#define _REQUEST_H_ - -#include "base.h" -#include "http_req.h" - -LI_EXPORT int http_request_parse(server *srv, connection *con, http_req *req); - -#endif diff --git a/src/response.c b/src/response.c deleted file mode 100644 index 6dfc4e62..00000000 --- a/src/response.c +++ /dev/null @@ -1,794 +0,0 @@ -#include <sys/types.h> -#include <sys/stat.h> - -#include <limits.h> -#include <errno.h> -#include <fcntl.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <ctype.h> -#include <assert.h> - -#include <stdio.h> - -#include "settings.h" - -#include "response.h" -#include "keyvalue.h" -#include "log.h" -#include "stat_cache.h" -#include "chunk.h" - -#include "connections.h" - -#include "plugin.h" - -#include "sys-socket.h" -#include "sys-files.h" -#include "sys-strings.h" - -int http_response_write_header(server *srv, connection *con, chunkqueue *raw) { - buffer *b; - size_t i; - int have_date = 0; - int have_server = 0; - int allow_keep_alive = 0; - - b = chunkqueue_get_prepend_buffer(raw); - - 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 ")); - } - buffer_append_long(b, con->http_status); - buffer_append_string_len(b, CONST_STR_LEN(" ")); - buffer_append_string(b, get_http_status_name(con->http_status)); - - if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { - response_header_overwrite(srv, con, CONST_STR_LEN("Transfer-Encoding"), CONST_STR_LEN("chunked")); - allow_keep_alive = 1; - } else if ((con->http_status >= 100 && con->http_status < 200) || - con->http_status == 204 || - con->http_status == 304) { - /* 1xx, 204 and 304 never have a content-body -> - * never have a Content-Length and are always - * able to do keep-alive - */ - allow_keep_alive = 1; - } else if (con->response.content_length >= 0) { - buffer_copy_off_t(srv->tmp_buf, con->response.content_length); - response_header_overwrite(srv, con, CONST_STR_LEN("Content-Length"), srv->tmp_buf->ptr, srv->tmp_buf->used - 1); - - allow_keep_alive = 1; - } - - /* keep-alive needs Content-Length or chunked encoding. */ - if (!allow_keep_alive) con->keep_alive = 0; - - /* 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 { - con->keep_alive_idle = con->conf.max_keep_alive_idle; - } - - if (con->request.http_version != HTTP_VERSION_1_1 || con->keep_alive == 0) { - if (con->keep_alive) { - response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive")); - } else { - response_header_overwrite(srv, con, CONST_STR_LEN("Connection"), CONST_STR_LEN("close")); - } - } - - - /* add all headers */ - for (i = 0; i < con->response.headers->used; i++) { - data_string *ds; - - ds = (data_string *)con->response.headers->data[i]; - - if (ds->value->used && ds->key->used && - 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-LIGHTTPD-")) && - 0 != strcasecmp(ds->key->ptr, "X-Sendfile")) { - if (0 == strcasecmp(ds->key->ptr, "Date")) have_date = 1; - if (0 == strcasecmp(ds->key->ptr, "Server")) have_server = 1; - - 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 0 - log_error_write(srv, __FILE__, __LINE__, "bb", - ds->key, ds->value); -#endif - } - } - - if (!have_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_prepare_copy(srv->ts_date_str, 255); - - strftime(srv->ts_date_str->ptr, srv->ts_date_str->size - 1, - "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(srv->cur_ts))); - - srv->ts_date_str->used = strlen(srv->ts_date_str->ptr) + 1; - - srv->last_generated_date_ts = srv->cur_ts; - } - - buffer_append_string_buffer(b, srv->ts_date_str); - } - - if (!have_server) { - if (buffer_is_empty(con->conf.server_tag)) { - buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: " PACKAGE_NAME "/" PACKAGE_VERSION)); - } else { - buffer_append_string_len(b, CONST_STR_LEN("\r\nServer: ")); - buffer_append_string_buffer(b, con->conf.server_tag); - } - } - - buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n")); - - - con->bytes_header = b->used - 1; - raw->bytes_in += b->used - 1; - - if (con->conf.log_response_header) { - log_error_write(srv, __FILE__, __LINE__, "sSb", "Response-Header:", "\n", b); - } - - return 0; -} - -#ifdef USE_OPENSSL -static void https_add_ssl_entries(connection *con) { - X509 *xs; - X509_NAME *xn; - X509_NAME_ENTRY *xe; - if ( - SSL_get_verify_result(con->sock->ssl) != X509_V_OK - || !(xs = SSL_get_peer_certificate(con->sock->ssl)) - ) { - return; - } - - xn = X509_get_subject_name(xs); - for (int i = 0, nentries = X509_NAME_entry_count(xn); i < nentries; ++i) { - int xobjnid; - const char * xobjsn; - data_string *envds; - - 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) { - continue; - } - - if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { - envds = data_string_init(); - } - buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_S_DN_")); - buffer_append_string(envds->key, xobjsn); - buffer_copy_string_len( - envds->value, - (const char *)xe->value->data, xe->value->length - ); - /* pick one of the exported values as "authed user", for example - * ssl.verifyclient.username = "SSL_CLIENT_S_DN_UID" or "SSL_CLIENT_S_DN_emailAddress" - */ - if (buffer_is_equal(con->conf.ssl_verifyclient_username, envds->key)) { - buffer_copy_string_buffer(con->authed_user, envds->value); - } - array_insert_unique(con->environment, (data_unset *)envds); - } - if (con->conf.ssl_verifyclient_export_cert) { - BIO *bio; - if (NULL != (bio = BIO_new(BIO_s_mem()))) { - data_string *envds; - int n; - - PEM_write_bio_X509(bio, xs); - n = BIO_pending(bio); - - if (NULL == (envds = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) { - envds = data_string_init(); - } - - buffer_copy_string_len(envds->key, CONST_STR_LEN("SSL_CLIENT_CERT")); - buffer_prepare_copy(envds->value, n+1); - BIO_read(bio, envds->value->ptr, n); - BIO_free(bio); - envds->value->ptr[n] = '\0'; - envds->value->used = n+1; - array_insert_unique(con->environment, (data_unset *)envds); - } - } - X509_free(xs); -} -#endif - - -handler_t handle_get_backend(server *srv, connection *con) { - handler_t r; - - /* looks like someone has already made a decision */ - if (con->mode == DIRECT && - (con->http_status != 0 && con->http_status != 200)) { - /* remove a packets in the queue */ - - return HANDLER_FINISHED; - } - - /* no decision yet, build conf->filename */ - if (con->mode == DIRECT && con->physical.path->used == 0) { - char *qstr; - - /* we only come here when we have to 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->conf.log_condition_handling) { - TRACE("run condition: %s", ""); - } - config_patch_connection(srv, con, COMP_SERVER_SOCKET); /* SERVERsocket */ - - /** - * prepare strings - * - * - uri.path_raw - * - uri.path (secure) - * - uri.query - * - */ - - /** - * Name according to RFC 2396 - * - * - scheme - * - authority - * - path - * - query - * - * (scheme)://(authority)(path)?(query)#fragment - * - * - */ - - if (con->conf.is_ssl) { - buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("https")); - } else { - buffer_copy_string_len(con->uri.scheme, CONST_STR_LEN("http")); - } - buffer_copy_string_buffer(con->uri.authority, con->request.http_host); - buffer_to_lower(con->uri.authority); - - config_patch_connection(srv, con, COMP_HTTP_SCHEME); /* Scheme: */ - config_patch_connection(srv, con, COMP_HTTP_HOST); /* Host: */ - config_patch_connection(srv, con, COMP_HTTP_REMOTE_IP); /* Client-IP */ - config_patch_connection(srv, con, COMP_HTTP_REFERER); /* Referer: */ - config_patch_connection(srv, con, COMP_HTTP_USER_AGENT);/* User-Agent: */ - config_patch_connection(srv, con, COMP_HTTP_COOKIE); /* Cookie: */ - config_patch_connection(srv, con, COMP_HTTP_REQUEST_METHOD); /* REQUEST_METHOD */ - - /** their might be a fragment which has to be cut away */ - if (NULL != (qstr = strchr(con->request.uri->ptr, '#'))) { - con->request.uri->used = qstr - con->request.uri->ptr; - con->request.uri->ptr[con->request.uri->used++] = '\0'; - } - - /** extract query string from request.uri */ - if (NULL != (qstr = strchr(con->request.uri->ptr, '?'))) { - buffer_copy_string (con->uri.query, qstr + 1); - buffer_copy_string_len(con->uri.path_raw, con->request.uri->ptr, qstr - con->request.uri->ptr); - } else { - buffer_reset (con->uri.query); - buffer_copy_string_buffer(con->uri.path_raw, con->request.uri); - } - - if (con->conf.log_request_handling) { - TRACE("-- %s", "splitting Request-URI"); - TRACE("Request-URI : %s", SAFE_BUF_STR(con->request.uri)); - TRACE("URI-scheme : %s", SAFE_BUF_STR(con->uri.scheme)); - TRACE("URI-authority: %s", SAFE_BUF_STR(con->uri.authority)); - TRACE("URI-path : %s", SAFE_BUF_STR(con->uri.path_raw)); - TRACE("URI-query : %s", SAFE_BUF_STR(con->uri.query)); - } - - if (srv->sockets_disabled) { - con->keep_alive = 0; - } - - - /** - * - * 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: - ERROR("plugins_call_handle_uri_raw() returned unexpected: %d", r); - break; - } - - /* build filename - * - * - decode url-encodings (e.g. %20 -> ' ') - * - remove path-modifiers (e.g. /../) - */ - - - - if (con->request.http_method == HTTP_METHOD_OPTIONS && - con->uri.path_raw->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') { - /* OPTIONS * ... */ - buffer_copy_string_buffer(con->uri.path, con->uri.path_raw); - } else { - buffer_copy_string_buffer(srv->tmp_buf, con->uri.path_raw); - buffer_urldecode_path(srv->tmp_buf); - buffer_path_simplify(con->uri.path, srv->tmp_buf); - } - - if (con->conf.log_request_handling) { - TRACE("-- %s", "sanitizing URI"); - TRACE("URI-path : %s", SAFE_BUF_STR(con->uri.path)); - } - -#ifdef USE_OPENSSL - if (con->conf.is_ssl && con->conf.ssl_verifyclient) { - https_add_ssl_entries(con); - } -#endif - - /** - * - * call plugins - * - * - based on the clean URL - * - */ - - config_patch_connection(srv, con, COMP_HTTP_URL); /* HTTPurl */ - config_patch_connection(srv, con, COMP_HTTP_QUERY_STRING); /* HTTPqs */ - - /* do we have to downgrade to 1.0 ? */ - if (!con->conf.allow_http11) { - con->request.http_version = HTTP_VERSION_1_0; - } - - 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: - ERROR("plugins_call_handle_uri_clean() returned unexpected: %d", r); - break; - } - - if (con->request.http_method == HTTP_METHOD_OPTIONS && - con->uri.path->ptr[0] == '*' && con->uri.path_raw->ptr[1] == '\0') { - /* option requests are handled directly without checking the path */ - - response_header_insert(srv, con, CONST_STR_LEN("Allow"), CONST_STR_LEN("OPTIONS, GET, HEAD, POST")); - - con->http_status = 200; - /* no more content to send */ - con->send->is_closed = 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_string_buffer(con->physical.doc_root, con->conf.document_root); - buffer_copy_string_buffer(con->physical.rel_path, con->uri.path); - - filename_unix2local(con->physical.rel_path); -#if defined(_WIN32) || defined(__CYGWIN__) - /* strip dots and spaces from the end - * - * windows/dos handle those filenames as the same file - * - * foo == foo. == foo..... == "foo... " == "foo.. ./" - * - * This will affect PATHINFO in some cases - * - * 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 i; - - if (b->used > 2 && - b->ptr[b->used-2] == '/' && - (b->ptr[b->used-3] == ' ' || - b->ptr[b->used-3] == '.')) { - b->ptr[b->used--] = '\0'; - } - - for (i = b->used - 2; b->used > 1; i--) { - if (b->ptr[i] == ' ' || - b->ptr[i] == '.') { - b->ptr[b->used--] = '\0'; - } else { - break; - } - } - } -#endif - - if (con->conf.log_request_handling) { - TRACE("-- %s", "before doc_root"); - TRACE("Doc-Root : %s", SAFE_BUF_STR(con->physical.doc_root)); - TRACE("Rel-Path : %s", SAFE_BUF_STR(con->physical.rel_path)); - TRACE("Path : %s", SAFE_BUF_STR(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: - ERROR("plugins_call_handle_docroot() returned unexpected: %d", r); - break; - } - - /* The default Mac OS X and Windows filesystems can't distiguish between - * upper- and lowercase, so convert to lowercase - */ - 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_is_empty(con->server_name)) { - buffer_copy_string_buffer(con->server_name, con->uri.authority); - } - - /** - * create physical filename - * -> physical.path = docroot + rel_path - * - */ - - buffer_copy_string_buffer(con->physical.path, con->physical.doc_root); - PATHNAME_APPEND_SLASH(con->physical.path); - buffer_copy_string_buffer(con->physical.basedir, con->physical.path); - if (con->physical.rel_path->used && - con->physical.rel_path->ptr[0] == DIR_SEPERATOR) { - buffer_append_string_len(con->physical.path, con->physical.rel_path->ptr + 1, con->physical.rel_path->used - 2); - } else { - buffer_append_string_buffer(con->physical.path, con->physical.rel_path); - } - - /* win32: directories can't have a trailing slash */ - if (con->physical.path->ptr[con->physical.path->used - 2] == DIR_SEPERATOR) { - con->physical.path->ptr[con->physical.path->used - 2] = '\0'; - con->physical.path->used--; - } - - if (con->conf.log_request_handling) { - TRACE("-- %s", "after doc_root"); - TRACE("Doc-Root : %s", SAFE_BUF_STR(con->physical.doc_root)); - TRACE("Rel-Path : %s", SAFE_BUF_STR(con->physical.rel_path)); - TRACE("Path : %s", SAFE_BUF_STR(con->physical.path)); - } - - 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: - ERROR("plugins_call_handle_physical() returned unexpected: %d", r); - break; - } - - config_patch_connection(srv, con, COMP_PHYSICAL_PATH); /* physical-path */ - - if (con->conf.log_request_handling) { - TRACE("-- %s", "logical -> physical"); - TRACE("Doc-Root : %s", SAFE_BUF_STR(con->physical.doc_root)); - TRACE("Rel-Path : %s", SAFE_BUF_STR(con->physical.rel_path)); - TRACE("Path : %s", SAFE_BUF_STR(con->physical.path)); - } - } - - /* - * No one took the file away from the normal path of execution yet (like mod_access) - * - * we don't have a backend yet, try to resolve the physical path and go on - * - */ - - if (con->mode == DIRECT) { - char *slash = NULL; - char *pathinfo = NULL; - int found = 0; - stat_cache_entry *sce = NULL; - - if (con->conf.log_request_handling) { - TRACE("-- %s", "handling physical path"); - TRACE("Path : %s", SAFE_BUF_STR(con->physical.path)); - } - - switch ((r = stat_cache_get_entry_async(srv, con, con->physical.path, &sce))) { - case HANDLER_GO_ON: - /* file exists */ - - if (con->conf.log_request_handling) { - TRACE("-- %s", "file found"); - TRACE("Path : %s", SAFE_BUF_STR(con->physical.path)); - } - -#ifdef HAVE_LSTAT - if ((sce->is_symlink != 0) && !con->conf.follow_symlink) { - con->http_status = 403; - - if (con->conf.log_request_handling) { - TRACE("-- %s", "access denied due symlink restriction"); - TRACE("Path : %s", SAFE_BUF_STR(con->physical.path)); - } - - buffer_reset(con->physical.path); - return HANDLER_FINISHED; - }; -#endif - - if (S_ISDIR(sce->st.st_mode)) { - if (con->uri.path->ptr[con->uri.path->used - 2] != '/') { - /* 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 ?*/ - - - } - break; - case HANDLER_WAIT_FOR_EVENT: - return HANDLER_WAIT_FOR_EVENT; - case HANDLER_ERROR: - switch (errno) { - case EACCES: - con->http_status = 403; - - if (con->conf.log_request_handling) { - TRACE("-- %s", "access denied"); - TRACE("Path : %s", SAFE_BUF_STR(con->physical.path)); - } - - buffer_reset(con->physical.path); - return HANDLER_FINISHED; - case ENOENT: - con->http_status = 404; - - if (con->conf.log_request_handling) { - TRACE("-- %s", "file not found"); - TRACE("Path : %s", SAFE_BUF_STR(con->physical.path)); - } - - buffer_reset(con->physical.path); - return HANDLER_FINISHED; - case ENOTDIR: - /* PATH_INFO ! :) */ - break; - case EMFILE: - return HANDLER_WAIT_FOR_FD; - default: - /* we have no idea what happened, so tell the user. */ - con->http_status = 500; - - ERROR("checking file '%s' (%s) failed: %d (%s) -> sending status 500", - SAFE_BUF_STR(con->uri.path), - SAFE_BUF_STR(con->physical.path), - errno, strerror(errno)); - - buffer_reset(con->physical.path); - - return HANDLER_FINISHED; - } - - /* not found, perhaps PATHINFO */ - - buffer_copy_string_buffer(srv->tmp_buf, con->physical.path); - - do { - if (slash) { - buffer_copy_string_len(con->physical.path, srv->tmp_buf->ptr, slash - srv->tmp_buf->ptr); - } else { - buffer_copy_string_buffer(con->physical.path, srv->tmp_buf); - } - - if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { - found = S_ISREG(sce->st.st_mode); - break; - } - - if (pathinfo != NULL) { - *pathinfo = '\0'; - } - slash = strrchr(srv->tmp_buf->ptr, '/'); - - if (pathinfo != NULL) { - /* restore '/' */ - *pathinfo = '/'; - } - - if (slash) pathinfo = slash; - } while ((found == 0) && (slash != NULL) && ((size_t)(slash - srv->tmp_buf->ptr) > (con->physical.basedir->used - 2))); - - if (found == 0) { - /* no, it really doesn't exists */ - con->http_status = 404; - - if (con->conf.log_file_not_found) { - TRACE("file not found: %s -> %s", - SAFE_BUF_STR(con->uri.path), - SAFE_BUF_STR(con->physical.path)); - } - - buffer_reset(con->physical.path); - - return HANDLER_FINISHED; - } - -#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 - - /* we have a PATHINFO */ - if (pathinfo) { - buffer_copy_string(con->request.pathinfo, pathinfo); - - /* - * shorten uri.path - */ - - con->uri.path->used -= strlen(pathinfo); - con->uri.path->ptr[con->uri.path->used - 1] = '\0'; - } - - if (con->conf.log_request_handling) { - TRACE("-- %s", "after pathinfo check"); - TRACE("Path : %s", SAFE_BUF_STR(con->physical.path)); - TRACE("URI : %s", SAFE_BUF_STR(con->uri.path)); - TRACE("Pathinfo : %s", SAFE_BUF_STR(con->request.pathinfo)); - } - break; - default: - ERROR("stat_cache_get_entry_async() returned unexpected: %d", r); - break; - } - - config_patch_connection(srv, con, COMP_PHYSICAL_PATH_EXISTS); /* physical-path */ - - if (con->conf.log_request_handling) { - TRACE("-- %s", "handling subrequest"); - TRACE("Path : %s", SAFE_BUF_STR(con->physical.path)); - } - - /* call the handlers */ - switch(r = plugins_call_handle_start_backend(srv, con)) { - case HANDLER_GO_ON: - break; - case HANDLER_FINISHED: - case HANDLER_COMEBACK: - case HANDLER_WAIT_FOR_EVENT: - case HANDLER_ERROR: - return r; - - default: - ERROR("plugins_call_handle_start_backend() returned unexpected: %d", r); - return r; - } - - if (con->conf.log_request_handling) { - TRACE("-- %s", "subrequest finished"); - } - } - - if (con->mode == DIRECT) { - /* if we are still here, no one wanted the file; status 403 is ok I think */ - con->http_status = 403; - - if (con->conf.log_request_handling) { - TRACE("%s", "aaaaaaah, sending 403"); - } - - return HANDLER_FINISHED; - } else { - return HANDLER_GO_ON; - } -} - - - diff --git a/src/response.h b/src/response.h deleted file mode 100644 index 0a35debc..00000000 --- a/src/response.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _RESPONSE_H_ -#define _RESPONSE_H_ - -#include <time.h> - -#include "settings.h" - -#include "server.h" - -LI_API int http_response_parse(server *srv, connection *con); -LI_API int http_response_write_header(server *srv, connection *con, chunkqueue *cq); - -LI_API int response_header_insert(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen); -LI_API int response_header_overwrite(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen); -LI_API int response_header_append(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen); - -LI_API handler_t handle_get_backend(server *srv, connection *con); -LI_API int http_response_redirect_to_directory(server *srv, connection *con); -LI_API int http_response_handle_cachable(server *srv, connection *con, buffer * mtime); - -LI_API buffer * strftime_cache_get(server *srv, time_t last_mod); -#endif diff --git a/src/server.c b/src/server.c deleted file mode 100644 index be0accc3..00000000 --- a/src/server.c +++ /dev/null @@ -1,1792 +0,0 @@ -#include <sys/types.h> -#include <sys/stat.h> - -#ifdef HAVE_SYS_TIME_H -#include <sys/time.h> -#else -#ifdef HAVE_TIME_H -#include <time.h> -#endif -#endif - -#include <string.h> -#include <errno.h> -#include <fcntl.h> -#include <stdlib.h> -#include <time.h> -#include <signal.h> -#include <assert.h> -#include <locale.h> - -#include <stdio.h> - -#include "settings.h" -#include "server.h" -#include "buffer.h" -#include "network.h" -#include "network_backends.h" -#include "log.h" -#include "keyvalue.h" -#include "response.h" -#include "request.h" -#include "chunk.h" -#include "fdevent.h" -#include "connections.h" -#include "stat_cache.h" -#include "plugin.h" -#include "joblist.h" -#include "status_counter.h" - -/** - * stack-size of the aio-threads - * - * the default is 8Mbyte which is a bit to much. Reducing it to 64k seems to be fine - * If you experience random segfaults, increase it. - */ - -#define LI_THREAD_STACK_SIZE (64 * 1024) - - -#ifdef HAVE_GETOPT_H -#include <getopt.h> -#else -#ifdef _WIN32 -#include "xgetopt.h" -#endif -#endif - -#include "valgrind/valgrind.h" - -#ifdef HAVE_SYS_WAIT_H -#include <sys/wait.h> -#endif - -#ifdef HAVE_PWD_H -#include <grp.h> -#include <pwd.h> -#endif - -#ifdef HAVE_SYS_RESOURCE_H -#include <sys/resource.h> -#endif - -#ifdef HAVE_SYS_PRCTL_H -#include <sys/prctl.h> -#endif - -#ifdef USE_OPENSSL -#include <openssl/err.h> -#endif - -#ifndef __sgi -/* IRIX doesn't like the alarm based time() optimization */ -/* #define USE_ALARM */ -#endif - -#ifdef _WIN32 -#undef HAVE_SIGNAL -#endif - -#include "sys-files.h" -#include "sys-process.h" -#include "sys-socket.h" - -#ifdef HAVE_GETUID -# ifndef HAVE_ISSETUGID - -static int l_issetugid() { - return (geteuid() != getuid() || getegid() != getgid()); -} - -# define issetugid l_issetugid -# endif -#endif - -static volatile sig_atomic_t srv_shutdown = 0; -static volatile sig_atomic_t graceful_shutdown = 0; -static volatile sig_atomic_t graceful_restart = 0; -static volatile sig_atomic_t handle_sig_alarm = 1; -static volatile sig_atomic_t handle_sig_hup = 0; -static volatile siginfo_t last_sigterm_info; -static volatile siginfo_t last_sighup_info; - -#if defined(HAVE_SIGACTION) && defined(SA_SIGINFO) -static void sigaction_handler(int sig, siginfo_t *si, void *context) { - static siginfo_t empty_siginfo; - UNUSED(context); - - if (!si) si = &empty_siginfo; - - switch (sig) { - case SIGTERM: - srv_shutdown = 1; - memcpy((siginfo_t*) &last_sigterm_info, si, sizeof(*si)); - break; - case SIGINT: - if (graceful_shutdown) { - srv_shutdown = 1; - } else { - graceful_shutdown = 1; - } - - memcpy((siginfo_t*) &last_sigterm_info, si, sizeof(*si)); - - break; - case SIGALRM: - handle_sig_alarm = 1; - break; - case SIGHUP: - handle_sig_hup = 1; - memcpy((siginfo_t*) &last_sighup_info, si, sizeof(*si)); - break; - case SIGCHLD: - break; - } -} -#elif defined(HAVE_SIGNAL) || defined(HAVE_SIGACTION) -static void signal_handler(int sig) { - switch (sig) { - case SIGTERM: srv_shutdown = 1; break; - case SIGINT: - if (graceful_shutdown) srv_shutdown = 1; - else graceful_shutdown = 1; - - break; - case SIGALRM: handle_sig_alarm = 1; break; - case SIGHUP: handle_sig_hup = 1; break; - case SIGCHLD: break; - } -} -#endif - -#ifdef HAVE_FORK -static void daemonize(void) { -#ifdef SIGTTOU - signal(SIGTTOU, SIG_IGN); -#endif -#ifdef SIGTTIN - signal(SIGTTIN, SIG_IGN); -#endif -#ifdef SIGTSTP - signal(SIGTSTP, SIG_IGN); -#endif - if (0 != fork()) exit(0); - - if (-1 == setsid()) exit(0); - - signal(SIGHUP, SIG_IGN); - - if (0 != fork()) exit(0); - - if (0 != chdir("/")) exit(0); -} -#endif - -static server *server_init(void) { - int i; - FILE *frandom = NULL; - - server *srv = calloc(1, sizeof(*srv)); - assert(srv); - - srv->max_fds = 1024; -#define CLEAN(x) \ - srv->x = buffer_init(); - - CLEAN(response_header); - CLEAN(parse_full_path); - CLEAN(ts_debug_str); - CLEAN(ts_date_str); - 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(tmp_chunk_len); -#undef CLEAN - -#define CLEAN(x) \ - srv->x = array_init(); - - CLEAN(config_context); - CLEAN(config_touched); -#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(); - } - - if ((NULL != (frandom = fopen("/dev/urandom", "rb")) || NULL != (frandom = fopen("/dev/random", "rb"))) - && 1 == fread(srv->entropy, sizeof(srv->entropy), 1, frandom)) { - srand(*(unsigned int*)srv->entropy); - srv->is_real_entropy = 1; - } else { - unsigned int j; - srand(time(NULL) ^ getpid()); - srv->is_real_entropy = 0; - for (j = 0; j < sizeof(srv->entropy); j++) - srv->entropy[j] = rand(); - } - if (frandom) fclose(frandom); - - srv->cur_ts = time(NULL); - srv->startup_ts = srv->cur_ts; - - srv->conns = calloc(1, sizeof(*srv->conns)); - assert(srv->conns); - - srv->joblist = calloc(1, sizeof(*srv->joblist)); - assert(srv->joblist); - - srv->joblist_prev = calloc(1, sizeof(*srv->joblist)); - assert(srv->joblist_prev); - - srv->fdwaitqueue = calloc(1, sizeof(*srv->fdwaitqueue)); - 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->split_vals = array_init(); - -#ifdef USE_LINUX_AIO_SENDFILE - srv->linux_io_ctx = NULL; - /** - * we can't call io_setup before the fork() in daemonize() - */ -#endif - - 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); - } - - -#ifdef USE_LINUX_AIO_SENDFILE - if (srv->linux_io_ctx) { - io_destroy(srv->linux_io_ctx); - } -#endif - -#define CLEAN(x) \ - buffer_free(srv->x); - - CLEAN(response_header); - CLEAN(parse_full_path); - CLEAN(ts_debug_str); - CLEAN(ts_date_str); - 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(tmp_chunk_len); -#undef CLEAN - -#ifdef USE_GTHREAD - fdevent_unregister(srv->ev, srv->wakeup_iosocket); - iosocket_free(srv->wakeup_iosocket); -#endif - -#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->ssl_pemfile); - buffer_free(s->ssl_ca_file); - buffer_free(s->error_handler); - buffer_free(s->errorfile_prefix); - array_free(s->mimetypes); - buffer_free(s->ssl_cipher_list); - buffer_free(s->ssl_verifyclient_username); -#ifdef USE_OPENSSL - SSL_CTX_free(s->ssl_ctx); -#endif - - free(s); - } - free(srv->config_storage); - srv->config_storage = NULL; - } - -#define CLEAN(x) \ - array_free(srv->x); - - CLEAN(config_context); - CLEAN(config_touched); - CLEAN(srvconf.upload_tempdirs); -#undef CLEAN - - joblist_free(srv, srv->joblist); - joblist_free(srv, srv->joblist_prev); - 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); -#ifdef USE_OPENSSL - if (srv->ssl_is_init) { - CRYPTO_cleanup_all_ex_data(); - ERR_free_strings(); - ERR_remove_state(0); - EVP_cleanup(); - } -#endif - free(srv); -} - -static void show_version (void) { -#ifdef USE_OPENSSL -# define TEXT_SSL " (ssl)" -#else -# define TEXT_SSL -#endif - char *b = PACKAGE_NAME "-" PACKAGE_VERSION TEXT_SSL \ -" - a light and fast webserver\n" \ -"Build-Date: " __DATE__ " " __TIME__ "\n"; -; -#undef TEXT_SSL - if (-1 == write(STDOUT_FILENO, b, strlen(b))) { - /* what to do if this happens ? */ - - exit(-1); - } -} - -static void show_features (void) { - const fdevent_handler_info_t *handler; - const network_backend_info_t *backend; - - const char *features = -#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 -#ifdef HAVE_LIBCRYPT - "\t+ crypt support\n" -#else - "\t- crypt support\n" -#endif -#ifdef USE_OPENSSL - "\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 -#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_MEMCACHE_H - "\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("\nEvent Handlers:\n\n"); - for (handler = fdevent_get_handlers(); handler->name; handler++) { - printf("\t%c %s", handler->init ? '+' : '-', handler->name); - if (handler->description) { - printf(": %s\n", handler->description); - } - else { - printf("\n"); - } - } - - printf("\nNetwork Backends:\n\n"); - for (backend = network_get_backends(); backend->name; backend++) { - printf("\t%c %s", backend->write_handler ? '+' : '-', backend->name); - if (backend->description) { - printf(": %s\n", backend->description); - } - else { - printf("\n"); - } - } - -#ifdef USE_MMAP - printf("\t+ (mmap) support\n"); -#else - printf("\t- (mmap) support\n"); -#endif - printf("\nFeatures:\n\n%s", features); -} - -static void show_help (void) { -#ifdef USE_OPENSSL -# define TEXT_SSL " (ssl)" -#else -# define TEXT_SSL -#endif - char *b = PACKAGE_NAME "-" PACKAGE_VERSION TEXT_SSL " ("__DATE__ " " __TIME__ ")" \ -" - a light and fast webserver\n" \ -"usage:\n" \ -" -f <name> filename of the config-file\n" \ -" -m <name> module directory (default: "LIBRARY_DIR")\n" \ -" -p print the parsed config-file in internal form, and exit\n" \ -" -t test the config-file, and exit\n" \ -" -D don't go to background (default: go to background)\n" \ -" -I go to background on SIGINT (useful with -D)\n" \ -" has no effect when using kqueue or /dev/poll\n" \ -" -v show version\n" \ -" -V show compile-time features\n" \ -" -h show this help\n" \ -"\n" -; -#undef TEXT_SSL -#undef TEXT_IPV6 - if (-1 == write(STDOUT_FILENO, b, strlen(b))) { - exit(-1); - } -} - -/** - * call this function whenever you get a EMFILE or ENFILE as return-value - * - * after each socket(), accept(), connect() or open() call - * - */ -int server_out_of_fds(server *srv, connection *con) { - /* we get NULL of accept() ran out of FDs */ - - if (con) { - fdwaitqueue_append(srv, con); - } - - return 0; -} - -static int lighty_mainloop(server *srv) { - fdevent_revents *revents = fdevent_revents_init(); - int poll_errno; - size_t conns_user_at_sockets_disabled = 0; - - /* the getevents and the poll() have to run in parallel - * as soon as one has data, it has to interrupt the otherone */ - - /* 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; - -#if 0 - pid_t pid; - - /* send the old process into a graceful-shutdown and start a - * new process right away - * - * BUGS: - * - if webserver is running on port < 1024 (e.g. 80, 433) - * we don't have the permissions to bind to that port anymore - * - * - * */ - if (0 == (pid = fork())) { - execve(argv[0], argv, envp); - - exit(-1); - } else if (pid == -1) { - - } else { - /* parent */ - - graceful_shutdown = 1; /* shutdown without killing running connections */ - graceful_restart = 1; /* don't delete pid file */ - } -#else - /* 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()) { - log_error_write(srv, __FILE__, __LINE__, "s", "cycling errorlog failed, dying"); - - return -1; - } else { - TRACE("logfiles cycled by UID=%d, PID=%d", - last_sighup_info.si_uid, - last_sighup_info.si_pid); - } -#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) { - int cs = 0; - 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; - } - - /* trigger waitpid */ - srv->cur_ts = min_ts; - - /* cleanup stat-cache */ - stat_cache_trigger_cleanup(srv); - /** - * check all connections for timeouts - * - */ - for (ndx = 0; ndx < conns->used; ndx++) { - int changed = 0; - connection *con; - int t_diff; - - con = conns->ptr[ndx]; - - switch (con->state) { - case CON_STATE_READ_REQUEST_HEADER: - case CON_STATE_READ_REQUEST_CONTENT: - if (con->recv->is_closed) { - if (srv->cur_ts - con->read_idle_ts > con->conf.max_connection_idle) { - /* time - out */ -#if 0 - TRACE("(connection process timeout) [%s]", SAFE_BUF_STR(con->dst_addr_buf)); -#endif - connection_set_state(srv, con, CON_STATE_ERROR); - changed = 1; - } - } - - if (con->request_count == 1) { - if (srv->cur_ts - con->read_idle_ts > con->conf.max_read_idle) { - /* time - out */ -#if 0 - TRACE("(initial read timeout) [%s]", SAFE_BUF_STR(con->dst_addr_buf)); -#endif - 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 0 - TRACE("(keep-alive read timeout) [%s]", SAFE_BUF_STR(con->dst_addr_buf)); -#endif - connection_set_state(srv, con, CON_STATE_ERROR); - changed = 1; - } - } - break; - case CON_STATE_WRITE_RESPONSE_HEADER: - case CON_STATE_WRITE_RESPONSE_CONTENT: - - - if (con->write_request_ts != 0 && - 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__, "sbsosds", - "NOTE: a request 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; - } - break; - default: - /* the other ones are uninteresting */ - break; - } - /* 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; - } - - if (changed) { - connection_state_machine(srv, con); - } - con->bytes_written_cur_second = 0; - *(con->conf.global_bytes_per_second_cnt_ptr) = 0; - -#if 0 - 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 - } - - if (cs == 1) fprintf(stderr, "\n"); - } - } - - if (srv->sockets_disabled) { - /* our server sockets are disabled, why ? */ - - if ((srv->fdwaitqueue->used == 0) && - (srv->conns->used <= srv->max_conns * 9 / 10) && - (0 == graceful_shutdown)) { - size_t i; - - for (i = 0; i < srv->srv_sockets.used; i++) { - server_socket *srv_socket = srv->srv_sockets.ptr[i]; - fdevent_event_add(srv->ev, srv_socket->sock, FDEVENT_IN); - } - - TRACE("[note] sockets enabled again%s", ""); - - srv->sockets_disabled = 0; - } - } else { - if ((srv->fdwaitqueue->used) || /* looks like some cons are waiting for FDs*/ - (srv->conns->used >= srv->max_conns) || /* out of connections */ - (graceful_shutdown)) { /* graceful_shutdown */ - size_t i; - - /* disable server-fds */ - - for (i = 0; i < srv->srv_sockets.used; i++) { - server_socket *srv_socket = srv->srv_sockets.ptr[i]; - - fdevent_event_del(srv->ev, srv_socket->sock); - - if (graceful_shutdown) { - /* we don't want this socket anymore, - * - * closing it right away will make it possible for - * the next lighttpd to take over (graceful restart) - * */ - - fdevent_unregister(srv->ev, srv_socket->sock); - closesocket(srv_socket->sock->fd); - srv_socket->sock->fd = -1; - -#ifdef HAVE_FORK - /* FreeBSD kqueue could possibly work with rfork(RFFDG) - * while Solaris /dev/poll would require re-registering - * all fd */ - if (srv->srvconf.daemonize_on_shutdown && - srv->event_handler != FDEVENT_HANDLER_FREEBSD_KQUEUE && - srv->event_handler != FDEVENT_HANDLER_SOLARIS_DEVPOLL) { - daemonize(); - } -#endif - - /* network_close() will cleanup after us */ - } - } - - if (graceful_shutdown) { - TRACE("[note] graceful shutdown started by UID=%d, PID=%d", last_sigterm_info.si_uid, last_sigterm_info.si_pid); - } else if (srv->fdwaitqueue->used) { - TRACE("[note] out of FDs, server-socket get disabled for a while, we have %zu connections open and they are waiting for %zu FDs", - srv->conns->used, srv->fdwaitqueue->used); - } else if (srv->conns->used >= srv->max_conns) { - TRACE("[note] we reached our connection limit of %zu connections. Disabling server-sockets for a while", srv->max_conns); - } - - srv->sockets_disabled = 1; - - /* we count the number of free fds indirectly. - * - * instead of checking the fds we only check the connection handles we free'd since - * the server-sockets got disabled - * */ - conns_user_at_sockets_disabled = srv->conns->used; - } - } - - 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; - continue; - } - - /* we still have some fds to share */ - if (!srv_shutdown && srv->fdwaitqueue->used) { - /* ok, we are back to the problem of 'how many fds do we have available ?' */ - connection *con; - int fd; - int avail_fds = conns_user_at_sockets_disabled - srv->conns->used; - - if (-1 == (fd = open("/dev/null", O_RDONLY))) { - switch (errno) { - case EMFILE: - avail_fds = 0; - - break; - default: - break; - } - } else { - close(fd); - - /* we have at least one FD as we just checked */ - if (!avail_fds) avail_fds++; - } - - - TRACE("conns used: %zu, fd-waitqueue has %zu entries, fds to share: %d", srv->conns->used, srv->fdwaitqueue->used, avail_fds); - - while (avail_fds-- && NULL != (con = fdwaitqueue_unshift(srv, srv->fdwaitqueue))) { - connection_state_machine(srv, con); - } - } - n = fdevent_poll(srv->ev, 1000); - poll_errno = errno; -#ifdef USE_GTHREAD - g_atomic_int_set(&srv->did_wakeup, 0); -#endif - - if (n > 0) { - /* n is the number of events */ - size_t i; - fdevent_get_revents(srv->ev, n, revents); - - /* handle client connections first - * - * this is a bit of a hack, but we have to make sure than we handle - * close-events before the connection is reused for a keep-alive - * request - * - * this is mostly an issue for mod_proxy_core, but you never know - * - */ - - for (i = 0; i < revents->used; i++) { - fdevent_revent *revent = revents->ptr[i]; - handler_t r; - - /* skip server-fds */ - if (revent->handler == network_server_handle_fdevent) continue; - - switch (r = (*(revent->handler))(srv, revent->context, revent->revents)) { - case HANDLER_WAIT_FOR_FD: - server_out_of_fds(srv, NULL); - case HANDLER_FINISHED: - case HANDLER_GO_ON: - case HANDLER_WAIT_FOR_EVENT: - break; - case HANDLER_ERROR: - /* should never happen */ - SEGFAULT("got HANDLER_ERROR from a plugin: %s", "dieing"); - break; - default: - ERROR("got handler_t(%d) from a plugin: ignored", r); - break; - } - } - - for (i = 0; i < revents->used; i++) { - fdevent_revent *revent = revents->ptr[i]; - handler_t r; - - /* server fds only */ - if (revent->handler != network_server_handle_fdevent) continue; - - switch (r = (*(revent->handler))(srv, revent->context, revent->revents)) { - case HANDLER_WAIT_FOR_FD: - server_out_of_fds(srv, NULL); - case HANDLER_FINISHED: - case HANDLER_GO_ON: - case HANDLER_WAIT_FOR_EVENT: - break; - case HANDLER_ERROR: - /* should never happen */ - SEGFAULT("got HANDLER_ERROR from a plugin: %s", "dieing"); - break; - default: - ERROR("got handler_t(%d) from a plugin: ignored", r); - break; - } - } - - } else if (n < 0 && poll_errno != EINTR) { - ERROR("fdevent_poll failed: %s", strerror(poll_errno)); - } - - /* - * Note: Two joblist's are needed so a connection can be added back into the joblist - * without getting stuck inside the for loop. - */ -#ifdef USE_GTHREAD - { - connection *con; - while (NULL != (con = g_async_queue_try_pop(srv->joblist_queue))) { - joblist_append(srv, con); - } - } -#endif - if(srv->joblist->used > 0) { - connections *joblist = srv->joblist; - /* switch joblist queues. */ - srv->joblist = srv->joblist_prev; - srv->joblist_prev = joblist; - } - for (ndx = 0; ndx < srv->joblist_prev->used; ndx++) { - connection *con = srv->joblist_prev->ptr[ndx]; - handler_t r; - - con->in_joblist = 0; - connection_state_machine(srv, con); - - switch(r = plugins_call_handle_joblist(srv, con)) { - case HANDLER_FINISHED: - case HANDLER_GO_ON: - break; - default: - ERROR("got handler_t(%d) from a plugin: ignored", r); - break; - } - } - - srv->joblist_prev->used = 0; - } - - fdevent_revents_free(revents); - - return 0; -} - -#ifdef USE_GTHREAD -static handler_t wakeup_handle_fdevent(void *s, void *context, int revent) { - server *srv = (server *)s; - connection *con = context; - char buf[16]; - UNUSED(con); - UNUSED(revent); - - (void) read(srv->wakeup_iosocket->fd, buf, sizeof(buf)); - return HANDLER_GO_ON; -} -#endif - -int main (int argc, char **argv, char **envp) { - server *srv = NULL; - int print_config = 0; - int test_config = 0; - int i_am_root; - int o; - int num_childs = 0; - int pid_fd = -1, fd; - size_t i; -#ifdef USE_GTHREAD - GThread **stat_cache_threads; - GThread **aio_write_threads = NULL; -#ifdef USE_LINUX_AIO_SENDFILE - GThread *linux_aio_read_thread_id = NULL; -#endif - GError *gerr = NULL; -#endif - -#ifdef HAVE_SIGACTION - struct sigaction act; -#endif -#ifdef HAVE_GETRLIMIT - struct rlimit rlim; -#endif - -#ifdef USE_ALARM - 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; -#endif - - UNUSED(envp); - - log_init(); - status_counter_init(); - - /* for nice %b handling in strfime() */ - setlocale(LC_TIME, "C"); - - if (NULL == (srv = server_init())) { - fprintf(stderr, "did this really happen?\n"); - return -1; - } - - /* init structs done */ - - srv->srvconf.port = 0; -#ifdef HAVE_GETUID - i_am_root = (getuid() == 0); -#else - i_am_root = 0; -#endif - srv->srvconf.dont_daemonize = 0; - srv->srvconf.daemonize_on_shutdown = 0; - srv->srvconf.max_stat_threads = 4; - srv->srvconf.max_read_threads = 8; - - while(-1 != (o = getopt(argc, argv, "f:m:hvVDIpt"))) { - switch(o) { - case 'f': - if (config_read(srv, optarg)) { - server_free(srv); - return -1; - } - break; - case 'm': - buffer_copy_string(srv->srvconf.modules_dir, optarg); - break; - case 'p': print_config = 1; break; - case 't': test_config = 1; break; - case 'D': srv->srvconf.dont_daemonize = 1; break; - case 'I': srv->srvconf.daemonize_on_shutdown = 1; break; - case 'v': show_version(); return 0; - case 'V': show_features(); return 0; - case 'h': show_help(); return 0; - default: - show_help(); - server_free(srv); - return -1; - } - } - - if (!srv->config_storage) { - log_error_write(srv, __FILE__, __LINE__, "s", - "No configuration available. Try using -f option."); - - server_free(srv); - return -1; - } - - if (print_config) { - data_unset *dc = srv->config_context->data[0]; - if (dc) { - dc->print(dc, 0); - fprintf(stdout, "\n"); - } else { - /* shouldn't happend */ - fprintf(stdout, "global config not found\n"); - } - } - - if (test_config) { - printf("Syntax OK\n"); - } - - if (test_config || print_config) { - server_free(srv); - return 0; - } - - /* close stdin and stdout, as they are not needed */ - /* move stdin to /dev/null */ - if (-1 != (fd = open("/dev/null", O_RDONLY))) { - close(STDIN_FILENO); - dup2(fd, STDIN_FILENO); - close(fd); - } - - /* move stdout to /dev/null */ - if (-1 != (fd = open("/dev/null", O_WRONLY))) { - close(STDOUT_FILENO); - dup2(fd, STDOUT_FILENO); - close(fd); - } - - if (0 != config_set_defaults(srv)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "setting default values failed"); - server_free(srv); - return -1; - } - - /* UID handling */ -#ifdef HAVE_GETUID - if (!i_am_root && issetugid()) { - /* we are setuid-root */ - - log_error_write(srv, __FILE__, __LINE__, "s", - "Are you nuts ? Don't apply a SUID bit to this binary"); - - server_free(srv); - return -1; - } -#endif - - /* check document-root */ - if (srv->config_storage[0]->document_root->used <= 1) { - log_error_write(srv, __FILE__, __LINE__, "s", - "document-root is not set\n"); - - server_free(srv); - - return -1; - } - - if (plugins_load(srv)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "loading plugins finally failed"); - - plugins_free(srv); - server_free(srv); - - return -1; - } - -#ifndef _WIN32 - /* open pid file BEFORE chroot */ - if (srv->srvconf.pid_file->used) { - if (-1 == (pid_fd = open(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 = open(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; - } - } - } -#endif - 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 - * */ - fprintf(stderr, "%s.%d: max parallel connections: %d\r\n", __FILE__, __LINE__, FD_SETSIZE); - srv->max_fds = FD_SETSIZE - 4; - } else { - srv->max_fds = 4096; - } - - if (i_am_root) { - struct group *grp = NULL; - struct passwd *pwd = NULL; - int use_rlimit = 1; - - /* valgrind only supports 1024 fds */ - if (RUNNING_ON_VALGRIND) use_rlimit = 0; - -#ifdef HAVE_GETRLIMIT - if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { - log_error_write(srv, __FILE__, __LINE__, - "ss", "couldn't get 'max filedescriptors'", - strerror(errno)); - return -1; - } - - if (use_rlimit && srv->srvconf.max_fds) { - /* set rlimits */ - - rlim.rlim_cur = srv->srvconf.max_fds; - 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; - } - } - - if (srv->event_handler == FDEVENT_HANDLER_SELECT) { - srv->max_fds = rlim.rlim_cur < ((int)FD_SETSIZE) - 200 ? rlim.rlim_cur : FD_SETSIZE - 200; - } else { - srv->max_fds = rlim.rlim_cur; - } - - /* 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 - if (srv->event_handler == FDEVENT_HANDLER_SELECT) { - /* don't raise the limit above FD_SET_SIZE */ - if (srv->max_fds > ((int)FD_SETSIZE) - 200) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "can't raise max filedescriptors above", FD_SETSIZE - 200, - "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); - return -1; - } - } - - -#ifdef HAVE_PWD_H - /* set user and group */ - if (srv->srvconf.username->used) { - 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 (srv->srvconf.groupname->used) { - 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 (grp->gr_gid == 0) { - log_error_write(srv, __FILE__, __LINE__, "s", - "I will not set gid to 0\n"); - return -1; - } - } -#endif - /* we need root-perms for port < 1024 */ - if (0 != network_init(srv)) { - plugins_free(srv); - server_free(srv); - - return -1; - } - -#ifdef HAVE_PWD_H - /** - * initgroups() has to be called before chroot() - */ - if (srv->srvconf.groupname->used) { - setgid(grp->gr_gid); - setgroups(0, NULL); - if (srv->srvconf.username->used) { - initgroups(srv->srvconf.username->ptr, grp->gr_gid); - } - } -#endif -#ifdef HAVE_CHROOT - if (srv->srvconf.changeroot->used) { - 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 (srv->srvconf.username->used) setuid(pwd->pw_uid); -#endif -#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE) - /** - * IRIX 6.5.x has prctl() but no PR_SET_DUMPABLE - */ - if (srv->srvconf.enable_cores) { - prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); - } -#endif - } else { - -#ifdef HAVE_GETRLIMIT - if (0 != getrlimit(RLIMIT_NOFILE, &rlim)) { - log_error_write(srv, __FILE__, __LINE__, - "ss", "couldn't get 'max filedescriptors'", - strerror(errno)); - return -1; - } - - if (srv->event_handler == FDEVENT_HANDLER_SELECT) { - srv->max_fds = rlim.rlim_cur < ((int)FD_SETSIZE) - 4 ? rlim.rlim_cur : FD_SETSIZE - 4; - } else { - srv->max_fds = rlim.rlim_cur; - } - - /* set core file rlimit, if enable_cores is set */ - if (srv->srvconf.enable_cores && getrlimit(RLIMIT_CORE, &rlim) == 0) { - rlim.rlim_cur = rlim.rlim_max; - setrlimit(RLIMIT_CORE, &rlim); - } - -#endif - if (srv->event_handler == FDEVENT_HANDLER_SELECT) { - /* don't raise the limit above FD_SET_SIZE */ - if (srv->max_fds > ((int)FD_SETSIZE) - 4) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "can't raise max filedescriptors above", FD_SETSIZE - 4, - "if event-handler is 'select'. Use 'poll' or something else or reduce server.max-fds."); - return -1; - } - } - - if (0 != network_init(srv)) { - plugins_free(srv); - server_free(srv); - - return -1; - } - } - - /* 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; - } - - if (HANDLER_GO_ON != plugins_call_init(srv)) { - log_error_write(srv, __FILE__, __LINE__, "s", "Initialization of plugins failed. Going down."); - - plugins_free(srv); - network_close(srv); - server_free(srv); - - return -1; - } - -#ifdef HAVE_FORK - /* network is up, let's deamonize ourself */ - if (srv->srvconf.dont_daemonize == 0) daemonize(); -#endif - -#ifdef HAVE_PWD_H - srv->gid = getgid(); - srv->uid = getuid(); -#endif - - /* write pid file */ - if (pid_fd != -1) { - buffer_copy_long(srv->tmp_buf, getpid()); - buffer_append_string_len(srv->tmp_buf, CONST_STR_LEN("\n")); - if (-1 == write(pid_fd, srv->tmp_buf->ptr, srv->tmp_buf->used - 1)) { - ERROR("writing to PID to '%s' failed: %s, ignored", "...", strerror(errno)); - } - close(pid_fd); - pid_fd = -1; - } - - if (HANDLER_GO_ON != plugins_call_set_defaults(srv)) { - log_error_write(srv, __FILE__, __LINE__, "s", "Configuration of plugins failed. Going down."); - - plugins_free(srv); - network_close(srv); - server_free(srv); - - return -1; - } - - /* 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(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) { - plugins_free(srv); - network_close(srv); - server_free(srv); - - return -1; - } - - if (-1 == log_error_open(srv->srvconf.errorlog_file, srv->srvconf.breakagelog_file, srv->srvconf.errorlog_use_syslog, srv->srvconf.dont_daemonize)) { - log_error_write(srv, __FILE__, __LINE__, "s", - "opening errorlog failed, dying"); - - plugins_free(srv); - network_close(srv); - server_free(srv); - return -1; - } - -#ifdef HAVE_SIGACTION - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &act, NULL); -# if defined(SA_SIGINFO) - 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(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); -#endif - -#ifdef USE_ALARM - signal(SIGALRM, signal_handler); - - /* setup periodic timer (1 second) */ - if (setitimer(ITIMER_REAL, &interval, NULL)) { - log_error_write(srv, __FILE__, __LINE__, "s", "setting timer failed"); - return -1; - } - - getitimer(ITIMER_REAL, &interval); -#endif - -#ifdef HAVE_FORK - /* start watcher and workers */ - num_childs = srv->srvconf.max_worker; - if (num_childs > 0) { - int child = 0; - while (!child && !srv_shutdown) { - if (num_childs > 0) { - switch (fork()) { - case -1: - return -1; - case 0: - child = 1; - break; - default: - num_childs--; - break; - } - } else { - int status; - - if (-1 != wait(&status)) { - /* a child terminated, restart it */ - num_childs++; - } else { - /* we got interrupted */ - - switch (errno) { - case EINTR: - /** - * we got asked to rotate the logs - * - * as we are just the parent and have no main-loop we have to fix it ourself - */ - if (handle_sig_hup) { - handle_sig_hup = 0; - - log_error_cycle(); - } - break; - default: - TRACE("(angel) wait() failed with: %s (errno=%d)", strerror(errno), errno); - break; - } - } - } - } - - if (srv_shutdown) { - /* kill all childs */ - kill(0, SIGTERM); - } - - /* if we are the parent, leave here */ - if (!child) return 0; - } -#endif - - if (NULL == (srv->ev = fdevent_init(srv->max_fds + 1, srv->event_handler))) { - log_error_write(srv, __FILE__, __LINE__, - "s", "fdevent_init failed"); - return -1; - } - /* - * kqueue() is called here, select resets its internals, - * all server sockets get their handlers - * - * */ - if (0 != network_register_fdevents(srv)) { - plugins_free(srv); - network_close(srv); - server_free(srv); - - return -1; - } - -#ifdef USE_GTHREAD - if (pipe(srv->wakeup_pipe) == -1) { - log_error_write(srv, __FILE__, __LINE__, "s", "pipe() failed"); - return -1; - } - srv->wakeup_iosocket = iosocket_init(); - srv->wakeup_iosocket->type = IOSOCKET_TYPE_PIPE; - srv->wakeup_iosocket->fd = srv->wakeup_pipe[0]; - srv->did_wakeup = 0; - fdevent_fcntl_set(srv->ev, srv->wakeup_iosocket); - /* block on write */ -#ifdef FD_CLOEXEC - /* close fd on exec (cgi) */ - fcntl(srv->wakeup_pipe[1], F_SETFD, FD_CLOEXEC); -#endif - fdevent_register(srv->ev, srv->wakeup_iosocket, wakeup_handle_fdevent, NULL); - fdevent_event_add(srv->ev, srv->wakeup_iosocket, FDEVENT_IN); -#endif - - /* might fail if user is using fam (not gamin) and famd isn't running */ - if (NULL == (srv->stat_cache = stat_cache_init())) { - log_error_write(srv, __FILE__, __LINE__, "s", - "stat-cache could not be setup, dieing."); - return -1; - } - -#ifdef USE_GTHREAD - g_thread_init(NULL); - - srv->stat_queue = g_async_queue_new(); - srv->joblist_queue = g_async_queue_new(); - srv->aio_write_queue = g_async_queue_new(); -#ifdef HAVE_SYS_INOTIFY_H - if (srv->srvconf.stat_cache_engine == STAT_CACHE_ENGINE_INOTIFY) { - srv->stat_cache->sock->fd = inotify_init(); - - fdevent_register(srv->ev, srv->stat_cache->sock, stat_cache_handle_fdevent, NULL); - fdevent_event_add(srv->ev, srv->stat_cache->sock, FDEVENT_IN); - } -#endif - stat_cache_threads = calloc(srv->srvconf.max_stat_threads, sizeof(*stat_cache_threads)); - - for (i = 0; i < srv->srvconf.max_stat_threads; i++) { - stat_cache_threads[i] = g_thread_create(stat_cache_thread, srv, 1, &gerr); - if (gerr) { - ERROR("g_thread_create failed: %s", gerr->message); - - return -1; - } - } - -#ifndef _WIN32 - switch (srv->network_backend) { - case NETWORK_BACKEND_GTHREAD_AIO: - aio_write_threads = calloc(srv->srvconf.max_read_threads, sizeof(*aio_write_threads)); - for (i = 0; i < srv->srvconf.max_read_threads; i++) { - aio_write_threads[i] = g_thread_create_full(network_gthread_aio_read_thread, srv, LI_THREAD_STACK_SIZE, 1, TRUE, G_THREAD_PRIORITY_NORMAL, &gerr); - if (gerr) { - ERROR("g_thread_create failed: %s", gerr->message); - - return -1; - } - } - break; -#ifdef USE_GTHREAD_SENDFILE - case NETWORK_BACKEND_GTHREAD_SENDFILE: - aio_write_threads = calloc(srv->srvconf.max_read_threads, sizeof(*aio_write_threads)); - for (i = 0; i < srv->srvconf.max_read_threads; i++) { - aio_write_threads[i] = g_thread_create_full(network_gthread_sendfile_read_thread, srv, LI_THREAD_STACK_SIZE, 1, TRUE, G_THREAD_PRIORITY_NORMAL, &gerr); - if (gerr) { - ERROR("g_thread_create failed: %s", gerr->message); - - return -1; - } - } - break; -#endif -#ifdef USE_GTHREAD_FREEBSD_SENDFILE - case NETWORK_BACKEND_GTHREAD_FREEBSD_SENDFILE: - aio_write_threads = calloc(srv->srvconf.max_read_threads, sizeof(*aio_write_threads)); - for (i = 0; i < srv->srvconf.max_read_threads; i++) { - aio_write_threads[i] = g_thread_create_full(network_gthread_freebsd_sendfile_read_thread, srv, LI_THREAD_STACK_SIZE, 1, TRUE, G_THREAD_PRIORITY_NORMAL, &gerr); - if (gerr) { - ERROR("g_thread_create failed: %s", gerr->message); - - return -1; - } - } - break; -#endif -#ifdef USE_POSIX_AIO - case NETWORK_BACKEND_POSIX_AIO: - srv->posix_aio_iocbs = calloc(srv->srvconf.max_read_threads, sizeof(*srv->posix_aio_iocbs)); - break; -#endif -#ifdef USE_LINUX_AIO_SENDFILE - case NETWORK_BACKEND_LINUX_AIO_SENDFILE: - TRACE("WARNING: You selected the experimental network.backend '%s'", "linux-aio-sendfile"); - srv->linux_io_iocbs = calloc(srv->srvconf.max_read_threads, sizeof(*srv->linux_io_iocbs)); - if (0 != (i = io_setup(srv->srvconf.max_read_threads, &(srv->linux_io_ctx)))) { - ERROR("io-setup() failed somehow %s", strerror(-i)); - - return -1; - } - linux_aio_read_thread_id = g_thread_create(linux_aio_read_thread, srv, 1, &gerr); - if (gerr) { - ERROR("g_thread_create failed: %s", gerr->message); - - return -1; - } - break; -#endif - default: - break; - } -#endif /* ifndef _WIN32 */ - -#endif /* USE_GTHREAD */ - - for (i = 0; i < srv->srv_sockets.used; i++) { - server_socket *srv_socket = srv->srv_sockets.ptr[i]; - if (-1 == fdevent_fcntl_set(srv->ev, srv_socket->sock)) { - log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed:", strerror(errno)); - return -1; - } - } - - lighty_mainloop(srv); - - if (0 == graceful_restart && - srv->srvconf.pid_file->used && - srv->srvconf.changeroot->used == 0) { - 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)); - } - } - } - - /* kill the threads */ - - srv->is_shutdown = 1; -#ifdef USE_GTHREAD -#ifdef USE_LINUX_AIO_SENDFILE - if (srv->network_backend == NETWORK_BACKEND_LINUX_AIO_SENDFILE) { - g_thread_join(linux_aio_read_thread_id); - free(srv->linux_io_iocbs); - } -#endif -#ifdef USE_POSIX_AIO - if (srv->network_backend == NETWORK_BACKEND_POSIX_AIO) { - free(srv->posix_aio_iocbs); - } -#endif - if (aio_write_threads != NULL) { - for (i = 0; i < srv->srvconf.max_read_threads; i++) { - g_thread_join(aio_write_threads[i]); - } - free(aio_write_threads); - } - - for (i = 0; i < srv->srvconf.max_stat_threads; i++) { - g_async_queue_push(srv->stat_queue, (void *) 1); - } - - for (i = 0; i < srv->srvconf.max_stat_threads; i++) { - g_thread_join(stat_cache_threads[i]); - } - - /* the ref-count should be 0 now */ - g_async_queue_unref(srv->stat_queue); - g_async_queue_unref(srv->joblist_queue); - g_async_queue_unref(srv->aio_write_queue); -#endif - /* clean-up */ - network_close(srv); - connections_free(srv); - plugins_free(srv); - server_free(srv); - - TRACE("server stopped by UID=%d, PID=%d", last_sigterm_info.si_uid, last_sigterm_info.si_pid); - - log_free(); - status_counter_free(); - -#ifdef USE_GTHREAD - free(stat_cache_threads); -#endif - - chunkpool_free(); - - return 0; -} diff --git a/src/server.h b/src/server.h deleted file mode 100644 index c6fddf97..00000000 --- a/src/server.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _SERVER_H_ -#define _SERVER_H_ - -#include "base.h" - -typedef enum { CONFIG_UNSET, CONFIG_DOCUMENT_ROOT } config_var_t; - -LI_EXPORT int config_read(server *srv, const char *fn); -LI_EXPORT int config_set_defaults(server *srv); -LI_EXPORT buffer * config_get_value_buffer(server *srv, connection *con, config_var_t field); - -#ifdef USE_GTHREAD -gpointer stat_cache_thread(gpointer ); -gpointer network_gthread_aio_read_thread(gpointer ); -gpointer network_gthread_sendfile_read_thread(gpointer ); -gpointer network_gthread_freebsd_sendfile_read_thread(gpointer ); -gpointer linux_aio_read_thread(gpointer ); -#endif - -#endif diff --git a/src/settings.h b/src/settings.h deleted file mode 100644 index 4724e83d..00000000 --- a/src/settings.h +++ /dev/null @@ -1,183 +0,0 @@ -#ifndef _LIGHTTPD_SETTINGS_H_ -#define _LIGHTTPD_SETTINGS_H_ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define BV(x) (1 << x) - -#define INET_NTOP_CACHE_MAX 4 -#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) - -/** - * max size of the HTTP request header - * - * 32k should be enough for everything (just a guess) - * - */ -#define MAX_HTTP_REQUEST_HEADER (32 * 1024) - -#ifdef HAVE_GLIB_H -#include <glib.h> -#endif - -/** - * if glib supports threads we will use it for async file-io - */ -#ifdef G_THREADS_ENABLED -# ifndef USE_GTHREAD -# define USE_GTHREAD -# endif -#endif - - -/* 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 HAVE_WRITEV && defined(__linux__) && !defined HAVE_SENDFILE_BROKEN -# define USE_LINUX_SENDFILE -# include <sys/sendfile.h> -# include <sys/uio.h> -#endif - -/* all the Async IO backends need GTHREAD support */ -#if defined(USE_GTHREAD) -# if defined(USE_LINUX_SENDFILE) -# if defined(HAVE_LIBAIO_H) - /** disabled for now as not all FSs are async-io capable */ -# define USE_LINUX_AIO_SENDFILE -# endif -# define USE_GTHREAD_SENDFILE -# endif -# if defined(HAVE_AIO_H) && (!defined(__FreeBSD__)) -/* FreeBSD has no SIGEV_THREAD for us */ -# define USE_POSIX_AIO -# include <sys/types.h> /* macosx wants it */ -# include <aio.h> -# endif -# ifdef HAVE_MMAP -# define USE_GTHREAD_AIO -# endif -#endif - -#if defined HAVE_SYS_UIO_H && defined HAVE_SENDFILE && defined HAVE_WRITEV && (defined(__FreeBSD__) || defined(__DragonFly__)) -# define USE_FREEBSD_SENDFILE -# include <sys/uio.h> -#endif - -#if defined(USE_FREEBSD_SENDFILE) && defined(USE_GTHREAD) -# define USE_GTHREAD_FREEBSD_SENDFILE -#endif - - -#if defined HAVE_SYS_SENDFILE_H && defined HAVE_SENDFILEV && defined HAVE_WRITEV && defined(__sun) -# define USE_SOLARIS_SENDFILEV -# include <sys/sendfile.h> -# include <sys/uio.h> -#endif - -#if defined HAVE_SYS_UIO_H && defined HAVE_WRITEV -# define USE_WRITEV -# include <sys/uio.h> -#endif - -#if defined HAVE_SYS_MMAN_H && defined HAVE_MMAP -# define USE_MMAP -# include <sys/mman.h> -/* NetBSD 1.3.x needs it */ -# ifndef MAP_FAILED -# define MAP_FAILED -1 -# endif - -#if defined(MAP_ANON) -#define HAVE_MEM_MMAP_ANON -#else -/* let's try /dev/zero */ -#define HAVE_MEM_MMAP_ZERO -#endif - -#endif - -#if defined HAVE_SYS_UIO_H && defined HAVE_WRITEV && defined HAVE_SEND_FILE && defined(__aix) -# define USE_AIX_SENDFILE -#endif - - -/** -* unix can use read/write or recv/send on sockets -* win32 only recv/send -*/ -#ifdef _WIN32 - -# define WIN32_LEAN_AND_MEAN -# define NOGDI -# define USE_WIN32_SEND -/* wait for async-io support -# define USE_WIN32_TRANSMITFILE -*/ -#else -# define USE_WRITE -#endif - - -typedef enum { HANDLER_UNSET, - HANDLER_GO_ON, - HANDLER_FINISHED, - HANDLER_COMEBACK, - HANDLER_WAIT_FOR_EVENT, - HANDLER_ERROR, - HANDLER_WAIT_FOR_FD -} handler_t; - -/* Shared library support */ -#ifdef _WIN32 - #define LI_IMPORT __declspec(dllimport) - #define LI_EXPORT __declspec(dllexport) - #define LI_DLLLOCAL - #define LI_DLLPUBLIC -#else - #define LI_IMPORT - #ifdef GCC_HASCLASSVISIBILITY - #define LI_EXPORT __attribute__ ((visibility("default"))) - #define LI_DLLLOCAL __attribute__ ((visibility("hidden"))) - #define LI_DLLPUBLIC __attribute__ ((visibility("default"))) - #else - #define LI_EXPORT - #define LI_DLLLOCAL - #define LI_DLLPUBLIC - #endif -#endif - -#ifdef LI_DECLARE_EXPORTS -#define LI_API LI_EXPORT -#else -#define LI_API LI_IMPORT -#endif - -/* Throwable classes must always be visible on GCC in all binaries */ -#ifdef _WIN32 - #define LI_EXCEPTIONAPI(api) api -#elif defined(GCC_HASCLASSVISIBILITY) - #define LI_EXCEPTIONAPI(api) LI_EXPORT -#else - #define LI_EXCEPTIONAPI(api) -#endif - -#ifdef UNUSED_PARAM -#elif defined(__GNUC__) -# define UNUSED_PARAM(x) UNUSED_ ## x __attribute__((unused)) -#elif defined(__LCLINT__) -# define UNUSED_PARAM(x) /*@unused@*/ x -#else -# define UNUSED_PARAM(x) x -#endif - - -#endif diff --git a/src/splaytree.c b/src/splaytree.c deleted file mode 100644 index 5d6a2b40..00000000 --- a/src/splaytree.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - 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 <stdlib.h> -#include <assert.h> -#include "splaytree.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, root_size, l_size, r_size; - - if (t == NULL) return t; - N.left = N.right = NULL; - l = r = &N; - root_size = node_size(t); - 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 */ - } -} - -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; - } - } -} - - diff --git a/src/splaytree.h b/src/splaytree.h deleted file mode 100644 index e9c1c68e..00000000 --- a/src/splaytree.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef _SPLAY_TREE_H_ -#define _SPLAY_TREE_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; - - -LI_EXPORT splay_tree * splaytree_splay (splay_tree *t, int key); -LI_EXPORT splay_tree * splaytree_insert(splay_tree *t, int key, void *data); -LI_EXPORT splay_tree * splaytree_delete(splay_tree *t, int key); -LI_EXPORT 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/src/stat_cache.c b/src/stat_cache.c deleted file mode 100644 index 55587ec2..00000000 --- a/src/stat_cache.c +++ /dev/null @@ -1,563 +0,0 @@ -/* - * make sure _GNU_SOURCE is defined - */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include <sys/types.h> -#include <sys/stat.h> - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <stdio.h> -#include <fcntl.h> -#include <assert.h> - -#include "log.h" -#include "stat_cache.h" -#include "fdevent.h" -#include "etag.h" -#include "server.h" -#include "joblist.h" - -#ifdef HAVE_ATTR_ATTRIBUTES_H -#include <attr/attributes.h> -#endif - -#include "sys-mmap.h" -#include "sys-files.h" -#include "sys-strings.h" - -#undef HAVE_FAM_H -#undef HAVE_SYS_INOTIFY_H - -#if 0 -/* enables debug code for testing if all nodes in the stat-cache as accessable */ -#define DEBUG_STAT_CACHE -#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 - */ -#ifdef USE_GTHREAD -typedef struct { - buffer *name; - - void *con; -} stat_job; - -static stat_job *stat_job_init() { - stat_job *sj = calloc(1, sizeof(*sj)); - - sj->name = buffer_init(); - - return sj; -} - -static void stat_job_free(stat_job *sj) { - if (!sj) return; - - buffer_free(sj->name); - - free(sj); -} - -gpointer stat_cache_thread(gpointer _srv) { - server *srv = (server *)_srv; - stat_job *sj = NULL; - - /* take the stat-job-queue */ - GAsyncQueue * inq; - - g_async_queue_ref(srv->stat_queue); - - inq = srv->stat_queue; - - /* */ - while (!srv->is_shutdown) { - /* let's see what we have to stat */ - struct stat st; - - if ((sj = g_async_queue_pop(inq))) { - if(sj == (stat_job *) 1) - continue; /* just notifying us that srv->is_shutdown changed */ - - /* don't care about the return code for now */ - stat(sj->name->ptr, &st); - - joblist_async_append(srv, sj->con); - stat_job_free(sj); - } - } - - g_async_queue_unref(srv->stat_queue); - - return NULL; -} -#endif - -#ifdef HAVE_GLIB_H -static guint sc_key_hash(gconstpointer v) { - buffer *b = (buffer *)v; - - return g_str_hash(b->ptr); -} - -static gboolean sc_key_equal(gconstpointer v1, gconstpointer v2) { - buffer *b1 = (buffer *)v1; - buffer *b2 = (buffer *)v2; - - return buffer_is_equal(b1, b2); -} -#endif - -stat_cache *stat_cache_init(void) { - stat_cache *fc = NULL; - - fc = calloc(1, sizeof(*fc)); - - fc->dir_name = buffer_init(); - fc->hash_key = buffer_init(); - -#if defined(HAVE_SYS_INOTIFY_H) - fc->sock = iosocket_init(); -#endif -#ifdef HAVE_GLIB_H - fc->files = g_hash_table_new(sc_key_hash, sc_key_equal); -#endif - - return fc; -} - -static stat_cache_entry * stat_cache_entry_init(void) { - stat_cache_entry *sce = NULL; - - sce = calloc(1, sizeof(*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); -} - -#ifdef HAVE_GLIB_H -static gboolean stat_cache_free_hrfunc(gpointer _key, gpointer _value, gpointer _user_data) { - stat_cache_entry *sce = _value; - buffer *b = _key; - UNUSED(_user_data); - - buffer_free(b); - stat_cache_entry_free(sce); - - return TRUE; -} -#endif - -void stat_cache_free(stat_cache *sc) { -#ifdef HAVE_GLIB_H - g_hash_table_foreach_remove(sc->files, stat_cache_free_hrfunc, NULL); - g_hash_table_destroy(sc->files); -#endif - - buffer_free(sc->dir_name); - buffer_free(sc->hash_key); - -#if defined(HAVE_SYS_INOTIFY_H) - if (sc->sock) iosocket_free(sc->sock); -#endif - - free(sc); -} - -#ifdef HAVE_XATTR -static int stat_cache_attr_get(buffer *buf, char *name) { - int attrlen; - int ret; - - attrlen = 1024; - buffer_prepare_copy(buf, attrlen); - attrlen--; - if(0 == (ret = attr_get(name, "Content-Type", buf->ptr, &attrlen, 0))) { - buf->used = attrlen + 1; - buf->ptr[attrlen] = '\0'; - } - return ret; -} -#endif - -handler_t stat_cache_handle_fdevent(void *_srv, void *_fce, int revent) { - server *srv = _srv; - - switch (srv->srvconf.stat_cache_engine) { -#ifdef HAVE_SYS_INOTIFY_H - case STAT_CACHE_ENGINE_INOTIFY: - return stat_cache_handle_fdevent_inotify(_srv, _fce, revent); -#else - UNUSED(_fce); - UNUSED(revent); -#endif - default: - return HANDLER_GO_ON; - } -} -#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 - -static int stat_cache_entry_is_current(server *srv, stat_cache_entry *sce) { - struct stat st; - UNUSED(srv); - UNUSED(sce); - - if (-1 == stat(sce->name->ptr, &st)) { - return 0; - } - /* still existing */ - - if (st.st_dev != sce->st.st_dev || st.st_ino != sce->st.st_ino) { - return 0; /* different file */ - } - - /* same file, still existing: update other stats: */ - sce->st = st; - /* need to check other properties before this: sce->stat_ts = srv->cur_ts; */ - return 1; -} - -static void stat_cache_remove_entry(stat_cache *sc, buffer *hash_key, stat_cache_entry *sce) { -#ifdef HAVE_GLIB_H - gpointer orig_key; - if (!g_hash_table_lookup_extended(sc->files, hash_key, &orig_key, NULL)) - return; - g_hash_table_remove(sc->files, hash_key); - stat_cache_entry_free(sce); - buffer_free((buffer*) orig_key); -#else - stat_cache_entry_free(sce); -#endif -} - -/*** - * - * - * - * returns: - * - HANDLER_FINISHED on cache-miss (don't forget to reopen the file) - * - HANDLER_ERROR on stat() failed -> see errno for problem - * - * - * glib: if glib is not available, we don't cache - */ - -static handler_t stat_cache_get_entry_internal(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce, int async) { - stat_cache_entry *sce = NULL; - stat_cache *sc; - struct stat st; - size_t k; - int fd; - struct stat lst; - - *ret_sce = NULL; - - /* - * check if the directory for this file has changed - */ - - sc = srv->stat_cache; - - buffer_copy_string_buffer(sc->hash_key, name); - buffer_append_long(sc->hash_key, con->conf.follow_symlink); - -#ifdef HAVE_GLIB_H - if ((sce = (stat_cache_entry *)g_hash_table_lookup(sc->files, sc->hash_key))) { - /* know this entry already */ - - if (sce->state == STAT_CACHE_ENTRY_STAT_FINISHED && - stat_cache_entry_is_current(srv, sce)) { - /* verify that this entry is still fresh */ - - *ret_sce = sce; - - return HANDLER_GO_ON; - } - } - - if (!sce) { - sce = stat_cache_entry_init(); - - buffer_copy_string_buffer(sce->name, name); - - g_hash_table_insert(sc->files, buffer_init_string(BUF_STR(sc->hash_key)), sce); - } -#else - /** - * we don't have glib, but we still have to store the sce somewhere to not loose it - */ - sce = stat_cache_entry_init(); - - buffer_copy_string_buffer(sce->name, name); -#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 - * - * */ - - /* pass a job to the stat-queue */ -#ifdef USE_GTHREAD - if (async && - srv->srvconf.max_stat_threads > 0 && - sce->state == STAT_CACHE_ENTRY_UNSET) { - stat_job *sj = stat_job_init(); - - buffer_copy_string_buffer(sj->name, name); - sj->con = con; - - g_async_queue_push(srv->stat_queue, sj); - - sce->state = STAT_CACHE_ENTRY_ASYNC_STAT; - - /* the response for this will be in the stat-cache, - * a second call will just fetch the data from the stat-cache */ - - return HANDLER_WAIT_FOR_EVENT; - } -#endif - /* - * O_NONBLOCK skips named pipes and locked files - * - * O_NOATIME leads to EPERM on SYMLINKS - * */ -#ifndef O_NONBLOCK -#define O_NONBLOCK 0 -#endif - if (-1 == (fd = open(name->ptr, O_NONBLOCK | O_RDONLY | (srv->srvconf.use_noatime ? O_NOATIME : 0)))) { - if (srv->srvconf.use_noatime && errno == EPERM) { - if (-1 == (fd = open(name->ptr, O_NONBLOCK | O_RDONLY))) { - stat_cache_remove_entry(sc, sc->hash_key, sce); - return HANDLER_ERROR; - } - } else { - stat_cache_remove_entry(sc, sc->hash_key, sce); - return HANDLER_ERROR; - } - } - - if (-1 == fstat(fd, &st)) { - close(fd); - stat_cache_remove_entry(sc, sc->hash_key, sce); - return HANDLER_ERROR; - } - - close(fd); - - sce->st = st; - sce->stat_ts = srv->cur_ts; - sce->state = STAT_CACHE_ENTRY_STAT_FINISHED; - - /* 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 (!con->conf.follow_symlink) { - if (stat_cache_lstat(srv, name, &lst) == 0) { -#ifdef DEBUG_STAT_CACHE - TRACE("found symlink in %s", SAFE_BUF_STR(name)); -#endif - sce->is_symlink = 1; - } - - /* - * we assume "/" can not be symlink, so - * skip the symlink stuff if our path is / - **/ - else if ((name->used > 2)) { - buffer *dname; - char *s_cur; - - dname = buffer_init(); - buffer_copy_string_buffer(dname, name); - - while ((s_cur = strrchr(dname->ptr,'/'))) { - *s_cur = '\0'; - dname->used = s_cur - dname->ptr + 1; - if (dname->ptr == s_cur) { -#ifdef DEBUG_STAT_CACHE - log_error_write(srv, __FILE__, __LINE__, "s", "reached /"); -#endif - break; - } -#ifdef DEBUG_STAT_CACHE - log_error_write(srv, __FILE__, __LINE__, "sbs", - "checking if", dname, "is a symlink"); -#endif - if (stat_cache_lstat(srv, dname, &lst) == 0) { - sce->is_symlink = 1; -#ifdef DEBUG_STAT_CACHE - log_error_write(srv, __FILE__, __LINE__, "sb", - "found symlink", dname); -#endif - break; - } - } - buffer_free(dname); - } - } -#endif - - if (S_ISREG(st.st_mode)) { - /* determine mimetype */ - buffer_reset(sce->content_type); -#ifdef HAVE_XATTR - if (con->conf.use_xattr) { - stat_cache_attr_get(sce->content_type, name->ptr); - } -#endif - /* xattr did not set a content-type. ask the config */ - if (buffer_is_empty(sce->content_type)) { - for (k = 0; k < con->conf.mimetypes->used; k++) { - data_string *ds = (data_string *)con->conf.mimetypes->data[k]; - buffer *type = ds->key; - - if (type->used == 0) continue; - - /* check if the right side is the same */ - if (type->used > name->used) continue; - - if (0 == strncasecmp(name->ptr + name->used - type->used, type->ptr, type->used - 1)) { - buffer_copy_string_buffer(sce->content_type, ds->value); - break; - } - } - } - etag_create(sce->etag, &(sce->st), con->etag_flags); - } else if (S_ISDIR(st.st_mode)) { - etag_create(sce->etag, &(sce->st), con->etag_flags); - } - - *ret_sce = sce; - - return HANDLER_GO_ON; -} - - -handler_t stat_cache_get_entry_async(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) { - return stat_cache_get_entry_internal(srv, con, name, ret_sce, 1); -} - -handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **ret_sce) { - return stat_cache_get_entry_internal(srv, con, name, ret_sce, 0); -} - -/** - * 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 - */ - -#ifdef HAVE_GLIB_H -static gboolean stat_cache_remove_old_entry(gpointer _key, gpointer _value, gpointer user_data) { - server *srv = user_data; - buffer *key = _key; - stat_cache_entry *sce = _value; - - if (sce->state == STAT_CACHE_ENTRY_STAT_FINISHED && - srv->cur_ts - sce->stat_ts > 10) { - buffer_free(key); - stat_cache_entry_free(sce); - - return TRUE; - } - - return FALSE; -} -#endif - -int stat_cache_trigger_cleanup(server *srv) { - stat_cache *sc; - - sc = srv->stat_cache; - -#ifdef HAVE_GLIB_H - if (!sc->files) return 0; - - g_hash_table_foreach_remove(sc->files, stat_cache_remove_old_entry, srv); -#endif - - return 0; -} diff --git a/src/stat_cache.h b/src/stat_cache.h deleted file mode 100644 index 3790edcf..00000000 --- a/src/stat_cache.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _FILE_CACHE_H_ -#define _FILE_CACHE_H_ - -#include "base.h" - -LI_EXPORT stat_cache * stat_cache_init(void); -LI_EXPORT void stat_cache_free(stat_cache *fc); - -LI_EXPORT handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **fce); -LI_EXPORT handler_t stat_cache_get_entry_async(server *srv, connection *con, buffer *name, stat_cache_entry **fce); -LI_EXPORT handler_t stat_cache_handle_fdevent(void *_srv, void *_fce, int revent); - -LI_EXPORT int stat_cache_trigger_cleanup(server *srv); -#endif diff --git a/src/status_counter.c b/src/status_counter.c deleted file mode 100644 index 6a3233f4..00000000 --- a/src/status_counter.c +++ /dev/null @@ -1,75 +0,0 @@ -#include <stdlib.h> - -#include "status_counter.h" -/** - * 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 = ... - */ - -static array *counters = NULL; - -void status_counter_init(void) { - counters = array_init(); -} -void status_counter_free(void) { - array_free(counters); -} - -array *status_counter_get_array(void) { - return counters; -} - -data_integer *status_counter_get_counter(const char *s, size_t len) { - data_integer *di; - array *status = status_counter_get_array(); - - if (NULL == (di = (data_integer *)array_get_element(status, s, len))) { - /* not found, create it */ - - if (NULL == (di = (data_integer *)array_get_unused_element(status, TYPE_INTEGER))) { - di = data_integer_init(); - } - buffer_copy_string_len(di->key, s, len); - di->value = 0; - - array_insert_unique(status, (data_unset *)di); - } - return di; -} - -/* dummies of the statistic framework functions - * they will be moved to a statistics.c later */ -int status_counter_inc(const char *s, size_t len) { - data_integer *di = status_counter_get_counter(s, len); - - di->value++; - - return 0; -} - -int status_counter_dec(const char *s, size_t len) { - data_integer *di = status_counter_get_counter(s, len); - - if (di->value > 0) di->value--; - - return 0; -} - -int status_counter_set(const char *s, size_t len, int val) { - data_integer *di = status_counter_get_counter(s, len); - - di->value = val; - - return 0; -} - - diff --git a/src/status_counter.h b/src/status_counter.h deleted file mode 100644 index 34d39a76..00000000 --- a/src/status_counter.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _STATUS_COUNTER_H_ -#define _STATUS_COUNTER_H_ - -#include <sys/types.h> - -#include "array.h" - -LI_EXPORT void status_counter_init(void); -LI_EXPORT void status_counter_free(void); -LI_EXPORT array * status_counter_get_array(void); -LI_EXPORT data_integer * status_counter_get_counter(const char *s, size_t len); -LI_EXPORT int status_counter_inc(const char *s, size_t len); -LI_EXPORT int status_counter_dec(const char *s, size_t len); -LI_EXPORT int status_counter_set(const char *s, size_t len, int val); - -#define COUNTER_INC(di) if (di) di->value++; -#define COUNTER_DEC(di) if (di && di->value > 0) di->value--; -#define COUNTER_SET(di, val) if (di) di->value = val; - -#endif diff --git a/src/stream.c b/src/stream.c deleted file mode 100644 index e4b52875..00000000 --- a/src/stream.c +++ /dev/null @@ -1,107 +0,0 @@ -#include <sys/types.h> -#include <sys/stat.h> - -#include <fcntl.h> - -#include "stream.h" -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "sys-mmap.h" -#include "sys-files.h" - -#ifndef O_BINARY -# define O_BINARY 0 -#endif - -int stream_open(stream *f, buffer *fn) { - struct stat st; -#ifdef HAVE_MMAP - int fd; -#elif defined _WIN32 - HANDLE *fh, *mh; - void *p; -#endif - - f->start = NULL; - - if (-1 == stat(fn->ptr, &st)) { - return -1; - } - - f->size = st.st_size; - -#ifdef HAVE_MMAP - if (-1 == (fd = open(fn->ptr, O_RDONLY | O_BINARY))) { - return -1; - } - - f->start = mmap(0, f->size, PROT_READ, MAP_SHARED, fd, 0); - - close(fd); - - if (MAP_FAILED == f->start) { - return -1; - } - -#elif defined _WIN32 - fh = CreateFile(fn->ptr, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_READONLY, - NULL); - - if (fh == INVALID_HANDLE_VALUE) return -1; - - mh = CreateFileMapping( fh, - NULL, - PAGE_READONLY, - (sizeof(off_t) > 4) ? f->size >> 32 : 0, - f->size & 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 ); -*/ - return -1; - } - - p = MapViewOfFile(mh, - FILE_MAP_READ, - 0, - 0, - 0); - CloseHandle(mh); - CloseHandle(fh); - - f->start = p; -#else -# error no mmap found -#endif - - return 0; -} - -int stream_close(stream *f) { -#ifdef HAVE_MMAP - if (f->start) munmap(f->start, f->size); -#elif defined(__WIN32) - if (f->start) UnmapViewOfFile(f->start); -#endif - - f->start = NULL; - - return 0; -} diff --git a/src/stream.h b/src/stream.h deleted file mode 100644 index 0a974c05..00000000 --- a/src/stream.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _STREAM_H_ -#define _STREAM_H_ - -#include "buffer.h" - -typedef struct { - char *start; - off_t size; -} stream; - -LI_EXPORT int stream_open(stream *f, buffer *fn); -LI_EXPORT int stream_close(stream *f); - -#endif diff --git a/src/sys-files.c b/src/sys-files.c deleted file mode 100644 index 2a9079a3..00000000 --- a/src/sys-files.c +++ /dev/null @@ -1,66 +0,0 @@ -#include "sys-files.h" - -#ifdef _WIN32 -#include "buffer.h" -DIR *opendir(const char *dn) { - DIR *d = malloc(sizeof(*d)); - - if (INVALID_HANDLE_VALUE == (d->h = FindFirstFile(dn, &(d->finddata)))) { - free(d); - return NULL; - } - - return d; -} - -struct dirent *readdir(DIR *d) { - if (!d->dent.d_name) { - /* opendir has set a finddata already, push it out */ - - d->dent.d_name = d->finddata.cFileName; - return &(d->dent); - } - if (FindNextFile(d->h, &(d->finddata))) { - d->dent.d_name = d->finddata.cFileName; - return &(d->dent); - } else { - return NULL; - } -} - -void closedir(DIR *d) { - FindClose(d); - - free(d); -} - -buffer *pathname_unix2local(buffer *fn) { - size_t i; - - for (i = 0; i < fn->used; i++) { - if (fn->ptr[i] == '/') { - fn->ptr[i] = '\\'; - } - } - - return fn; -} - -buffer *filename_unix2local(buffer *fn) { - size_t i; - - for (i = 0; i < fn->used; i++) { - if (fn->ptr[i] == '/') { - fn->ptr[i] = '\\'; - } - } -#if 0 - buffer_prepare_append(fn, 4); - memmove(fn->ptr + 4, fn->ptr, fn->used); - memcpy(fn->ptr, "\\\\?\\", 4); - fn->used += 4; -#endif - return fn; -} -#endif - diff --git a/src/sys-files.h b/src/sys-files.h deleted file mode 100644 index b964f990..00000000 --- a/src/sys-files.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef _SYS_FILES_H_ -#define _SYS_FILES_H_ - -#define DIR_SEPERATOR_UNIX '/' -#define DIR_SEPERATOR_UNIX_STR "/" -#define DIR_SEPERATOR_WIN '\\' -#define DIR_SEPERATOR_WIN_STR "\\" - -#include "settings.h" - -#ifdef _WIN32 -#include <windows.h> -#include <io.h> /* open */ -#include <direct.h> /* chdir */ - -#include "buffer.h" - -#define DIR_SEPERATOR DIR_SEPERATOR_WIN -#define DIR_SEPERATOR_STR DIR_SEPERATOR_WIN_STR - -#define __S_ISTYPE(mode, mask) (((mode) & _S_IFMT) == (mask)) - -#undef S_ISDIR -#undef S_ISCHR -#undef S_ISBLK -#undef S_ISREG -#define S_ISDIR(mode) __S_ISTYPE((mode), _S_IFDIR) -#define S_ISCHR(mode) __S_ISTYPE((mode), _S_IFCHR) -#define S_ISBLK(mode) __S_ISTYPE((mode), _S_IFBLK) -#define S_ISREG(mode) __S_ISTYPE((mode), _S_IFREG) -/* we don't support symlinks */ -#define S_ISLNK(mode) 0 - -#define lstat stat -#define mkstemp(x) open(mktemp(x), O_RDWR) -#define mkdir(x, y) mkdir(x) - -/* retrieve the most recent network, or general libc error */ -#define light_sock_errno() (WSAGetLastError()) - -struct dirent { - const char *d_name; -}; - -typedef struct { - HANDLE h; - WIN32_FIND_DATA finddata; - struct dirent dent; -} DIR; - -LI_EXPORT DIR * opendir(const char *dn); -LI_EXPORT struct dirent * readdir(DIR *d); -LI_EXPORT void closedir(DIR *d); - -LI_EXPORT buffer * filename_unix2local(buffer *b); -LI_EXPORT buffer * pathname_unix2local(buffer *b); - -#else -#include <unistd.h> -#include <dirent.h> - -#define DIR_SEPERATOR DIR_SEPERATOR_UNIX -#define DIR_SEPERATOR_STR DIR_SEPERATOR_UNIX_STR - -#define light_sock_errno() (errno) - -#define filename_unix2local(x) /* (x) */ -#define pathname_unix2local(x) /* (x) */ -#endif - -#define PATHNAME_APPEND_SLASH(x) \ - if (x->used > 1 && x->ptr[x->used - 2] != DIR_SEPERATOR) { \ - char sl[2] = { DIR_SEPERATOR, 0 }; \ - BUFFER_APPEND_STRING_CONST(x, sl); \ - } - -#ifndef O_LARGEFILE -# define O_LARGEFILE 0 -#endif - -#ifndef O_NOATIME -# define O_NOATIME 0 -#endif - -#endif - - diff --git a/src/sys-mmap.h b/src/sys-mmap.h deleted file mode 100644 index 560987b3..00000000 --- a/src/sys-mmap.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef WIN32_MMAP_H -#define WIN32_MMAP_H - -#include "settings.h" - -#ifdef _WIN32 - -#define MAP_FAILED -1 -#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) - -#include <windows.h> - -#else -#include <sys/mman.h> - -#ifndef MAP_FAILED -#define MAP_FAILED -1 -#endif -#endif - -#endif diff --git a/src/sys-process.h b/src/sys-process.h deleted file mode 100644 index 53df76e7..00000000 --- a/src/sys-process.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _SYS_PROCESS_H_ -#define _SYS_PROCESS_H_ - -#ifdef _WIN32 -#include <process.h> -#define pid_t int -/* win32 has no fork() */ -#define kill(x, y) do { } while (0) -#define getpid() 0 - -#else -#include <sys/wait.h> -#include <unistd.h> -#endif - -#endif - diff --git a/src/sys-socket.c b/src/sys-socket.c deleted file mode 100644 index 97664115..00000000 --- a/src/sys-socket.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "base.h" -#include "sys-socket.h" - -#ifndef HAVE_INET_ATON -/* win32 has inet_addr instead if inet_aton */ -# ifdef HAVE_INET_ADDR -int inet_aton(const char *cp, struct in_addr *inp) { - struct in_addr a; - - a.s_addr = inet_addr(cp); - - if (INADDR_NONE == a.s_addr) { - return 0; - } - - inp->s_addr = a.s_addr; - - return 1; -} -# else -# error no inet_aton emulation found -# endif - -#endif - -#ifdef _WIN32 - -#include <winsock2.h> - -/* windows doesn't have inet_ntop */ - -/* -I have to look into this more. WSAAddressToString takes a sockaddr structure, which includes -the port number, so I must first test this stuff more carefully. For now, no IPV6 on windows. -You will notice that HAVE_IPV6 is never true for win32. -*/ - -const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) -{ - /* WSAAddressToString takes a full sockaddr, while inet_ntop only takes the address */ - struct sockaddr_in sock4; - struct sockaddr_in6 sock6; - DWORD addrLen = cnt; - int err = 0; - - /* src is either an in_addr or an in6_addr. */ - const struct in_addr *src4 = (const struct in_addr*) src; - const struct in6_addr *src6 = (const struct in6_addr*) src; - - int ipv6 = af == AF_INET6; - - // DebugBreak(); - - if ( ipv6 ) - { - sock6.sin6_family = AF_INET6; - sock6.sin6_port = 0; - sock6.sin6_addr = *src6; - } - else - { - sock4.sin_family = AF_INET; - sock4.sin_port = 0; - sock4.sin_addr = *src4; - } - - err = WSAAddressToStringA( - ipv6 ? (LPSOCKADDR) &sock6 : (LPSOCKADDR) &sock4, - ipv6 ? sizeof(sock6) : sizeof(sock4), - NULL, - dst, &addrLen ); - return err == 0 ? dst : NULL; -} - - -#endif diff --git a/src/sys-socket.h b/src/sys-socket.h deleted file mode 100644 index cf93efd2..00000000 --- a/src/sys-socket.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef SYS_SOCKET_H -#define SYS_SOCKET_H - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifdef _WIN32 -#ifndef FD_SETSIZE -/* By default this is 64 */ -#define FD_SETSIZE 4096 -#endif -#include <winsock2.h> -#include <ws2tcpip.h> -//#include <wspiapi.h> -//#define HAVE_IPV6 -- not until we've resolved the inet_ntop issue. - -#define ECONNRESET WSAECONNRESET -#define EINPROGRESS WSAEINPROGRESS -#define EALREADY WSAEALREADY -#define ENOTCONN WSAENOTCONN -#define EWOULDBLOCK WSAEWOULDBLOCK -#define ECONNABORTED WSAECONNABORTED -#define ECONNREFUSED WSAECONNREFUSED -#define EHOSTUNREACH WSAEHOSTUNREACH -#define ioctl ioctlsocket -#define hstrerror(x) "" -#define STDIN_FILENO 0 -#define STDOUT_FILENO 1 -#define STDERR_FILENO 2 -#ifndef __MINGW32__ -#define ssize_t int -#endif - -#define sockread( fd, buf, bytes ) recv( fd, buf, bytes, 0 ) - -LI_EXPORT const char * inet_ntop(int af, const void *src, char *dst, socklen_t cnt); -int inet_aton(const char *cp, struct in_addr *inp); -#define HAVE_INET_ADDR -#undef HAVE_INET_ATON - -#else -#include <sys/types.h> /* required by netinet/tcp.h on FreeBSD */ -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <sys/un.h> -#include <arpa/inet.h> - -#ifndef SUN_LEN -#define SUN_LEN(su) \ - (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) -#endif - -#define sockread( fd, buf, bytes ) read( fd, buf, bytes ) -#define closesocket(x) close(x) - -#include <netdb.h> -#endif /* !_WIN32 */ - -typedef union { -#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; -} sock_addr; - -#endif diff --git a/src/sys-strings.h b/src/sys-strings.h deleted file mode 100644 index 1103a8c1..00000000 --- a/src/sys-strings.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef _SYS_STRINGS_H_ -#define _SYS_STRINGS_H_ - -#ifdef _WIN32 -#define strcasecmp stricmp -#define strncasecmp strnicmp -#include <stdlib.h> -#define str_to_off_t(p, e, b) _strtoi64(p, e, b) -#define STR_OFF_T_MAX LLONG_MAX -#define STR_OFF_T_MIN LLONG_MIN -#define strtoull _strtoui64 -#ifdef __MINGW32__ -/* missing prototype */ -unsigned __int64 _strtoui64( - const char *nptr, - char **endptr, - int base - ); -__int64 _strtoi64( - const char *nptr, - char **endptr, - int base - ); -#endif -#else /** we are a unix */ - -/** - * we use strtoll() for parsing the ranges into a off_t - * - * if off_t is 32bit, we can use strtol() instead - */ - #if SIZEOF_OFF_T == SIZEOF_LONG - #define str_to_off_t(p, e, b) strtol(p, e, b) - #define STR_OFF_T_MAX LONG_MAX - #define STR_OFF_T_MIN LONG_MIN - #elif defined(HAVE_STRTOLL) - #define str_to_off_t(p, e, b) strtoll(p, e, b) - #define STR_OFF_T_MAX LLONG_MAX - #define STR_OFF_T_MIN LLONG_MIN - #else - #error off_t is more than 4 bytes but we can not parse it with strtol() (run autogen.sh again if you build from svn) - #endif -#endif - -#endif - diff --git a/src/timing.c b/src/timing.c deleted file mode 100644 index 94c9441b..00000000 --- a/src/timing.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "base.h" -#include "timing.h" - -void timing_log(server *srv, connection *con, int field) { -#ifdef HAVE_GLIB_H - if (srv->srvconf.log_timing) { - g_get_current_time(&(con->timestamps[field])); - } -#endif -} - diff --git a/src/timing.h b/src/timing.h deleted file mode 100644 index 7fbb2ae5..00000000 --- a/src/timing.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _TIMING_H_ -#define _TIMING_H_ - -#include "base.h" - -#define TIME_DIFF(t2, t1) \ - ((con->timestamps[t2].tv_sec - con->timestamps[t1].tv_sec) * 1000 + \ - (con->timestamps[t2].tv_usec - con->timestamps[t1].tv_usec) / 1000) - -LI_API void timing_log(server *srv, connection *con, int field); - -#endif diff --git a/src/valgrind/Makefile.am b/src/valgrind/Makefile.am deleted file mode 100644 index f3071778..00000000 --- a/src/valgrind/Makefile.am +++ /dev/null @@ -1 +0,0 @@ -noinst_HEADERS = valgrind.h diff --git a/src/valgrind/valgrind.h b/src/valgrind/valgrind.h deleted file mode 100644 index a7120859..00000000 --- a/src/valgrind/valgrind.h +++ /dev/null @@ -1,469 +0,0 @@ -/* -*- c -*- - ---------------------------------------------------------------- - - Notice that the following BSD-style license applies to this one - file (valgrind.h) only. The entire rest of Valgrind is licensed - under the terms of the GNU General Public License, version 2. See - the COPYING file in the source distribution for details. - - ---------------------------------------------------------------- - - This file is part of Valgrind, a dynamic binary instrumentation - framework. - - Copyright (C) 2000-2005 Julian Seward. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. The origin of this software must not be misrepresented; you must - not claim that you wrote the original software. If you use this - software in a product, an acknowledgment in the product - documentation would be appreciated but is not required. - - 3. Altered source versions must be plainly marked as such, and must - not be misrepresented as being the original software. - - 4. The name of the author may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. - - ---------------------------------------------------------------- - - Notice that the above BSD-style license applies to this one file - (valgrind.h) only. The entire rest of Valgrind is licensed under - the terms of the GNU General Public License, version 2. See the - COPYING file in the source distribution for details. - - ---------------------------------------------------------------- -*/ - - -/* This file is for inclusion into client (your!) code. - - You can use these macros to manipulate and query Valgrind's - execution inside your own programs. - - The resulting executables will still run without Valgrind, just a - little bit more slowly than they otherwise would, but otherwise - unchanged. When not running on valgrind, each client request - consumes very few (eg. < 10) instructions, so the resulting performance - loss is negligible unless you plan to execute client requests - millions of times per second. Nevertheless, if that is still a - problem, you can compile with the NVALGRIND symbol defined (gcc - -DNVALGRIND) so that client requests are not even compiled in. */ - -#ifndef __VALGRIND_H -#define __VALGRIND_H - -#include <stdarg.h> - -/* Nb: this file might be included in a file compiled with -ansi. So - we can't use C++ style "//" comments nor the "asm" keyword (instead - use "__asm__"). */ - -/* If we're not compiling for our target architecture, don't generate - any inline asms. Note that in this file we're using the compiler's - CPP symbols for identifying architectures, which are different to - the ones we use within the rest of Valgrind. */ -#if !defined(__i386__) && !defined(__x86_64__) && !defined(__powerpc__) -# ifndef NVALGRIND -# define NVALGRIND 1 -# endif /* NVALGRIND */ -#endif - -/* ------------------------------------------------------------------ */ -/* The architecture-specific part */ -/* ------------------------------------------------------------------ */ - -#ifdef NVALGRIND - -/* Define NVALGRIND to completely remove the Valgrind magic sequence - from the compiled code (analogous to NDEBUG's effects on assert()) */ -#define VALGRIND_MAGIC_SEQUENCE( \ - _zzq_rlval, _zzq_default, _zzq_request, \ - _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4) \ - { \ - (_zzq_rlval) = (_zzq_default); \ - } - -#else /* NVALGRIND */ - -/* The following defines the magic code sequences which the JITter spots and - handles magically. Don't look too closely at them; they will rot - your brain. We must ensure that the default value gets put in the return - slot, so that everything works when this is executed not under Valgrind. - Args are passed in a memory block, and so there's no intrinsic limit to - the number that could be passed, but it's currently four. - - The macro args are: - _zzq_rlval result lvalue - _zzq_default default value (result returned when running on real CPU) - _zzq_request request code - _zzq_arg1..4 request params - - Nb: we put the assembly code sequences for all architectures in this one - file. This is because this file must be stand-alone, and we don't want - to have multiple files. -*/ - -#ifdef __x86_64__ -#define VALGRIND_MAGIC_SEQUENCE( \ - _zzq_rlval, _zzq_default, _zzq_request, \ - _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4) \ - \ - { volatile unsigned long long _zzq_args[5]; \ - _zzq_args[0] = (volatile unsigned long long)(_zzq_request); \ - _zzq_args[1] = (volatile unsigned long long)(_zzq_arg1); \ - _zzq_args[2] = (volatile unsigned long long)(_zzq_arg2); \ - _zzq_args[3] = (volatile unsigned long long)(_zzq_arg3); \ - _zzq_args[4] = (volatile unsigned long long)(_zzq_arg4); \ - __asm__ volatile("roll $29, %%eax ; roll $3, %%eax\n\t" \ - "rorl $27, %%eax ; rorl $5, %%eax\n\t" \ - "roll $13, %%eax ; roll $19, %%eax" \ - : "=d" (_zzq_rlval) \ - : "a" (&_zzq_args[0]), "0" (_zzq_default) \ - : "cc", "memory" \ - ); \ - } -#endif /* __x86_64__ */ - -#ifdef __i386__ -#define VALGRIND_MAGIC_SEQUENCE( \ - _zzq_rlval, _zzq_default, _zzq_request, \ - _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4) \ - \ - { unsigned int _zzq_args[5]; \ - _zzq_args[0] = (unsigned int)(_zzq_request); \ - _zzq_args[1] = (unsigned int)(_zzq_arg1); \ - _zzq_args[2] = (unsigned int)(_zzq_arg2); \ - _zzq_args[3] = (unsigned int)(_zzq_arg3); \ - _zzq_args[4] = (unsigned int)(_zzq_arg4); \ - __asm__ volatile("roll $29, %%eax ; roll $3, %%eax\n\t" \ - "rorl $27, %%eax ; rorl $5, %%eax\n\t" \ - "roll $13, %%eax ; roll $19, %%eax" \ - : "=d" (_zzq_rlval) \ - : "a" (&_zzq_args[0]), "0" (_zzq_default) \ - : "cc", "memory" \ - ); \ - } -#endif /* __i386__ */ - -#ifdef __powerpc__ -#define VALGRIND_MAGIC_SEQUENCE( \ - _zzq_rlval, _zzq_default, _zzq_request, \ - _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4) \ - \ - { volatile unsigned int _zzq_args[5]; \ - register unsigned int _zzq_tmp __asm__("r3"); \ - register volatile unsigned int *_zzq_ptr __asm__("r4"); \ - _zzq_args[0] = (volatile unsigned int)(_zzq_request); \ - _zzq_args[1] = (volatile unsigned int)(_zzq_arg1); \ - _zzq_args[2] = (volatile unsigned int)(_zzq_arg2); \ - _zzq_args[3] = (volatile unsigned int)(_zzq_arg3); \ - _zzq_args[4] = (volatile unsigned int)(_zzq_arg4); \ - _zzq_ptr = _zzq_args; \ - __asm__ volatile("tw 0,3,27\n\t" \ - "rlwinm 0,0,29,0,0\n\t" \ - "rlwinm 0,0,3,0,0\n\t" \ - "rlwinm 0,0,13,0,0\n\t" \ - "rlwinm 0,0,19,0,0\n\t" \ - "nop\n\t" \ - : "=r" (_zzq_tmp) \ - : "0" (_zzq_default), "r" (_zzq_ptr) \ - : "memory"); \ - _zzq_rlval = (__typeof__(_zzq_rlval)) _zzq_tmp; \ - } -#endif /* __powerpc__ */ - -/* Insert assembly code for other architectures here... */ - -#endif /* NVALGRIND */ - - -/* ------------------------------------------------------------------ */ -/* The architecture-independent part */ -/* ------------------------------------------------------------------ */ - -/* Some request codes. There are many more of these, but most are not - exposed to end-user view. These are the public ones, all of the - form 0x1000 + small_number. - - Core ones are in the range 0x00000000--0x0000ffff. The non-public ones - start at 0x2000. -*/ - -/* These macros are used by tools -- they must be public, but don't embed them - * into other programs. */ -#define VG_USERREQ_TOOL_BASE(a,b) \ - ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) -#define VG_IS_TOOL_USERREQ(a, b, v) \ - (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) - -typedef - enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, - VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, - - /* These allow any function to be called from the - simulated CPU but run on the real CPU. - Nb: the first arg passed to the function is always the ThreadId of - the running thread! So CLIENT_CALL0 actually requires a 1 arg - function, etc. */ - VG_USERREQ__CLIENT_CALL0 = 0x1101, - VG_USERREQ__CLIENT_CALL1 = 0x1102, - VG_USERREQ__CLIENT_CALL2 = 0x1103, - VG_USERREQ__CLIENT_CALL3 = 0x1104, - - /* Can be useful in regression testing suites -- eg. can send - Valgrind's output to /dev/null and still count errors. */ - VG_USERREQ__COUNT_ERRORS = 0x1201, - - /* These are useful and can be interpreted by any tool that tracks - malloc() et al, by using vg_replace_malloc.c. */ - VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, - VG_USERREQ__FREELIKE_BLOCK = 0x1302, - /* Memory pool support. */ - VG_USERREQ__CREATE_MEMPOOL = 0x1303, - VG_USERREQ__DESTROY_MEMPOOL = 0x1304, - VG_USERREQ__MEMPOOL_ALLOC = 0x1305, - VG_USERREQ__MEMPOOL_FREE = 0x1306, - - /* Allow printfs to valgrind log. */ - VG_USERREQ__PRINTF = 0x1401, - VG_USERREQ__PRINTF_BACKTRACE = 0x1402, - - /* Stack support. */ - VG_USERREQ__STACK_REGISTER = 0x1501, - VG_USERREQ__STACK_DEREGISTER = 0x1502, - VG_USERREQ__STACK_CHANGE = 0x1503, - } Vg_ClientRequest; - -#ifndef __GNUC__ -#define __extension__ -#endif - -/* Returns the number of Valgrinds this code is running under. That is, - 0 if running natively, 1 if running under Valgrind, 2 if running under - Valgrind which is running under another Valgrind, etc. */ -#define RUNNING_ON_VALGRIND __extension__ \ - ({unsigned int _qzz_res; \ - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* returned if not */, \ - VG_USERREQ__RUNNING_ON_VALGRIND, \ - 0, 0, 0, 0); \ - _qzz_res; \ - }) - - -/* Discard translation of code in the range [_qzz_addr .. _qzz_addr + - _qzz_len - 1]. Useful if you are debugging a JITter or some such, - since it provides a way to make sure valgrind will retranslate the - invalidated area. Returns no value. */ -#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ - {unsigned int _qzz_res; \ - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ - VG_USERREQ__DISCARD_TRANSLATIONS, \ - _qzz_addr, _qzz_len, 0, 0); \ - } - -#ifdef NVALGRIND - -#define VALGRIND_PRINTF(...) -#define VALGRIND_PRINTF_BACKTRACE(...) - -#else /* NVALGRIND */ - -int VALGRIND_PRINTF(const char *format, ...) - __attribute__((format(__printf__, 1, 2))); -__attribute__((weak)) -int -VALGRIND_PRINTF(const char *format, ...) -{ - unsigned long _qzz_res; - va_list vargs; - va_start(vargs, format); - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, VG_USERREQ__PRINTF, - (unsigned long)format, (unsigned long)vargs, 0, 0); - va_end(vargs); - return (int)_qzz_res; -} - -int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) - __attribute__((format(__printf__, 1, 2))); -__attribute__((weak)) -int -VALGRIND_PRINTF_BACKTRACE(const char *format, ...) -{ - unsigned long _qzz_res; - va_list vargs; - va_start(vargs, format); - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, VG_USERREQ__PRINTF_BACKTRACE, - (unsigned long)format, (unsigned long)vargs, 0, 0); - va_end(vargs); - return (int)_qzz_res; -} - -#endif /* NVALGRIND */ - -/* These requests allow control to move from the simulated CPU to the - real CPU, calling an arbitary function */ -#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ - ({unsigned long _qyy_res; \ - VALGRIND_MAGIC_SEQUENCE(_qyy_res, 0 /* default return */, \ - VG_USERREQ__CLIENT_CALL0, \ - _qyy_fn, \ - 0, 0, 0); \ - _qyy_res; \ - }) - -#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ - ({unsigned long _qyy_res; \ - VALGRIND_MAGIC_SEQUENCE(_qyy_res, 0 /* default return */, \ - VG_USERREQ__CLIENT_CALL1, \ - _qyy_fn, \ - _qyy_arg1, 0, 0); \ - _qyy_res; \ - }) - -#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ - ({unsigned long _qyy_res; \ - VALGRIND_MAGIC_SEQUENCE(_qyy_res, 0 /* default return */, \ - VG_USERREQ__CLIENT_CALL2, \ - _qyy_fn, \ - _qyy_arg1, _qyy_arg2, 0); \ - _qyy_res; \ - }) - -#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ - ({unsigned long _qyy_res; \ - VALGRIND_MAGIC_SEQUENCE(_qyy_res, 0 /* default return */, \ - VG_USERREQ__CLIENT_CALL3, \ - _qyy_fn, \ - _qyy_arg1, _qyy_arg2, _qyy_arg3); \ - _qyy_res; \ - }) - - -/* Counts the number of errors that have been recorded by a tool. Nb: - the tool must record the errors with VG_(maybe_record_error)() or - VG_(unique_error)() for them to be counted. */ -#define VALGRIND_COUNT_ERRORS \ - ({unsigned int _qyy_res; \ - VALGRIND_MAGIC_SEQUENCE(_qyy_res, 0 /* default return */, \ - VG_USERREQ__COUNT_ERRORS, \ - 0, 0, 0, 0); \ - _qyy_res; \ - }) - -/* Mark a block of memory as having been allocated by a malloc()-like - function. `addr' is the start of the usable block (ie. after any - redzone) `rzB' is redzone size if the allocator can apply redzones; - use '0' if not. Adding redzones makes it more likely Valgrind will spot - block overruns. `is_zeroed' indicates if the memory is zeroed, as it is - for calloc(). Put it immediately after the point where a block is - allocated. - - If you're allocating memory via superblocks, and then handing out small - chunks of each superblock, if you don't have redzones on your small - blocks, it's worth marking the superblock with VALGRIND_MAKE_NOACCESS - when it's created, so that block overruns are detected. But if you can - put redzones on, it's probably better to not do this, so that messages - for small overruns are described in terms of the small block rather than - the superblock (but if you have a big overrun that skips over a redzone, - you could miss an error this way). See memcheck/tests/custom_alloc.c - for an example. - - Nb: block must be freed via a free()-like function specified - with VALGRIND_FREELIKE_BLOCK or mismatch errors will occur. */ -#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ - {unsigned int _qzz_res; \ - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ - VG_USERREQ__MALLOCLIKE_BLOCK, \ - addr, sizeB, rzB, is_zeroed); \ - } - -/* Mark a block of memory as having been freed by a free()-like function. - `rzB' is redzone size; it must match that given to - VALGRIND_MALLOCLIKE_BLOCK. Memory not freed will be detected by the leak - checker. Put it immediately after the point where the block is freed. */ -#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ - {unsigned int _qzz_res; \ - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ - VG_USERREQ__FREELIKE_BLOCK, \ - addr, rzB, 0, 0); \ - } - -/* Create a memory pool. */ -#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ - {unsigned int _qzz_res; \ - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ - VG_USERREQ__CREATE_MEMPOOL, \ - pool, rzB, is_zeroed, 0); \ - } - -/* Destroy a memory pool. */ -#define VALGRIND_DESTROY_MEMPOOL(pool) \ - {unsigned int _qzz_res; \ - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ - VG_USERREQ__DESTROY_MEMPOOL, \ - pool, 0, 0, 0); \ - } - -/* Associate a piece of memory with a memory pool. */ -#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ - {unsigned int _qzz_res; \ - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ - VG_USERREQ__MEMPOOL_ALLOC, \ - pool, addr, size, 0); \ - } - -/* Disassociate a piece of memory from a memory pool. */ -#define VALGRIND_MEMPOOL_FREE(pool, addr) \ - {unsigned int _qzz_res; \ - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ - VG_USERREQ__MEMPOOL_FREE, \ - pool, addr, 0, 0); \ - } - -/* Mark a piece of memory as being a stack. Returns a stack id. */ -#define VALGRIND_STACK_REGISTER(start, end) \ - ({unsigned int _qzz_res; \ - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ - VG_USERREQ__STACK_REGISTER, \ - start, end, 0, 0); \ - _qzz_res; \ - }) - -/* Unmark the piece of memory associated with a stack id as being a - stack. */ -#define VALGRIND_STACK_DEREGISTER(id) \ - {unsigned int _qzz_res; \ - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ - VG_USERREQ__STACK_DEREGISTER, \ - id, 0, 0, 0); \ - } - -/* Change the start and end address of the stack id. */ -#define VALGRIND_STACK_CHANGE(id, start, end) \ - {unsigned int _qzz_res; \ - VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ - VG_USERREQ__STACK_CHANGE, \ - id, start, end, 0); \ - } - -#endif /* __VALGRIND_H */ diff --git a/src/xgetopt.c b/src/xgetopt.c deleted file mode 100644 index 73694ada..00000000 --- a/src/xgetopt.c +++ /dev/null @@ -1,222 +0,0 @@ -// XGetopt.cpp Version 1.2 -// -// Author: Hans Dietrich -// hdietrich2@hotmail.com -// -// Description: -// XGetopt.cpp implements getopt(), a function to parse command lines. -// -// History -// Version 1.2 - 2003 May 17 -// - Added Unicode support -// -// Version 1.1 - 2002 March 10 -// - Added example to XGetopt.cpp module header -// -// This software is released into the public domain. -// You are free to use it in any way you like. -// -// This software is provided "as is" with no expressed -// or implied warranty. I accept no liability for any -// damage or loss of business that this software may cause. -// -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -// if you are using precompiled headers then include this line: -//#include "stdafx.h" -/////////////////////////////////////////////////////////////////////////////// - - -/////////////////////////////////////////////////////////////////////////////// -// if you are not using precompiled headers then include these lines: -#include <windows.h> -#include <stdio.h> -#include <tchar.h> -/////////////////////////////////////////////////////////////////////////////// - - -#include "XGetopt.h" - - -/////////////////////////////////////////////////////////////////////////////// -// -// X G e t o p t . c p p -// -// -// NAME -// getopt -- parse command line options -// -// SYNOPSIS -// int getopt(int argc, TCHAR *argv[], TCHAR *optstring) -// -// extern TCHAR *optarg; -// extern int optind; -// -// DESCRIPTION -// The getopt() function parses the command line arguments. Its -// arguments argc and argv are the argument count and array as -// passed into the application on program invocation. In the case -// of Visual C++ programs, argc and argv are available via the -// variables __argc and __argv (double underscores), respectively. -// getopt returns the next option letter in argv that matches a -// letter in optstring. (Note: Unicode programs should use -// __targv instead of __argv. Also, all character and string -// literals should be enclosed in _T( ) ). -// -// optstring is a string of recognized option letters; if a letter -// is followed by a colon, the option is expected to have an argument -// that may or may not be separated from it by white space. optarg -// is set to point to the start of the option argument on return from -// getopt. -// -// Option letters may be combined, e.g., "-ab" is equivalent to -// "-a -b". Option letters are case sensitive. -// -// getopt places in the external variable optind the argv index -// of the next argument to be processed. optind is initialized -// to 0 before the first call to getopt. -// -// When all options have been processed (i.e., up to the first -// non-option argument), getopt returns EOF, optarg will point -// to the argument, and optind will be set to the argv index of -// the argument. If there are no non-option arguments, optarg -// will be set to NULL. -// -// The special option "--" may be used to delimit the end of the -// options; EOF will be returned, and "--" (and everything after it) -// will be skipped. -// -// RETURN VALUE -// For option letters contained in the string optstring, getopt -// will return the option letter. getopt returns a question mark (?) -// when it encounters an option letter not included in optstring. -// EOF is returned when processing is finished. -// -// BUGS -// 1) Long options are not supported. -// 2) The GNU double-colon extension is not supported. -// 3) The environment variable POSIXLY_CORRECT is not supported. -// 4) The + syntax is not supported. -// 5) The automatic permutation of arguments is not supported. -// 6) This implementation of getopt() returns EOF if an error is -// encountered, instead of -1 as the latest standard requires. -// -// EXAMPLE -// BOOL CMyApp::ProcessCommandLine(int argc, TCHAR *argv[]) -// { -// int c; -// -// while ((c = getopt(argc, argv, _T("aBn:"))) != EOF) -// { -// switch (c) -// { -// case _T('a'): -// TRACE(_T("option a\n")); -// // -// // set some flag here -// // -// break; -// -// case _T('B'): -// TRACE( _T("option B\n")); -// // -// // set some other flag here -// // -// break; -// -// case _T('n'): -// TRACE(_T("option n: value=%d\n"), atoi(optarg)); -// // -// // do something with value here -// // -// break; -// -// case _T('?'): -// TRACE(_T("ERROR: illegal option %s\n"), argv[optind-1]); -// return FALSE; -// break; -// -// default: -// TRACE(_T("WARNING: no handler for option %c\n"), c); -// return FALSE; -// break; -// } -// } -// // -// // check for non-option args here -// // -// return TRUE; -// } -// -/////////////////////////////////////////////////////////////////////////////// - -TCHAR *optarg; // global argument pointer -int optind = 0; // global argv index - -int getopt(int argc, TCHAR *argv[], TCHAR *optstring) -{ - static TCHAR *next = NULL; - TCHAR c; - TCHAR *cp = NULL; - - if (optind == 0) - next = NULL; - - optarg = NULL; - - if (next == NULL || *next == _T('\0')) - { - if (optind == 0) - optind++; - - if (optind >= argc || argv[optind][0] != _T('-') || argv[optind][1] == _T('\0')) - { - optarg = NULL; - if (optind < argc) - optarg = argv[optind]; - return EOF; - } - - if (_tcscmp(argv[optind], _T("--")) == 0) - { - optind++; - optarg = NULL; - if (optind < argc) - optarg = argv[optind]; - return EOF; - } - - next = argv[optind]; - next++; // skip past - - optind++; - } - - c = *next++; - cp = _tcschr(optstring, c); - - if (cp == NULL || c == _T(':')) - return _T('?'); - - cp++; - if (*cp == _T(':')) - { - if (*next != _T('\0')) - { - optarg = next; - next = NULL; - } - else if (optind < argc) - { - optarg = argv[optind]; - optind++; - } - else - { - return _T('?'); - } - } - - return c; -} |